"use strict";
/*
 * Copyright 2019-2020 Unbounded Systems, LLC
 *
 * 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.RegistryDockerImage = void 0;
const core_1 = require("@adpt/core");
const utils_1 = require("@adpt/utils");
const lodash_1 = require("lodash");
const url_1 = require("url");
const util_1 = require("util");
const action_1 = require("../action");
const image_ref_1 = require("./image-ref");
function buildNameTag(url, pathTag) {
    if (pathTag === undefined)
        return undefined;
    const ref = image_ref_1.mutableImageRef();
    ref.pathTag = pathTag; // defaults tag to 'latest' if not present
    ref.domain = url;
    if (!ref.registryTag)
        throw new utils_1.InternalError(`Image reference '${ref.ref}' does not have a registryTag`);
    return ref.registryTag;
}
function urlToRegistryString(registryUrl) {
    let ret;
    if (registryUrl.startsWith("http")) {
        const parsed = new url_1.URL(registryUrl);
        ret = parsed.host + parsed.pathname;
    }
    else {
        ret = registryUrl;
    }
    if (ret.endsWith("/"))
        ret = ret.slice(0, -1);
    return ret;
}
function normalizeRegistryUrl(url) {
    if (util_1.isString(url))
        url = { external: url, internal: url };
    return {
        external: urlToRegistryString(url.external),
        internal: urlToRegistryString(url.internal)
    };
}
/**
 * Represents a Docker image in a registry.
 * @remarks
 * If the image does not exist in the specified registry, it will be pushed
 * to that registry.
 * @public
 */
class RegistryDockerImage extends action_1.Action {
    constructor(props) {
        super(props);
        /** @internal */
        this.dependsOn = (_goalStatus, helpers) => {
            if (!core_1.isHandle(this.props.imageSrc))
                return undefined;
            return helpers.dependsOn(this.props.imageSrc);
        };
        this.registry = normalizeRegistryUrl(props.registryUrl);
    }
    /**
     * Returns information about the version of the Docker image that reflects
     * the current set of props for the component and has been pushed to the
     * registry.
     * @remarks
     * Returns undefined if the `props.imageSrc` component's `latestImage` method
     * returns undefined (depending on the component referenced by
     * `props.imageSrc`, that may indicate the source image has not been built).
     * Also returns undefined if the current image has not yet been
     * pushed to the registry.
     */
    image() {
        const srcImage = core_1.callInstanceMethod(this.props.imageSrc, undefined, "latestImage");
        const latestImg = this.latestImage();
        const latestReg = this.latestRegistryUrl_ || this.state.registryUrl;
        if (!srcImage || !latestImg || !latestReg)
            return undefined;
        if (srcImage.id === latestImg.id &&
            this.currentNameTag(srcImage.pathTag) === latestImg.nameTag &&
            lodash_1.isEqual(this.registry, latestReg)) {
            return latestImg;
        }
        return undefined; // Pushed image is not current
    }
    /**
     * Returns information about the most current version of the Docker image
     * that has been pushed to the registry.
     * @remarks
     * Returns undefined if no image has ever been pushed by this component.
     */
    latestImage() { return this.latestImage_ || this.state.image; }
    /** @internal */
    initialState() { return {}; }
    /** @internal */
    shouldAct(diff) {
        if (diff === core_1.ChangeType.delete)
            return false;
        let name = this.getNewPathTag() || this.srcImageName();
        name = name ? ` '${name}'` : "";
        return { act: true, detail: `Pushing image${name} to ${this.registry.external}` };
    }
    /** @internal */
    async action(op) {
        if (op === core_1.ChangeType.delete || op === core_1.ChangeType.none)
            return;
        const info = await core_1.callInstanceMethod(this.props.imageSrc, undefined, "pushTo", this.registry.external, this.getNewPathTag());
        if (info === undefined) {
            throw new Error(`Image source component did not push image to registry`);
        }
        this.latestImage_ = Object.assign({}, info);
        this.latestRegistryUrl_ = this.registry;
        this.setState({
            image: this.latestImage_,
            registryUrl: this.latestRegistryUrl_,
        });
    }
    currentNameTag(pathTag) {
        return buildNameTag(this.registry.internal, this.getNewPathTag() || pathTag);
    }
    getNewPathTag() {
        return this.props.newPathTag || this.props.newTag;
    }
    srcImageName() {
        return core_1.callFirstInstanceWithMethod(this.props.imageSrc, undefined, "displayName");
    }
}
exports.RegistryDockerImage = RegistryDockerImage;
//# sourceMappingURL=RegistryDockerImage.js.map