"use strict";
/*
 * Copyright © 2020 Atomist, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.goalSetState = exports.storeGoalSet = exports.constructGoalSet = exports.descriptionFromState = exports.constructProvenance = exports.storeGoal = exports.constructSdmGoal = exports.constructSdmGoalImplementation = exports.goalCorrespondsToSdmGoal = exports.updateGoal = exports.environmentFromGoal = void 0;
const GraphClient_1 = require("@atomist/automation-client/lib/spi/graph/GraphClient");
const _ = require("lodash");
const omitEmpty = require("omit-empty");
const sprintf_js_1 = require("sprintf-js");
const Goal_1 = require("../../api/goal/Goal");
const SdmGoalMessage_1 = require("../../api/goal/SdmGoalMessage");
const types_1 = require("../../typings/types");
function environmentFromGoal(goal) {
    return goal.definition.environment.replace(/\/$/, ""); // remove trailing slash at least
}
exports.environmentFromGoal = environmentFromGoal;
async function updateGoal(ctx, before, params) {
    const description = params.description;
    const approval = params.approved ? constructProvenance(ctx) :
        !!before ? before.approval : undefined;
    const data = params.data ?
        params.data :
        !!before ? before.data : undefined;
    before.version = (before.version || 1) + 1;
    const sdmGoal = Object.assign(Object.assign({}, eventToMessage(before)), { state: params.state === "success" && !!before && before.approvalRequired ? types_1.SdmGoalState.waiting_for_approval : params.state, phase: params.phase, description, url: params.url ? params.url : before.url, externalUrls: params.externalUrls ? params.externalUrls : before.externalUrls, approval, ts: Date.now(), provenance: [constructProvenance(ctx)].concat(!!before ? before.provenance : []), error: _.get(params, "error.message"), data, push: cleanPush(before.push), version: before.version });
    await storeGoal(ctx, sdmGoal);
}
exports.updateGoal = updateGoal;
function eventToMessage(event) {
    return Object.assign(Object.assign({}, event), { repo: {
            name: event.repo.name,
            owner: event.repo.owner,
            providerId: event.repo.providerId,
        }, id: undefined });
}
function goalCorrespondsToSdmGoal(goal, sdmGoal) {
    return goal.name === sdmGoal.name && environmentFromGoal(goal) === sdmGoal.environment;
}
exports.goalCorrespondsToSdmGoal = goalCorrespondsToSdmGoal;
function constructSdmGoalImplementation(gi, registration) {
    return {
        method: SdmGoalMessage_1.SdmGoalFulfillmentMethod.Sdm,
        name: gi.implementationName,
        registration,
    };
}
exports.constructSdmGoalImplementation = constructSdmGoalImplementation;
function constructSdmGoal(ctx, parameters) {
    const { goalSet, goal, goalSetId, state, id, providerId, url } = parameters;
    const fulfillment = parameters.fulfillment || {
        method: SdmGoalMessage_1.SdmGoalFulfillmentMethod.Other,
        name: "unknown",
        registration: "unknown",
    };
    if (!id.branch) {
        throw new Error(sprintf_js_1.sprintf("Please provide a branch in the RemoteRepoRef %j", parameters));
    }
    if (!id.sha) {
        throw new Error(sprintf_js_1.sprintf("Please provide a sha in the RemoteRepoRef %j", parameters));
    }
    const preConditions = [];
    const description = descriptionFromState(goal, state);
    const environment = environmentFromGoal(goal);
    if (Goal_1.hasPreconditions(goal)) {
        preConditions.push(...goal.dependsOn.map(d => ({
            goalSet,
            name: d.name,
            uniqueName: d.uniqueName,
            environment: environmentFromGoal(d),
        })));
    }
    const retryFeasible = goal.definition.retryFeasible ? goal.definition.retryFeasible : false;
    return {
        goalSet,
        registration: ctx.context.name,
        goalSetId,
        name: goal.name,
        uniqueName: goal.uniqueName,
        environment,
        fulfillment,
        sha: id.sha,
        branch: id.branch,
        repo: {
            name: id.repo,
            owner: id.owner,
            providerId,
        },
        state,
        description,
        descriptions: {
            planned: goal.plannedDescription,
            requested: goal.requestedDescription,
            inProcess: goal.inProcessDescription,
            completed: goal.successDescription,
            failed: goal.failureDescription,
            canceled: goal.canceledDescription,
            stopped: goal.stoppedDescription,
            skipped: goal.skippedDescription,
            waitingForApproval: goal.waitingForApprovalDescription,
            waitingForPreApproval: goal.waitingForPreApprovalDescription,
        },
        url,
        externalKey: goal.context,
        ts: Date.now(),
        approvalRequired: goal.definition.approvalRequired ? goal.definition.approvalRequired : false,
        preApprovalRequired: goal.definition.preApprovalRequired ? goal.definition.preApprovalRequired : false,
        retryFeasible,
        provenance: [constructProvenance(ctx)],
        preConditions,
        parameters: !!goal.definition.parameters ?
            JSON.stringify(goal.definition.parameters) : undefined,
        version: 1,
    };
}
exports.constructSdmGoal = constructSdmGoal;
async function storeGoal(ctx, sdmGoal) {
    const newGoal = omitEmpty(sdmGoal, { omitZero: false });
    delete (newGoal).push;
    await ctx.graphClient.mutate({
        name: "UpdateSdmGoal",
        variables: {
            goal: newGoal,
        },
        options: GraphClient_1.MutationNoCacheOptions,
    });
    return sdmGoal;
}
exports.storeGoal = storeGoal;
function constructProvenance(ctx) {
    return {
        registration: ctx.context.name,
        version: ctx.context.version,
        name: ctx.context.operation,
        correlationId: ctx.correlationId,
        ts: Date.now(),
    };
}
exports.constructProvenance = constructProvenance;
function descriptionFromState(goal, state, goalEvent) {
    switch (state) {
        case types_1.SdmGoalState.planned:
            return _.get(goalEvent, "descriptions.planned", goal.plannedDescription);
        case types_1.SdmGoalState.requested:
            return _.get(goalEvent, "descriptions.requested", goal.requestedDescription);
        case types_1.SdmGoalState.in_process:
            return _.get(goalEvent, "descriptions.inProcess", goal.inProcessDescription);
        case types_1.SdmGoalState.waiting_for_approval:
            return _.get(goalEvent, "descriptions.waitingForApproval", goal.waitingForApprovalDescription);
        case types_1.SdmGoalState.waiting_for_pre_approval:
            return _.get(goalEvent, "descriptions.waitingForPreApproval", goal.waitingForPreApprovalDescription);
        case types_1.SdmGoalState.success:
            return _.get(goalEvent, "descriptions.completed", goal.successDescription);
        case types_1.SdmGoalState.failure:
            return _.get(goalEvent, "descriptions.failed", goal.failureDescription);
        case types_1.SdmGoalState.skipped:
            return _.get(goalEvent, "descriptions.skipped", goal.skippedDescription);
        case types_1.SdmGoalState.canceled:
            return _.get(goalEvent, "descriptions.canceled", goal.canceledDescription);
        case types_1.SdmGoalState.stopped:
            return _.get(goalEvent, "descriptions.stopped", goal.stoppedDescription);
        default:
            throw new Error("Unknown goal state " + state);
    }
}
exports.descriptionFromState = descriptionFromState;
function constructGoalSet(ctx, goalSetId, goalSet, sdmGoals, tags, push) {
    let repo;
    if (!!push) {
        repo = {
            name: push.repo.name,
            owner: push.repo.owner,
            providerId: push.repo.org.provider.providerId,
        };
    }
    else if (!!sdmGoals && sdmGoals.length > 0) {
        const goal = sdmGoals.find(g => !!g.repo);
        if (!!goal) {
            repo = {
                name: goal.repo.name,
                owner: goal.repo.owner,
                providerId: goal.repo.providerId,
            };
        }
    }
    const sdmGoalSet = {
        sha: push.after.sha,
        branch: push.branch,
        goalSetId,
        goalSet,
        ts: Date.now(),
        repo,
        state: goalSetState(sdmGoals),
        goals: sdmGoals.map(g => ({
            name: g.name,
            uniqueName: g.uniqueName,
        })),
        provenance: constructProvenance(ctx),
        tags,
    };
    return sdmGoalSet;
}
exports.constructGoalSet = constructGoalSet;
async function storeGoalSet(ctx, goalSet) {
    await ctx.graphClient.mutate({
        name: "UpdateSdmGoalSet",
        variables: {
            goalSet: omitEmpty(goalSet, { omitZero: false }),
        },
        options: GraphClient_1.MutationNoCacheOptions,
    });
}
exports.storeGoalSet = storeGoalSet;
function goalSetState(goals) {
    if (goals.some(g => g.state === types_1.SdmGoalState.failure)) {
        return types_1.SdmGoalState.failure;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.canceled)) {
        return types_1.SdmGoalState.canceled;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.stopped)) {
        return types_1.SdmGoalState.stopped;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.in_process)) {
        return types_1.SdmGoalState.in_process;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.waiting_for_pre_approval)) {
        return types_1.SdmGoalState.waiting_for_pre_approval;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.waiting_for_approval)) {
        return types_1.SdmGoalState.waiting_for_approval;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.pre_approved)) {
        return types_1.SdmGoalState.pre_approved;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.approved)) {
        return types_1.SdmGoalState.approved;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.requested)) {
        return types_1.SdmGoalState.requested;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.planned)) {
        return types_1.SdmGoalState.planned;
    }
    else if (goals.some(g => g.state === types_1.SdmGoalState.skipped)) {
        return types_1.SdmGoalState.skipped;
    }
    else if (goals.every(g => g.state === types_1.SdmGoalState.success)) {
        return types_1.SdmGoalState.success;
    }
    else {
        const unknowns = goals.filter(g => g.state !== types_1.SdmGoalState.success).map(g => `${g.name}:${g.state}`);
        throw new Error("Unknown goal state(s): " + JSON.stringify(unknowns));
    }
}
exports.goalSetState = goalSetState;
function cleanPush(push) {
    const newPush = _.cloneDeep(push);
    if (!!newPush && !!newPush.goals) {
        delete newPush.goals;
    }
    return newPush;
}
//# sourceMappingURL=storeGoals.js.map