"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TerraformProviderDatasource = void 0;
const tslib_1 = require("tslib");
// TODO: types (#7154)
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const logger_1 = require("../../../logger");
const external_host_error_1 = require("../../../types/errors/external-host-error");
const decorator_1 = require("../../../util/cache/package/decorator");
const p = tslib_1.__importStar(require("../../../util/promises"));
const regex_1 = require("../../../util/regex");
const url_1 = require("../../../util/url");
const hashicorpVersioning = tslib_1.__importStar(require("../../versioning/hashicorp"));
const base_1 = require("../terraform-module/base");
const utils_1 = require("../terraform-module/utils");
class TerraformProviderDatasource extends base_1.TerraformDatasource {
    static id = 'terraform-provider';
    static defaultRegistryUrls = [
        'https://registry.terraform.io',
        'https://releases.hashicorp.com',
    ];
    static repositoryRegex = (0, regex_1.regEx)(/^hashicorp\/(?<packageName>\S+)$/);
    constructor() {
        super(TerraformProviderDatasource.id);
    }
    defaultRegistryUrls = TerraformProviderDatasource.defaultRegistryUrls;
    defaultVersioning = hashicorpVersioning.id;
    registryStrategy = 'hunt';
    async getReleases({ packageName, registryUrl, }) {
        // istanbul ignore if
        if (!registryUrl) {
            return null;
        }
        logger_1.logger.trace(`terraform-provider.getDependencies() packageName: ${packageName}`);
        if (registryUrl === this.defaultRegistryUrls[1]) {
            return await this.queryReleaseBackend(packageName, registryUrl);
        }
        const repository = TerraformProviderDatasource.getRepository({
            packageName,
        });
        const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryUrl);
        if (registryUrl === this.defaultRegistryUrls[0]) {
            return await this.queryRegistryExtendedApi(serviceDiscovery, registryUrl, repository);
        }
        return await this.queryRegistryVersions(serviceDiscovery, registryUrl, repository);
    }
    static getRepository({ packageName }) {
        return packageName.includes('/') ? packageName : `hashicorp/${packageName}`;
    }
    /**
     * this uses the api that terraform registry has in addition to the base api
     * this endpoint provides more information, such as release date
     * this api is undocumented.
     */
    async queryRegistryExtendedApi(serviceDiscovery, registryUrl, repository) {
        const backendURL = (0, utils_1.createSDBackendURL)(registryUrl, 'providers.v1', serviceDiscovery, repository);
        const res = (await this.http.getJson(backendURL)).body;
        const dep = {
            releases: res.versions.map((version) => ({
                version,
            })),
        };
        if (res.source) {
            dep.sourceUrl = res.source;
        }
        // set published date for latest release
        const latestVersion = dep.releases.find((release) => res.version === release.version);
        // istanbul ignore else
        if (latestVersion) {
            latestVersion.releaseTimestamp = res.published_at;
        }
        dep.homepage = `${registryUrl}/providers/${repository}`;
        return dep;
    }
    /**
     * this version uses the Provider Registry Protocol that all registries are required to implement
     * https://www.terraform.io/internals/provider-registry-protocol
     */
    async queryRegistryVersions(serviceDiscovery, registryUrl, repository) {
        const backendURL = (0, utils_1.createSDBackendURL)(registryUrl, 'providers.v1', serviceDiscovery, `${repository}/versions`);
        const res = (await this.http.getJson(backendURL))
            .body;
        const dep = {
            releases: res.versions.map(({ version }) => ({
                version,
            })),
        };
        return dep;
    }
    async queryReleaseBackend(packageName, registryURL) {
        const hashicorpPackage = packageName.replace('hashicorp/', '');
        const backendLookUpName = `terraform-provider-${hashicorpPackage}`;
        const backendURL = (0, url_1.joinUrlParts)(registryURL, backendLookUpName, `index.json`);
        const res = (await this.http.getJson(backendURL)).body;
        const dep = {
            releases: Object.keys(res.versions).map((version) => ({
                version,
            })),
            sourceUrl: (0, url_1.joinUrlParts)('https://github.com/terraform-providers', backendLookUpName),
        };
        return dep;
    }
    async getBuilds(registryURL, repository, version) {
        if (registryURL === TerraformProviderDatasource.defaultRegistryUrls[1]) {
            // check if registryURL === secondary backend
            const repositoryRegexResult = TerraformProviderDatasource.repositoryRegex.exec(repository)?.groups;
            if (!repositoryRegexResult) {
                // non hashicorp builds are not supported with releases.hashicorp.com
                return null;
            }
            const packageName = repositoryRegexResult.packageName;
            const backendLookUpName = `terraform-provider-${packageName}`;
            let versionReleaseBackend;
            try {
                versionReleaseBackend = await this.getReleaseBackendIndex(backendLookUpName, version);
            }
            catch (err) {
                /* istanbul ignore next */
                if (err instanceof external_host_error_1.ExternalHostError) {
                    throw err;
                }
                logger_1.logger.debug({ err, backendLookUpName, version }, `Failed to retrieve builds for ${backendLookUpName} ${version}`);
                return null;
            }
            return versionReleaseBackend.builds;
        }
        // check public or private Terraform registry
        const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryURL);
        if (!serviceDiscovery) {
            logger_1.logger.trace(`Failed to retrieve service discovery from ${registryURL}`);
            return null;
        }
        const backendURL = (0, utils_1.createSDBackendURL)(registryURL, 'providers.v1', serviceDiscovery, repository);
        const versionsResponse = (await this.http.getJson(`${backendURL}/versions`)).body;
        if (!versionsResponse.versions) {
            logger_1.logger.trace(`Failed to retrieve version list for ${backendURL}`);
            return null;
        }
        const builds = versionsResponse.versions.find((value) => value.version === version);
        if (!builds) {
            logger_1.logger.trace(`No builds found for ${repository}:${version} on ${registryURL}`);
            return null;
        }
        const result = await p.map(builds.platforms, async (platform) => {
            const buildURL = `${backendURL}/${version}/download/${platform.os}/${platform.arch}`;
            try {
                const res = (await this.http.getJson(buildURL)).body;
                const newBuild = {
                    name: repository,
                    url: res.download_url,
                    version,
                    ...res,
                };
                return newBuild;
            }
            catch (err) {
                /* istanbul ignore next */
                if (err instanceof external_host_error_1.ExternalHostError) {
                    throw err;
                }
                logger_1.logger.debug({ err, url: buildURL }, 'Failed to retrieve build');
                return null;
            }
        }, { concurrency: 4 });
        const filteredResult = result.filter(is_1.default.truthy);
        return filteredResult.length === result.length ? filteredResult : null;
    }
    async getReleaseBackendIndex(backendLookUpName, version) {
        return (await this.http.getJson(`${TerraformProviderDatasource.defaultRegistryUrls[1]}/${backendLookUpName}/${version}/index.json`)).body;
    }
}
exports.TerraformProviderDatasource = TerraformProviderDatasource;
tslib_1.__decorate([
    (0, decorator_1.cache)({
        namespace: `datasource-${TerraformProviderDatasource.id}`,
        key: (getReleasesConfig) => `${getReleasesConfig.registryUrl}/${TerraformProviderDatasource.getRepository(getReleasesConfig)}`,
    })
], TerraformProviderDatasource.prototype, "getReleases", null);
tslib_1.__decorate([
    (0, decorator_1.cache)({
        namespace: `datasource-${TerraformProviderDatasource.id}-builds`,
        key: (registryURL, repository, version) => `${registryURL}/${repository}/${version}`,
    })
], TerraformProviderDatasource.prototype, "getBuilds", null);
tslib_1.__decorate([
    (0, decorator_1.cache)({
        namespace: `datasource-${TerraformProviderDatasource.id}-releaseBackendIndex`,
        key: (backendLookUpName, version) => `${backendLookUpName}/${version}`,
    })
], TerraformProviderDatasource.prototype, "getReleaseBackendIndex", null);
//# sourceMappingURL=index.js.map