Dev Guide
Dev GuideUser GuideGitHubNuGetDevCommunitySubmit a ticketLog In
GitHubNuGetDevCommunitySubmit a ticket

Lifecycle

This topic describes the lifecycle of an application with Optimizely Data Platform (ODP).

The basics

The Lifecycle is how you manage all the stages of an app:

  • Install
  • Settings
  • Form submission
  • Upgrades
  • Uninstalls

App template generated by ocp app init contains a skeleton of lifecycle methods. Add any logic your application needs there.

Settings form submission

One of the core components in creating your app is interacting with the settings.yml. Handling the actions and being able to validate keys or communicate toasts back into the OCP application.

Here is a sample settings.yml and the associated onSettingsForm from the Lifecycle that handles the interactions from it.

settings.yml

sections:
  - key: auth
    label: Authenticate
    properties:
      - authConfirmed
    elements:
    - type: secret
      key: client_id
      label: Client ID
      help: Get your Client ID from Vendor
    - type: secret
      key: client_secret
      label: Client Secret
      help: Get your Client Secret from Vendor
    - type: button
      label: Authorize
      style: primary
      action: authorize #<-- action passed in
      disabled:
        key: client_id
        empty: true
  - key: config
    label: Configuration
    properties:
      - canImport
    disabled:
      key: auth.authConfirmed
      equals: false
    elements:
    - type: toggle
      key: winback
      defaultValue: true
      label: Winback
      help: Toggle to send Winback segment to Vendor (engaged, winback, churned)
    - type: toggle
      key: order_likelihood
      defaultValue: true
      label: Order Likelihood
      help: Toggle to send Order Likelihood segment to Vendor (Extremely Likely, Very Likely, Likely, Unlikely)
    - type: toggle
      key: discount_affinity
      defaultValue: true
      label: Discount Affinity
      help: Toggle to send Discount Affinity segment to Vendor (Always, Sometimes, Never)
    - type: button
      label: Save
      action: save #<-- action passed in
      style: primary
    - type: button
      label: Start Initial Import
      action: import #<-- action passed in
      disabled:
        key: canImport
        equals: false

onSettingsForm

Each time the form is submitted you can handle interactions with the form and also set variables for use in the forms for enabling new sections.

import {Vendor} from '../lib/Vendor';
  
  ...

  public async onSettingsForm(
          section: string, action: string, formData: App.SubmittedFormData
  ): Promise<App.LifecycleSettingsResult> {
    const result = new App.LifecycleSettingsResult();
    const vendor = new Vendor();
    try {
      switch (action) {
      case 'authorize': // <-- switching on the action from above
        try {
          await vendor.validateAuth(formData);
          formData.authConfirmed = true; // <-- setting a variable for use in settings.yml
          await storage.settings.put(section, formData); // <-- saving form data
          result.addToast('success', 'Authorized');
        } catch (e) {
          logger.error('issue authenticating', e);
          result.addToast('warning', 'Could not Authenticate, please validate your information is correct');
        }
        break;
      case 'save':
        try {
          await vendor.createObjects(formData);
          formData.canImport = true;
          await storage.settings.put(section, formData);
          result.addToast('success', 'Ready to Import');
        } catch (e) {
          logger.error('Issue creating objects', e);
          result.addToast('warning', 'Could not create audiences');
        }
        break;
      case 'import':
        logger.info('Starting Job');
        await App.jobs.trigger('nightly_export', {}); // <-- starting a Job
        result.addToast('info', 'Sync Started');
        break;
      }
      return result;
    } catch (e) {
      logger.error('Issue:', e);
      return result.addToast('danger', 'Sorry, an unexpected error occurred. Please try again in a moment.');
    }
  }

App installation

When an app is installed to an ODP account, the onInstall method is called.
The method should contain any logic that is required to initialize the app, for example:

  • initialization of KV storage
  • registering webhooks in third-party services
  • performing dynamic ODP schema amendments via API (declarative schema changes are performed automatically by OCP before the method is called)
  • etc.

App upgrades

When an app is upgrades to a new version, either by an auto-upgrade or a manual upgrade, OCP calls three lifecycle methods:

  • onUpgrade - this method is called after declarative schema changes are performed by OCP. It can be used to perform dynamic schema changes via API.
  • onFinalizeUpgrade - this method is called after webhook URLs are created for all new functions in the version. Use it to register webhooks in a third-party service if needed.
  • onAfterUpgrade - this method is called after the upgrade is completed. It can be used to trigger any required one-off jobs.

Keep in mind the upgrade is not necessarily from the previous most recent version.
The previous version of the app is provided in 'onUpgrade' method attribute.

Authentication

onAuthorizationRequest and onAuthorizationGrant are called as part of the authorization process initialized by
clicking on OAuth Button element type in app settings form.
Refer to authentication doc for more details.

Uninstalling an app

When an app gets uninstalled from an ODP account, OCP calls onUninstall lifecycle method.
The method should contain the logic to clean up after the app is uninstalled.
This might include invalidating authentication tokens in third-party services,
removing resources from third-party services, etc.