"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.handleSdmGoalSetEvent = exports.Queue = exports.DefaultQueueOptions = void 0;
const graphQL_1 = require("@atomist/automation-client/lib/graph/graphQL");
const HandlerResult_1 = require("@atomist/automation-client/lib/HandlerResult");
const GraphClient_1 = require("@atomist/automation-client/lib/spi/graph/GraphClient");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const _ = require("lodash");
const storeGoals_1 = require("../../../api-helper/goal/storeGoals");
const logInterpreters_1 = require("../../../api-helper/log/logInterpreters");
const types_1 = require("../../../typings/types");
const commonPushTests_1 = require("../../mapping/support/commonPushTests");
const GoalNameGenerator_1 = require("../GoalNameGenerator");
const GoalWithFulfillment_1 = require("../GoalWithFulfillment");
const environment_1 = require("../support/environment");
exports.DefaultQueueOptions = {
    concurrent: 2,
    fetch: 10,
};
/**
 * Goal to queue current goal set until it is the first in the list and can execute
 */
class Queue extends GoalWithFulfillment_1.FulfillableGoal {
    constructor(options = exports.DefaultQueueOptions, ...dependsOn) {
        super(Object.assign({}, GoalWithFulfillment_1.getGoalDefinitionFrom(options, GoalNameGenerator_1.DefaultGoalNameGenerator.generateName("queue"), QueueDefinition)), ...dependsOn);
        this.options = options;
    }
    register(sdm) {
        super.register(sdm);
        const optsToUse = Object.assign(Object.assign({}, exports.DefaultQueueOptions), this.options);
        this.addFulfillment({
            name: `queue-${this.definition.uniqueName}`,
            pushTest: commonPushTests_1.AnyPush,
            goalExecutor: async (gi) => {
                const { context, configuration, goalEvent, progressLog } = gi;
                const goalSets = await context.graphClient.query({
                    name: "InProcessSdmGoalSets",
                    variables: {
                        fetch: optsToUse.fetch + optsToUse.concurrent,
                        offset: 0,
                        registration: [configuration.name],
                    },
                    options: GraphClient_1.QueryNoCacheOptions,
                });
                if (!!goalSets && !!goalSets.SdmGoalSet) {
                    const ix = goalSets.SdmGoalSet.findIndex(gs => gs.goalSetId === goalEvent.goalSetId);
                    if (ix >= 0) {
                        progressLog.write(`Goal set currently at position ${ix + 1} in queue`);
                        if (ix < optsToUse.concurrent) {
                            progressLog.write(`Goal set can start immediately`);
                            return {
                                state: types_1.SdmGoalState.success,
                            };
                        }
                    }
                    else {
                        progressLog.write(`Goal set not currently pending`);
                    }
                }
                return {
                    state: types_1.SdmGoalState.in_process,
                };
            },
            logInterpreter: logInterpreters_1.LogSuppressor,
        });
        sdm.addEvent({
            name: `OnAnySdmGoalSet`,
            description: `Handle queuing for goal ${this.definition.uniqueName}`,
            subscription: graphQL_1.subscription({
                name: "OnAnySdmGoalSet",
                variables: {
                    registration: [sdm.configuration.name],
                },
            }),
            listener: handleSdmGoalSetEvent(optsToUse, this.definition, sdm.configuration),
        });
    }
}
exports.Queue = Queue;
const QueueDefinition = {
    uniqueName: "queue",
    displayName: "queue goals",
    environment: environment_1.IndependentOfEnvironment,
    workingDescription: "Queued",
    completedDescription: "Started goals",
    failedDescription: "Failed to queue goals",
};
function handleSdmGoalSetEvent(options, definition, configuration) {
    return async (e, ctx) => {
        const optsToUse = Object.assign(Object.assign({}, exports.DefaultQueueOptions), options);
        const goalSets = await ctx.graphClient.query({
            name: "InProcessSdmGoalSets",
            variables: {
                fetch: optsToUse.fetch + optsToUse.concurrent,
                offset: 0,
                registration: [configuration.name],
            },
            options: GraphClient_1.QueryNoCacheOptions,
        });
        if (goalSets && goalSets.SdmGoalSet && goalSets.SdmGoalSet.length > 0) {
            await startGoals(goalSets, optsToUse, definition, ctx);
            // await updateGoals(goalSets, optsToUse, details, ctx);
        }
        return HandlerResult_1.Success;
    };
}
exports.handleSdmGoalSetEvent = handleSdmGoalSetEvent;
async function loadQueueGoals(goalsSets, definition, ctx) {
    return (await ctx.graphClient.query({
        name: "SdmGoalsByGoalSetIdAndUniqueName",
        variables: {
            goalSetId: goalsSets.map(gs => gs.goalSetId),
            uniqueName: [definition.uniqueName],
        },
        options: GraphClient_1.QueryNoCacheOptions,
    })).SdmGoal || [];
}
async function startGoals(goalSets, options, definition, ctx) {
    // Update goal sets that are allowed to start
    const goalSetsToStart = goalSets.SdmGoalSet.slice(0, options.concurrent)
        .filter(gs => gs.goals.some(g => g.uniqueName === definition.uniqueName));
    if (goalSetsToStart.length > 0) {
        logger_1.logger.debug(`Following goal sets are ready to start: '${goalSetsToStart.map(gs => gs.goalSetId).join(", ")}'`);
        const queueGoals = await loadQueueGoals(goalSetsToStart, definition, ctx);
        for (const goalSetToStart of goalSetsToStart) {
            const queueGoal = _.maxBy(queueGoals.filter(g => g.goalSetId === goalSetToStart.goalSetId), "ts");
            logger_1.logger.debug(`Updating goal '${definition.uniqueName}' of goal set '${queueGoal.goalSetId}' to 'success'`);
            if (queueGoal.state === types_1.SdmGoalState.in_process) {
                await storeGoals_1.updateGoal(ctx, queueGoal, {
                    state: types_1.SdmGoalState.success,
                    description: definition.completedDescription,
                });
            }
        }
    }
}
//# sourceMappingURL=Queue.js.map