Add automation scripts

Use postDeploy and preUndeploy scripts to automate configurations for Composable Commerce and external systems.

Ask about this Page
Copy for LLM
View as Markdown
It is common for any integration to require the creation of specific resources during the deployment lifecycle in the Composable Commerce API, such as Types, API Extensions, or Subscriptions.
You can define scripts in connect.yaml that run on different deployment stages, which allows Connect to create and modify those resources.

JavaScript/TypeScript

postDeploy

postDeploy runs after a successful deployment of the Connect application and is useful when you need to set up an API Extension or Subscription required to trigger your Connector endpoint.
postDeploy has access to extra environment variables depending on the application type.
Environment variableApplication typeDescription
CONNECT_SERVICE_URLServiceThe public URL of the Connect application, should be used when setting up an API Extension in the Composable Commerce API.
CONNECT_SUBSCRIPTION_DESTINATIONEventThe subscription destination type for the Connect Deployment. Should be used when setting up a Subscription in the Composable Commerce API. Supported values are SNS and GoogleCloudPubSub.
CONNECT_GCP_PROJECT_IDEventGoogle Cloud Platform (GCP) project ID. Should be used when setting up a Subscription in the Composable Commerce API.
CONNECT_GCP_TOPIC_NAMEEventGCP Pub/Sub topic name. Should be used when setting up a Subscription in the Composable Commerce API .
CONNECT_AWS_TOPIC_ARNEventAWS SNS topic ARN. Should be used when setting up a Subscription in the Composable Commerce API.

preUndeploy

preUndeploy runs before the Connector is undeployed and is useful when deleting unused resources from the Composable Commerce API.

Example service application

# connect.yaml
# ...
applicationType: service
scripts:
  postDeploy: node post-deploy.js
  preUndeploy: node pre-undeploy.js
# ...
// post-deploy.js
async function run() {
  try {
    const EXTENSION_KEY = 'myconnector-extension-key';
    const serviceUrl = process.env.CONNECT_SERVICE_URL;
    // ...
    const result = await apiRoot
      .extensions()
      .post({
        body: {
          key: EXTENSION_KEY,
          destination: {
            type: 'HTTP',
            url: serviceUrl,
          },
          triggers: [
            {
              resourceTypeId: 'cart',
              actions: ['Update'],
            },
          ],
        },
      })
      .execute();
  } catch (error) {
    process.stderr.write(`Post-deploy failed: ${error.message}\n`);
    process.exitCode = 1;
  }
}

run();
// pre-undeploy.js
async function run() {
  try {
    const EXTENSION_KEY = 'myconnector-extension-key';
    const serviceUrl = process.env.CONNECT_SERVICE_URL;
    // ...
    const result = await apiRoot
      .extensions()
      .delete({
        body: {
          key: EXTENSION_KEY,
        },
      })
      .execute();
  } catch (error) {
    process.stderr.write(`Post-deploy failed: ${error.message}\n`);
    process.exitCode = 1;
  }
}

run();

Example event application

# connect.yaml
# ...
applicationType: event
scripts:
  postDeploy: node post-deploy.js
  preUndeploy: node pre-undeploy.js
# ...
// post-deploy.js
async function run() {
  try {
    await createSubscription();
  } catch (error) {
    process.stderr.write(`Post-deploy failed: ${error.message}\n`);
    process.exitCode = 1;
  }
}

async function createSubscription() {
  const SUBSCRIPTION_KEY = 'myconnector-subscription-key';
  const subscriptionDestination = process.env.CONNECT_SUBSCRIPTION_DESTINATION;

  // Check if subscription already exists
  const {
    body: { results: subscriptions },
  } = await apiRoot
    .subscriptions()
    .get({
      queryArgs: {
        where: `key = "${SUBSCRIPTION_KEY}"`,
      },
    })
    .execute();

  // Delete existing subscription if found
  if (subscriptions.length > 0) {
    const subscription = subscriptions[0];
    await apiRoot
      .subscriptions()
      .withKey({ key: SUBSCRIPTION_KEY })
      .delete({
        queryArgs: {
          version: subscription.version,
        },
      })
      .execute();
  }

  // Google Cloud Pub/Sub subscription
  if (
    subscriptionDestination === null ||
    subscriptionDestination === 'GoogleCloudPubSub'
  ) {
    const topicName = process.env.CONNECT_GCP_TOPIC_NAME;
    const projectId = process.env.CONNECT_GCP_PROJECT_ID;

    await apiRoot
      .subscriptions()
      .post({
        body: {
          key: SUBSCRIPTION_KEY,
          destination: {
            type: 'GoogleCloudPubSub',
            topic: topicName,
            projectId,
          },
          messages: [
            {
              resourceTypeId: 'customer',
              types: ['CustomerCreated'],
            },
          ],
        },
      })
      .execute();

    return;
  }

  // AWS SNS subscription
  if (subscriptionDestination === 'SNS') {
    const topicArn = process.env.CONNECT_AWS_TOPIC_ARN;

    await apiRoot
      .subscriptions()
      .post({
        body: {
          key: SUBSCRIPTION_KEY,
          destination: {
            type: 'SNS',
            topicArn,
            authenticationMode: 'IAM',
          },
          messages: [
            {
              resourceTypeId: 'customer',
              types: ['CustomerCreated'],
            },
          ],
        },
      })
      .execute();

    return;
  }

  throw new Error(
    `Unknown subscription destination type: ${subscriptionDestination}`
  );
}

run();
// pre-undeploy.js
async function run() {
  try {
    const SUBSCRIPTION_KEY = 'myconnector-subscription-key';
    // ...
    const result = await apiRoot
      .subscriptions()
      .delete({ body: { key: SUBSCRIPTION_KEY } })
      .execute();
  } catch (error) {
    process.stderr.write(`Post-deploy failed: ${error.message}\n`);
    process.exitCode = 1;
  }
}

run();