@forge/cli

14.1.0-next.1-experimental-13c2f8d14.1.0-next.21-experimental-554d417
out/command-line/controller/global-install-controller.js
+out/command-line/controller/global-install-controller.jsNew file
+209
Index: package/out/command-line/controller/global-install-controller.js
===================================================================
--- package/out/command-line/controller/global-install-controller.js
+++ package/out/command-line/controller/global-install-controller.js
@@ -0,0 +1,209 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.GlobalInstallController = void 0;
+const cli_shared_1 = require("@forge/cli-shared");
+const site_translation_1 = require("../../installations/site-translation");
+const LINKABLE_CONNECTIONS = [
+    { product: 'Jira', kind: 'site' },
+    { product: 'Confluence', kind: 'site' },
+    { product: 'Bitbucket', kind: 'workspace' }
+];
+class GlobalInstallController {
+    view;
+    identityClient;
+    installUnitClient;
+    installationService;
+    supportedProductsService;
+    constructor(view, identityClient, installUnitClient, installationService, supportedProductsService) {
+        this.view = view;
+        this.identityClient = identityClient;
+        this.installUnitClient = installUnitClient;
+        this.installationService = installationService;
+        this.supportedProductsService = supportedProductsService;
+    }
+    async run({ appId, environment, nonInteractive, unitId, product, site }) {
+        if ((product || site) && !unitId) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.connectRequiresUnit);
+        }
+        const environmentType = await this.assertDeployed(appId, environment);
+        const isNonInteractive = nonInteractive || Boolean(unitId);
+        const target = await this.resolveTarget(unitId, isNonInteractive);
+        if (isNonInteractive && (product || site)) {
+            await this.linkConnectionByFlags(appId, environment, target.unitId, product, site);
+            return;
+        }
+        const existingInstallationId = await this.installUnitClient.getUnitInstallationId(appId, environment, target.unitId);
+        if (existingInstallationId) {
+            this.view.showAlreadyInstalled(target);
+            const alreadyLinkedProducts = await this.installUnitClient.getLinkedConnectionProducts(appId, existingInstallationId);
+            await this.linkConnections({
+                environment,
+                installationId: existingInstallationId,
+                nonInteractive: isNonInteractive,
+                alreadyLinkedProducts
+            });
+            return;
+        }
+        this.view.showInstallConfirmation(target);
+        if (!isNonInteractive && !(await this.view.confirmContinueInstall())) {
+            return;
+        }
+        const { installationId } = await this.view.runInstall(() => this.installUnitClient.installAppIntoUnit({
+            appId,
+            environmentKey: environment,
+            unitId: target.unitId,
+            environmentType
+        }));
+        this.view.showInstallSuccess(environment, target);
+        await this.linkConnections({ environment, installationId, nonInteractive: isNonInteractive });
+    }
+    async runUpgrade({ appId, environment, nonInteractive, unitId, majorVersionId, isCodeUpgrade }) {
+        await this.assertDeployed(appId, environment);
+        const installations = await this.installUnitClient.getUnitInstallations(appId, environment);
+        if (!installations.length) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noUnitInstallations);
+        }
+        let selectedUnitId;
+        if (unitId) {
+            const match = installations.find((installation) => installation.unitId === unitId);
+            if (!match) {
+                throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.notInstalledInUnit(unitId));
+            }
+            selectedUnitId = match.unitId;
+        }
+        else if (installations.length === 1) {
+            selectedUnitId = installations[0].unitId;
+        }
+        else if (nonInteractive) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.nonInteractiveUnitRequired);
+        }
+        else {
+            selectedUnitId = (await this.view.promptForUnitUpgrade(installations)).unitId;
+        }
+        await this.view.runUpgrade(() => this.installUnitClient.upgradeAppInUnit({
+            appId,
+            environmentKey: environment,
+            unitId: selectedUnitId,
+            majorVersionId,
+            isCodeUpgrade
+        }));
+        this.view.showUpgradeSuccess(environment, { unitId: selectedUnitId });
+    }
+    async assertDeployed(appId, environment) {
+        const permissions = await this.installationService.getAppEnvironmentPermissions(appId, environment);
+        if (!permissions?.hasDeployments) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.error.noDeploymentFound(environment));
+        }
+        return permissions.environmentType;
+    }
+    async resolveTarget(unitFlag, nonInteractive) {
+        if (unitFlag) {
+            return { unitId: unitFlag };
+        }
+        const org = await this.resolveOrg(nonInteractive);
+        const { unit, onlyUnit } = await this.resolveUnit(org, nonInteractive);
+        return { unitId: unit.id, unitName: unit.name, orgName: org.name, onlyUnit };
+    }
+    async resolveOrg(nonInteractive) {
+        const orgs = await this.view.fetchWithSpinner(() => this.identityClient.getAllOrgs());
+        if (!orgs.length) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noOrgs);
+        }
+        if (orgs.length === 1) {
+            return orgs[0];
+        }
+        if (nonInteractive) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.nonInteractiveOrgRequired);
+        }
+        return this.view.selectOrganization(orgs);
+    }
+    async resolveUnit(org, nonInteractive) {
+        const units = await this.view.fetchWithSpinner(() => this.identityClient.getAllUnits(org.id));
+        if (!units.length) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noUnits(org.name));
+        }
+        const onlyUnit = units.length === 1;
+        if (onlyUnit) {
+            return { unit: units[0], onlyUnit };
+        }
+        if (nonInteractive) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.nonInteractiveUnitRequired);
+        }
+        this.view.showSelectUnitOverview();
+        return { unit: await this.view.selectUnit(units), onlyUnit };
+    }
+    async linkConnections({ environment, installationId, nonInteractive, alreadyLinkedProducts = [] }) {
+        if (nonInteractive) {
+            return;
+        }
+        const linkedSet = new Set(alreadyLinkedProducts.map((product) => product.toLowerCase()));
+        const options = LINKABLE_CONNECTIONS.map((connection) => ({
+            ...connection,
+            linked: linkedSet.has(connection.product.toLowerCase())
+        }));
+        if (options.every((option) => option.linked)) {
+            return;
+        }
+        if (!(await this.view.confirmConnect(false))) {
+            return;
+        }
+        if (!installationId) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noInstallationId);
+        }
+        for (;;) {
+            const selected = await this.view.selectConnection(options);
+            if (selected.linked) {
+                this.view.showAlreadyLinked(selected.product);
+                continue;
+            }
+            const kind = selected.kind;
+            const target = await this.view.promptForConnectionTarget(kind);
+            const url = this.supportedProductsService.validateSite(this.normalizeTarget(kind, target), selected.product);
+            this.view.showConnectConfirmation(selected.product, kind);
+            if (!(await this.view.confirmContinueConnect())) {
+                continue;
+            }
+            await this.performLink(environment, installationId, selected.product, kind, url);
+            selected.linked = true;
+            if (options.every((option) => option.linked)) {
+                break;
+            }
+            if (!(await this.view.confirmConnect(true))) {
+                break;
+            }
+        }
+    }
+    async linkConnectionByFlags(appId, environment, unitId, product, site) {
+        if (!product || !site) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.connectFlagsRequired);
+        }
+        const connection = LINKABLE_CONNECTIONS.find((c) => c.product.toLowerCase() === product.toLowerCase());
+        if (!connection) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.unsupportedConnection(product));
+        }
+        const installationId = await this.installUnitClient.getUnitInstallationId(appId, environment, unitId);
+        if (!installationId) {
+            throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.notInstalledInUnit(unitId));
+        }
+        const url = this.supportedProductsService.validateSite(this.normalizeTarget(connection.kind, site.toString()), connection.product);
+        await this.performLink(environment, installationId, connection.product, connection.kind, url);
+    }
+    async performLink(environment, installationId, product, kind, url) {
+        await this.view.runConnect(() => this.installUnitClient.linkOptionalConnection(installationId, product, url));
+        this.view.showConnectionSuccess(environment, product, this.connectionDisplay(kind, url));
+    }
+    normalizeTarget(kind, input) {
+        if (kind === 'workspace') {
+            return site_translation_1.BitbucketTranslator.normalizeWorkspaceInput(input);
+        }
+        return input.trim();
+    }
+    connectionDisplay(kind, url) {
+        if (kind === 'workspace') {
+            return site_translation_1.BitbucketTranslator.extractWorkspaceName(url) || url.host;
+        }
+        return url.host;
+    }
+}
+exports.GlobalInstallController = GlobalInstallController;
+//# sourceMappingURL=global-install-controller.js.map
\ No newline at end of file