"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.configureSdm = void 0;
const string_1 = require("@atomist/automation-client/lib/internal/util/string");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const _ = require("lodash");
const ConfigurationValues_1 = require("../../api/machine/ConfigurationValues");
const FulfillGoalOnRequested_1 = require("../handlers/events/delivery/goals/FulfillGoalOnRequested");
const goalExecution_1 = require("../handlers/events/delivery/goals/goalExecution");
const CacheCleanupAutomationEventListener_1 = require("../handlers/events/delivery/goals/k8s/CacheCleanupAutomationEventListener");
const goalSigning_1 = require("../signing/goalSigning");
const array_1 = require("../util/misc/array");
const SdmGoalMetricReportingAutomationEventListener_1 = require("../util/SdmGoalMetricReportingAutomationEventListener");
const startupMessage_1 = require("../util/startupMessage");
const defaultSoftwareDeliveryMachineConfiguration_1 = require("./defaultSoftwareDeliveryMachineConfiguration");
const InvokeSdmStartupListenersAutomationEventListener_1 = require("./InvokeSdmStartupListenersAutomationEventListener");
const modes_1 = require("./modes");
/**
 * Configure and set up a Software Delivery Machine instance with the automation-client framework for standalone
 * or single goal based execution
 * @param {(configuration: (Configuration & SoftwareDeliveryMachineOptions)) => SoftwareDeliveryMachine} machineMaker
 * @param {ConfigureOptions} options
 * @returns {ConfigurationPostProcessor}
 */
function configureSdm(machineMaker, options = {}) {
    return async (config) => {
        let mergedConfig = config;
        // Configure the local SDM
        mergedConfig = await doWithSdmLocal(local => {
            return local.configureLocal()(mergedConfig);
        }) || mergedConfig;
        const defaultSdmConfiguration = defaultSoftwareDeliveryMachineConfiguration_1.defaultSoftwareDeliveryMachineConfiguration(config);
        mergedConfig = _.merge(defaultSdmConfiguration, mergedConfig);
        ConfigurationValues_1.validateConfigurationValues(mergedConfig, options);
        const sdm = await machineMaker(mergedConfig);
        await doWithSdmLocal(local => sdm.addExtensionPacks(local.LocalLifecycle, local.LocalSdmConfig));
        // Configure the job forking ability
        await configureJobLaunching(mergedConfig, sdm);
        configureGoalSigning(mergedConfig);
        await registerMetadata(mergedConfig, sdm);
        // Register startup message detail
        _.update(mergedConfig, "logging.banner.contributors", old => !!old ? old : []);
        mergedConfig.logging.banner.contributors.push(startupMessage_1.sdmStartupMessage(sdm), startupMessage_1.sdmExtensionPackStartupMessage(sdm));
        _.update(mergedConfig, "listeners", old => !!old ? old : []);
        mergedConfig.listeners.push(new InvokeSdmStartupListenersAutomationEventListener_1.InvokeSdmStartupListenersAutomationEventListener(sdm), new SdmGoalMetricReportingAutomationEventListener_1.SdmGoalMetricReportingAutomationEventListener());
        return mergedConfig;
    };
}
exports.configureSdm = configureSdm;
/**
 * Configure how this SDM is going to handle goals
 * @param mergedConfig
 * @param machine
 */
async function configureJobLaunching(mergedConfig, machine) {
    const forked = process.env.ATOMIST_ISOLATED_GOAL === "true";
    if (forked) {
        configureSdmToRunExactlyOneGoal(mergedConfig, machine);
    }
    else {
        // initialize the GoalSchedulers
        for (const goalScheduler of array_1.toArray(mergedConfig.sdm.goalScheduler || [])) {
            if (!!goalScheduler.initialize) {
                await goalScheduler.initialize(mergedConfig);
            }
        }
        _.update(mergedConfig, "commands", old => !!old ? old : []);
        mergedConfig.commands.push(...machine.commandHandlers);
        _.update(mergedConfig, "events", old => !!old ? old : []);
        mergedConfig.events.push(...machine.eventHandlers);
        _.update(mergedConfig, "ingesters", old => !!old ? old : []);
        mergedConfig.ingesters.push(...machine.ingesters);
    }
}
/**
 * Configure SDM to run only one goal
 * @param mergedConfig
 * @param sdm
 */
function configureSdmToRunExactlyOneGoal(mergedConfig, sdm) {
    if (process.env.ATOMIST_JOB_NAME) {
        mergedConfig.name = process.env.ATOMIST_REGISTRATION_NAME;
    }
    else {
        mergedConfig.name = `${mergedConfig.name}-${process.env.ATOMIST_GOAL_ID || string_1.guid()}`;
    }
    // Force ephemeral policy and no handlers or ingesters
    mergedConfig.policy = "ephemeral";
    mergedConfig.commands = [];
    mergedConfig.events = [
        () => new FulfillGoalOnRequested_1.FulfillGoalOnRequested(sdm.goalFulfillmentMapper, [...sdm.goalExecutionListeners])
    ];
    mergedConfig.ingesters = [];
    mergedConfig.ws.enabled = false;
    mergedConfig.cluster.enabled = false;
    mergedConfig.listeners.push(new goalExecution_1.GoalExecutionAutomationEventListener(sdm), new CacheCleanupAutomationEventListener_1.CacheCleanupAutomationEventListener(sdm));
    mergedConfig.requestProcessorFactory =
        (automations, cfg, listeners) => new goalExecution_1.GoalExecutionRequestProcessor(automations, cfg, listeners);
    // Disable app events for forked clients
    mergedConfig.applicationEvents.enabled = false;
}
/**
 * Configure SDM to sign and verify goals
 * @param mergedConfig
 */
function configureGoalSigning(mergedConfig) {
    if (!!mergedConfig.sdm.goalSigning && mergedConfig.sdm.goalSigning.enabled === true) {
        _.update(mergedConfig, "graphql.listeners", old => !!old ? old : []);
        mergedConfig.graphql.listeners.push(new goalSigning_1.GoalSigningAutomationEventListener(mergedConfig.sdm.goalSigning));
    }
}
async function registerMetadata(config, machine) {
    // tslint:disable-next-line:no-implicit-dependencies
    const sdmPj = require("@atomist/sdm/package.json");
    config.metadata = Object.assign(Object.assign({}, config.metadata), { "atomist.sdm": `${sdmPj.name}:${sdmPj.version}`, "atomist.sdm.name": machine.name, "atomist.sdm.extension-packs": machine.extensionPacks.map(ex => `${ex.name}:${ex.version}`).join(", ") });
    config.sdm.name = machine.name;
    await doWithSdmLocal(() => {
        // tslint:disable-next-line:no-implicit-dependencies
        const sdmLocalPj = require("@atomist/sdm-local/package.json");
        config.metadata["atomist.sdm-local"] = `${sdmLocalPj.name}:${sdmLocalPj.version}`;
    });
}
/**
 * Perform the given operation with the sdm-local module if it's available.
 * If it isn't, silently continue without error.
 * @param {(sdmLocal: any) => any} callback
 * @return {any}
 */
async function doWithSdmLocal(callback) {
    if (modes_1.isInLocalMode() || modes_1.isGitHubAction()) {
        // tslint:disable-next-line:no-implicit-dependencies
        const local = attemptToRequire("@atomist/sdm-local", !process.env.ATOMIST_NPM_LOCAL_LINK);
        if (local) {
            return callback(local);
        }
        else {
            logger_1.logger.warn("Skipping local mode configuration because 'ATOMIST_NPM_LOCAL_LINK' was defined, " +
                "but '@atomist/sdm-local' could not be loaded");
            return undefined;
        }
    }
    return undefined;
}
/**
 * Attempt to NPM require module
 * @param module
 * @param failOnError
 */
function attemptToRequire(module, failOnError) {
    try {
        return require(module);
    }
    catch (err) {
        if (failOnError) {
            throw new Error(`Unable to load '${module}'. Please install with 'npm install ${module}'.`);
        }
        else {
            return undefined;
        }
    }
}
//# sourceMappingURL=configureSdm.js.map