@forge/cli

14.1.0-next.1-experimental-13c2f8d14.1.0-next.21-experimental-554d417
out/installations/graphql-client.js
~out/installations/graphql-client.jsModified
+122−34
Index: package/out/installations/graphql-client.js
===================================================================
--- package/out/installations/graphql-client.js
+++ package/out/installations/graphql-client.js
@@ -4,8 +4,9 @@
 const exponential_backoff_1 = require("exponential-backoff");
 const ari_1 = require("@forge/util/packages/ari");
 const cli_shared_1 = require("@forge/cli-shared");
 const egress_1 = require("@forge/egress");
+const installation_helper_1 = require("./installation-helper");
 const harmonized_app_installation_errors_1 = require("./harmonized-apps/harmonized-app-installation-errors");
 exports.UNINSTALLATION_EVENT_POLL_INTERVAL = 500;
 const DECOUPLED_STATE_NOT_ENABLED_CODE = 'DECOUPLED_STATE_NOT_ENABLED';
 const APP_INSTALLATION_LIMIT_WARNING = 'APP_INSTALLATION_LIMIT_WARNING';
@@ -136,16 +137,24 @@
     pause;
     logger;
     SITE_RESOURCE_TYPE = 'site';
     WORKSPACE_RESOURCE_TYPE = 'workspace';
+    UNIT_RESOURCE_TYPE = 'unit';
     static buildInstallationContext(product, resourceId, resourceType) {
         const ari = ari_1.AnyAri.create({
             resourceOwner: (0, cli_shared_1.ariResourceOwner)(product),
             resourceType: resourceType,
             resourceId: resourceId
         });
         return ari.toString();
     }
+    static buildUnitInstallationContext(unitId) {
+        return ari_1.AnyAri.create({
+            resourceOwner: 'platform',
+            resourceType: 'unit',
+            resourceId: unitId
+        }).toString();
+    }
     constructor(graphqlClient, sitedProductTranslator, bitbucketTranslator, pause, logger = cli_shared_1.NullLogger) {
         this.graphqlClient = graphqlClient;
         this.sitedProductTranslator = sitedProductTranslator;
         this.bitbucketTranslator = bitbucketTranslator;
@@ -184,15 +193,15 @@
             }
         }
         return undefined;
     }
-    async installAppIntoSite({ environmentKey, site, product, appId, licenseOverride, overrides, majorVersionId, environmentType }) {
-        const workspaceAri = await this.buildInstallationContext(product, site);
+    async submitInstallMutation({ installationContext, appId, environmentKey, licenseOverride, overrides, majorVersionId, environmentType }) {
         const query = `
       mutation forge_cli_installApplication($input: AppInstallationInput!) {
         installApp(input: $input) {
           success
           taskId
+          installationId
           errors {
             message
             extensions {
               errorType
@@ -203,9 +212,9 @@
       }
     `;
         const variables = {
             input: {
-                installationContext: workspaceAri,
+                installationContext,
                 appId,
                 environmentKey,
                 async: true,
                 licenseOverride,
@@ -213,9 +222,9 @@
                 ...(majorVersionId ? { versionId: majorVersionId } : {})
             }
         };
         const mutationResult = await this.graphqlClient.mutate(query, variables);
-        const { response: { installApp: { success, taskId, errors } }, requestId, extensions } = mutationResult;
+        const { response: { installApp: { success, taskId, installationId, errors } }, requestId, extensions } = mutationResult;
         const error = (0, cli_shared_1.getError)(errors);
         if (!success) {
             if (error.code === PERMISSION_DENIED) {
                 throw new PermissionDeniedError(requestId, ari_1.AnyAri.parse(appId).resourceId, error.statusCode);
@@ -229,13 +238,111 @@
         if (!taskId) {
             throw new MissingTaskIdError(error.message);
         }
         await this.monitorAppInstallOrUpgrade(taskId, !!majorVersionId, environmentKey, environmentType);
-        const warnings = this.extractInstallWarnings(extensions);
-        return {
-            warnings
-        };
+        return { installationId: installationId ?? undefined, warnings: this.extractInstallWarnings(extensions) };
     }
+    async installAppIntoSite({ environmentKey, site, product, appId, licenseOverride, overrides, majorVersionId, environmentType }) {
+        const { warnings } = await this.submitInstallMutation({
+            installationContext: await this.buildInstallationContext(product, site),
+            appId,
+            environmentKey,
+            licenseOverride,
+            overrides,
+            majorVersionId,
+            environmentType
+        });
+        return { warnings };
+    }
+    async installAppIntoUnit({ environmentKey, unitId, appId, licenseOverride, overrides, majorVersionId, environmentType }) {
+        const { installationId, warnings } = await this.submitInstallMutation({
+            installationContext: InstallationsGraphqlClient.buildUnitInstallationContext(unitId),
+            appId,
+            environmentKey,
+            licenseOverride,
+            overrides,
+            majorVersionId,
+            environmentType
+        });
+        const resolvedInstallationId = installationId ?? (await this.getUnitInstallationId(appId, environmentKey, unitId));
+        return { installationId: resolvedInstallationId, warnings };
+    }
+    async upgradeAppInUnit({ appId, environmentKey, unitId, majorVersionId, isCodeUpgrade }) {
+        const { success, errors, taskId, requestId } = await this.submitUpgradeMutation({
+            installationContext: InstallationsGraphqlClient.buildUnitInstallationContext(unitId),
+            appId,
+            environmentKey,
+            majorVersionId,
+            isCodeUpgrade
+        });
+        const error = (0, cli_shared_1.getError)(errors);
+        if (!success) {
+            throw new UpgradeError(`${error.message} (requestId: ${requestId || 'unknown'})`, {
+                requestId,
+                code: error.code,
+                statusCode: error.statusCode
+            });
+        }
+        if (!taskId) {
+            throw new MissingTaskIdError(error.message);
+        }
+        await this.monitorAppInstallOrUpgrade(taskId, !!majorVersionId, environmentKey);
+    }
+    async submitUpgradeMutation({ installationContext, appId, environmentKey, majorVersionId, isCodeUpgrade }) {
+        const query = `
+      mutation forge_cli_upgradeApplication($input: AppInstallationUpgradeInput!) {
+        upgradeApp(input: $input) {
+          success
+          taskId
+          errors {
+            message
+            extensions {
+              errorType
+              statusCode
+            }
+          }
+        }
+      }
+    `;
+        const { response: { upgradeApp: { success, errors, taskId } }, requestId } = await this.graphqlClient.mutate(query, {
+            input: {
+                installationContext,
+                appId,
+                environmentKey,
+                ...(majorVersionId ? { versionId: majorVersionId } : {}),
+                ...(isCodeUpgrade ? { computeOnly: isCodeUpgrade } : {}),
+                async: true
+            }
+        });
+        return { success, errors, taskId, requestId };
+    }
+    async getUnitInstallationId(appId, environmentKey, unitId) {
+        const installations = await this.listInstallations(appId, { environment: environmentKey });
+        return installations.find((installation) => installation.installedInUnit && installation.unitId === unitId)?.id;
+    }
+    async getUnitInstallations(appId, environmentKey) {
+        const installations = await this.listInstallations(appId, { environment: environmentKey });
+        return installations
+            .filter((installation) => Boolean(installation.installedInUnit && installation.unitId))
+            .map((installation) => ({
+            unitId: installation.unitId,
+            environmentKey: installation.environmentKey,
+            connections: (installation.connections ?? []).map((connection) => connection.product),
+            version: installation.version.version,
+            isLatest: installation.version.isLatest
+        }));
+    }
+    async getLinkedConnectionProducts(appId, installationId) {
+        const installation = await this.getInstallation(appId, installationId);
+        return (installation.connections ?? []).map((connection) => connection.product);
+    }
+    async linkOptionalConnection(installationId, product, site) {
+        const context = await this.buildInstallationContext(product, site);
+        await this.provisionInstallationLink({
+            installationAri: (0, installation_helper_1.buildInstallationContextFromId)(installationId),
+            context
+        });
+    }
     async getEntitlementOffering(site, product) {
         try {
             const productKeys = this.getHamsProductKeys(product);
             const results = await Promise.allSettled(productKeys.map((productKey) => this.graphqlClient.query(`query getEntitlementOffering($hostNames: [String!], $product: String!) {
@@ -297,34 +404,15 @@
         }
     }
     async upgradeInstallation({ site, product, environmentKey, appId, currentVersion, majorVersionId, isCodeUpgrade, targetVersion, isLatest }) {
         const workspaceAri = await this.buildInstallationContext(product, site);
-        const query = `
-      mutation forge_cli_upgradeApplication($input: AppInstallationUpgradeInput!) {
-        upgradeApp(input: $input) {
-          success
-          taskId
-          errors {
-            message
-            extensions {
-              errorType
-              statusCode
-            }
-          }
-        }
-      }
-    `;
-        const variables = {
-            input: {
-                installationContext: workspaceAri,
-                appId,
-                environmentKey,
-                ...(majorVersionId ? { versionId: majorVersionId } : {}),
-                ...(isCodeUpgrade ? { computeOnly: isCodeUpgrade } : {}),
-                async: true
-            }
-        };
-        const { response: { upgradeApp: { success, errors, taskId } }, requestId } = await this.graphqlClient.mutate(query, variables);
+        const { success, errors, taskId, requestId } = await this.submitUpgradeMutation({
+            installationContext: workspaceAri,
+            appId,
+            environmentKey,
+            majorVersionId,
+            isCodeUpgrade
+        });
         const error = (0, cli_shared_1.getError)(errors);
         if (!success) {
             const { VERSION_DECREASES_ROLLING_RELEASE_CODE, MISSING_APP_MANAGED_PERMISSIONS_ROLLING_RELEASE_CODE } = RollingReleaseValidationErrorCodes;
             switch (error.code) {