"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.hasMetadataAnnotation = exports.filterIgnoredSpecs = exports.calculateChanges = exports.changeResource = void 0;
const configuration_1 = require("@atomist/automation-client/lib/configuration");
const _ = require("lodash");
const apply_1 = require("../kubernetes/apply");
const delete_1 = require("../kubernetes/delete");
const secret_1 = require("../kubernetes/secret");
const spec_1 = require("../kubernetes/spec");
const application_1 = require("./application");
const previousSpecVersion_1 = require("./previousSpecVersion");
/**
 * Delete/apply resources in change.  The spec file provided by the
 * change.path may contain multiple specs.  The requested change is
 * applied to each.
 */
async function changeResource(p, change) {
    const beforeContents = await previousSpecVersion_1.previousSpecVersion(p.baseDir, change.path, change.sha);
    const beforeSpecs = spec_1.parseKubernetesSpecs(beforeContents);
    let specs;
    if (change.change !== "delete") {
        const specFile = await p.getFile(change.path);
        if (!specFile) {
            throw new Error(`Resource spec file '${change.path}' does not exist in project`);
        }
        const specContents = await specFile.getContent();
        specs = spec_1.parseKubernetesSpecs(specContents);
    }
    const changes = calculateChanges(beforeSpecs, specs, change.change);
    const syncOpts = configuration_1.configurationValue("sdm.k8s.options.sync", {});
    for (const specChange of changes) {
        let changer;
        if (specChange.change === "delete") {
            changer = delete_1.deleteSpec;
        }
        else {
            changer = apply_1.applySpec;
        }
        _.set(specChange.spec, "metadata.annotations['atomist.com/sync-sha']", change.sha);
        if (specChange.change !== "delete" && specChange.spec.kind === "Secret" && syncOpts.secretKey) {
            specChange.spec = await secret_1.decryptSecret(specChange.spec, syncOpts.secretKey);
        }
        await changer(specChange.spec);
    }
}
exports.changeResource = changeResource;
/**
 * Inspect before and after specs to determine actions.
 *
 * If the action is "delete", return delete actions for all specs in
 * `before` that do not have an ignore annotation for the current SDM,
 * as the "delete" action implies there are no `after` specs.
 *
 * If the action is "apply", return apply actions for all specs in
 * `after` and delete actions for all specs in `before` that are not
 * in `after` that do not have an ignore annotation for the current
 * SDM.  If a `before` spec contains a sync ignore annotation for the
 * current SDM and the `after` annotation does not, the `after` spec
 * with an "apply" action is included in the returned changes.  If an
 * `after` spec contains a sync ignore annotation for the current SDM,
 * then it is omitted from the returned changes, regardless of whether
 * it appears in the `before` specs or not.
 *
 * @param before The specs before the change
 * @param after The specs after the change
 * @param change The type of change
 * @return Array containing the type of change for each spec
 */
function calculateChanges(before, after, change) {
    const beforeFiltered = filterIgnoredSpecs(before);
    if (change === "delete") {
        return beforeFiltered.map(spec => ({ change, spec }));
    }
    const changes = filterIgnoredSpecs(after).map(spec => ({ change, spec }));
    for (const spec of beforeFiltered) {
        if (!after.some(a => application_1.sameObject(a, spec))) {
            changes.push({ change: "delete", spec });
        }
    }
    return changes;
}
exports.calculateChanges = calculateChanges;
/**
 * Returned array of specs with those that should be ignored filtered
 * out.
 *
 * @param specs Array of specs to check
 * @return Array of not ignored specs.
 */
function filterIgnoredSpecs(specs) {
    if (!specs) {
        return [];
    }
    return specs.filter(spec => !hasMetadataAnnotation(spec, "sync", "ignore"));
}
exports.filterIgnoredSpecs = filterIgnoredSpecs;
/**
 * Check if the Kubernetes Object has an annotation that is relevant to the current SDM
 * @param spec the spec to inspect
 * @param annotationKey the key to validate for
 * @param annotationValue the value validate for
 * @returns the result of the annotation inspection
 */
function hasMetadataAnnotation(spec, annotationKey, annotationValue) {
    const sdmName = configuration_1.configurationValue("name");
    const nameKey = `atomist.com/sdm-pack-k8s/${annotationKey}/${sdmName}`;
    const specValue = _.get(spec, `metadata.annotations['${nameKey}']`);
    return !!specValue && specValue === annotationValue;
}
exports.hasMetadataAnnotation = hasMetadataAnnotation;
//# sourceMappingURL=change.js.map