Automation modules
Bitbucket modules
Common modules
Compass modules
Confluence modules
Jira modules
Jira Service Management modules
Rovo modules

Action (EAP)

Forge’s EAP offers experimental features to selected users for testing and feedback purposes. These features are unsupported and not recommended for use in production environments. They are also subject to change without notice. For more details, see Forge EAP, Preview, and GA.

To sign up for this EAP, submit a ticket here.

Due to performance and scalability challenges in the upcoming milestone preview, we plan a phased rollout of apps. Initially, only a select number of apps will be permitted for use in production environments and distribution through the marketplace.

Both Rovo and Automation utilize the same Forge module core:action. However, the action can be used in different contexts depending on the app's manifest configuration.

In subsequent sections, you can learn how to use the core:action module in your Forge app to extend the Automation platform.

Overview

Users can select your app action from the Automation component picker. After adding an app action to a rule, users will configure, validate, and enable it.

When the rule gets executed, your action will get executed, too. Automation will pass along the user’s configuration when invoking your action’s handler function. If you notice an invalid configuration, you can fail the action and append error messages to the Automation audit log.

If your action ran successfully, control will be passed back to Automation, and the rule will continue execution.

Manifest

1
2
modules {}
└─ automation:actionProvider []
   ├─ key (string) [Mandatory]
   └─ actions (array) [Mandatory]
└─ action []
   ├─ key (string) [Mandatory]
   ├─ name (string) [Mandatory]
   ├─ function (string) [Mandatory]
   ├─ actionVerb (string) [Mandatory]
   ├─ description (string) [Mandatory]
   ├─ config {} [Optional]
   │  └─ resource (string) [Mandatory]
   │  └─ render (default | native) [Optional]
   ├─ inputs {} [Mandatory]
   │  └─ inputName {}
   │     ├─ title (string) [Mandatory]
   │     ├─ type (string) [Mandatory]
   │     ├─ required (boolean) [Mandatory]
   │     └─ description (string) [Optional]
function []
└─ key (string) [Mandatory]
└─ handler (string) [Mandatory]
resources []
└─ key (string) [Mandatory]
└─ path (string) [Mandatory]

The actionProvider module

Automation will only show actions that are referenced by an actionProvider.

To make your actions visible in Automation, you must add them to an actionProvider module.

The action module supports both Automation and Rovo. To control where the action should be shown, you need to opt into Automation explicitly by adding your action to an actionProvider.

PropertyTypeRequiredDescription
keystringYesA key for the action provider. Must be unique within the manifest.
Regex: ^[a-zA-Z0-9_-]+$
actionsstring[]YesA list of references to action modules (defined next)

The action module

The action module allows you to configure an action.

PropertyTypeRequiredDescription
keystringYesA key for the action, which other modules can refer to. Must be unique within the manifest.
Regex: ^[a-zA-Z0-9_-]+$
namestringYesA human-friendly name for the action which will be displayed in the user interface.
function or endpointstringYesA reference to the hosted Forge function that defines the behavior of this action.
If you are using Forge remote then you can use an external API endpoint here.
actionVerbstringYesThe verb that best represents your action: GET, CREATE, UPDATE, DELETE, TRIGGER.
This property is only used by Rovo right now. Unfortunately, you still need to provide a value until we make it optional.
descriptionstringYesTextual representation of the component's configuration state.
configconfigYesForm to provide additional context during action invocation.
inputsinputs YesThe inputs for this action.

The config property

After adding an app action to a rule, users can configure it. This is done via a configuration form controlled by your app. See Action configuration below for more details.

The config property allows you to choose between UI Kit and Custom UI.

PropertyTypeRequiredDescription
resourcestringRequired if using Custom UI or the latest version of UI KitA reference to the static resources entry that your context menu app wants to display. See resources
rendernativeYes for UI KitIndicates the module uses UI Kit.

The inputs property

When your action is invoked, it receives a set of inputs, each of which is configured via the inputs property.

Each input must have a unique user-defined name, referred to as inputName, which acts as a parent container for the following properties: title, type, required, and description.

PropertyTypeRequiredDescription
titlestringYesThe name of the input.
typestringYesThe data type of the input: string, integer, number, or boolean.
requiredstringYesTrue if the input is required.
descriptionstringNoA short description of your action.

Scopes

Automation action module doesn't require any additional scopes to be added to the manifest.

Keep in mind that if your action communicates with the product API, it might need additional scopes. See Add scopes to call an Atlassian REST API for more details.

Example modules

Using a hosted Forge function

1
2
modules:
  action:
    - key: log-time
      function: logTime
      name: Fetch timesheet by date
      actionVerb: CREATE
      description: |
        Retrieve a user's timesheet based on a date
      config:
        render: native
        resource: main-resource
      inputs:
        timesheetDate:
          title: Timesheet Date
          type: string
          required: true
          description: "The date that the user wants a timesheet for"
  function:
    - key: logTime
      handler: index.logTime
    - key: summary
      handler: index.summary
resources:
  - key: main-resource
    path: src/frontend/fui.tsx

Using a Forge remote endpoint

1
2
modules:
  action:
    - key: log-time
      endpoint: logTime
      name: Log time
      actionVerb: CREATE
      description: |
        Log some time for the user against a Jira issue
      config:
        render: native
        resource: main-resource
      inputs:
        issueKey:
          title: Jira Issue Key
          type: string
          required: true
          description: "The jira issue to log time against"
        time:
          title: Time to log in minutes
          type: integer
          required: true
          description: "The number of minutes to log"
  endpoint:
    - key: logTime
      remote: timesheetapp
      route:
        path: log-time
      auth:
        appUserToken:
          enabled: true
        appSystemToken:
          enabled: false
remote: 
  - key: timesheetapp
    baseUrl: "https://backend.timesheetapp.com"

Action configuration

Users will be able to configure your action after they have added it to a rule. They do that by providing inputs via a configuration form.

That configuration form is built by you, using either UI Kit or Custom UI.

We recommend choosing UI Kit due to its simplicity and efficiency in capturing user inputs.

configuration-example

For a great user experience, it is important for the configuration form to interact properly with Automation. There are two areas to take care of:

  • Maintaining configuration state
  • Validation

Maintaining configuration state

Users should be able to make changes to your action, navigate to another one, and then get back to yours. Changes they have made should not be lost along the way.

Configuration state can be maintained in multiple ways prior to saving the rule:

  • A user submits the configuration form with the Next button
  • An app has onChange or onBlur handlers implemented per input in the configuration form that will call view.submit(payload) on each such event triggered.
  • For better state maintenance and client-side validation similar to the native automation actions we encourage the implementation of the onChange and onBlur handlers
  • Navigating away from the action configuration by clicking the Back button won’t maintain the state, it is designed to undo the dirty state of the form

Maintaining state example in UI Kit

When the user clicks the Next button during rule creation/update, the Form’s onSubmit will be triggered, allowing you to handle the form's validation and submission.

More information about the form can be found under Form component.

1
2
import { view } from '@forge/bridge';
import ForgeReconciler, {
    Form,
    useForm,
    Text,
    useProductContext,
    ErrorMessage,
    TextArea,
} from '@forge/react';

export const CommentForm = ({ context }) => {
  const formInstance = useForm({
    defaultValues: context.extension,
  });
  const { handleSubmit, register, getValues } = formInstance;

  const onSubmit = data => {
    view.submit(data);
  };

  const onChangeHandler = (value) => {
    view.submit({ ...getValues(), ...value });
  };

  const { onChange: onCommentChange, ...commentRegisterProps } = register("comment", {
    required: { value: true, message: "Comment is required" },
  });

  return (
    <Form onSubmit={handleSubmit(onSubmit)}>
      <Text>Comment</Text>
      <TextArea
        {...commentRegisterProps}
        onChange={e => {
          onCommentChange(e);
          onChangeHandler({ comment: e.target.value });
        }}
      />
      {formInstance.formState.errors.comment?.message && (
        <ErrorMessage>
            {formInstance.formState.errors.comment?.message}
        </ErrorMessage>
      )}
    </Form>
  );
}
export const App = () => {
  const context = useProductContext();

  return !context ? <Text>Loading...</Text> : <CommentForm context={context} />;
};

ForgeReconciler.render(<App />);

Maintaining state example in Custom UI

Custom UI necessitates additional setup. The code must be either bundled into your Forge app or served from an external CDN, ensuring appropriate Content Security Policy (CSP) configurations are in place.

To be able to submit the configuration form with the native Next button - a subscription to an event is required, where the event listener callback should call the view.submit.

The event is called AUTOMATION_ACTION_SUBMIT. It will notify your app that a configuration form was submitted. This event is only available for Custom ui modules.

1
2
import ReactDOM from 'react-dom/client';
import { useEffect, useRef, useState } from 'react';

import Form, { Field } from '@atlaskit/form';
import Textfield from '@atlaskit/textfield';

import { view, events } from '@forge/bridge';

export const App = () => {
  const [formData, setFormData] = useState(null);
  const formRef = useRef(null);

  useEffect(() => {
    view.getContext().then(({ extension }) => {
      setFormData(extension);
    });

    const subscription = events.on('AUTOMATION_ACTION_SUBMIT', () => {
      formRef.current?.onSubmit();
    });

    return () => {
      subscription.then((sub) => sub.unsubscribe());
    };
  }, []);

  const onChangeHandler = value => {
    const updatedFormData = { ...formData, ...value };

    view.submit(updatedFormData);
    setFormData(updatedFormData);
  };

  const onSubmit = data => {
    view.submit(data);
  };

  return (
    <Form onSubmit={onSubmit}>
      {({ formProps }) => {
        formRef.current = formProps;

        return (
          <form {...formProps}>
            <Field
              name="issueKey"
              label="Issue key"
              defaultValue={formData.issueKey}
            >
              {({ fieldProps: { onChange, ...restFieldProps } }) => (
                <Textfield
                  {...restFieldProps}
                  onChange={e => {
                    onChangeHandler({ [restFieldProps.name]: e.target.value });
                    onChange(e);
                  }}
                />
              )}
            </Field>
          </form>
        );
      }}
    </Form>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(<App />);

Validation

In order to be able to save the rule in the Automation platform, the configuration needs to pass some basic validation checks.

  • The total size of the configuration, as a JSON, should not exceed 100 kilobytes
  • The configuration should contain fields corresponding to the inputs declared in the manifest module
    • Every field type and size is validated against the input declaration
    • Fields present in the configuration but not declared as inputs in the manifest will be rejected

Accepting Smart Values

If you want to use Smart Values to pass dynamic configuration to your action, you can add a hidden input field to your form (make sure to declare it in the manifest).

Then set the value of the input field to the smart value, e.g. “{{version.name}}".

When Automation executes your action, it will pass on the resolved value.

Action execution

When Automation executes your action, it invokes the referenced Forge function (or remote endpoint if you are using Forge remote). A payload object is passed into the function as an argument. It includes the inputs defined for the action during the configuration inside the UI.

Here’s an example of a payload object, and how it can be accessed.

1
2
"payload": {
  "projectKey": "CP",
  "issueKey": "CP-16",
  "comment": "hello world"
}

The addComment function from the previous example might look like this:

1
2
export async function addComment(payload) {

  // Extract necessary information
  const projectKey = payload?.projectKey;
  const issueKey = payload?.issueKey;
  const comment = payload.comment || "This is a comment";


  // Your code goes here ...
}

Permissions: asApp() vs asUser()

Automation will always invoke your action as a particular user (currently the one configured under “Rule Actor”). Therefore, you should always use asUser() when making outbound requests. Quoting from Forge’s Shared Responsibility Model:

You must use asUser() whenever you are performing an operation on behalf of a user. This ensures your app has at most the permissions of the calling user.

In the past, we recommended the use of asApp(), because asUser() wasn’t yet available. This has now changed.

Please avoid using asApp(), whenever possible.

A note on return values

At this point, your action won’t be able to contribute any data back to Automation. Return values from your handler function will be ignored, unless you return a special errors object (see section below).

Communicating errors

If the user hasn’t provided valid inputs to your action, you can return a list of error messages. The rule will stop execution, the action will be marked as failed, and the rule owner will be notified by email. The rule owner can then navigate to the Audit Log, where they can then see the error messages you have provided.

Note: if your app throws an exception, the execution will be marked as failed too, but only a generic error message will be shown.

1
2
export async function addCommentWithUsageError(payload) {
  const issueKey = payload?.issueKey;

  if (!issueKey) {
    return {
      errors: [
        {
          message: `Payload provided with invalid issue key: ${issueKey}`,
        }
      ],
    };
  }

  // continue execution
}

Icons

An app icon will be used as an action icon visible in the automation flow configuration. If the app defines multiple actions, they will have the same icon.

An app icon can be configured in the Developer console:

app-icon

Known issues

Behaviour on app installation

When a user uninstalls your app, there might still be references to your app actions in their rules. These references will continue to exist (together with the documentation).

However, the action will be shown as an “unknown component”.

If the user chooses to reinstall the app, all the actions will appear again together with the previous configuration.

Internationalization

If your action appends error messages to Automation’s Audit log, these will be persisted in the Audit Log. There is no support for internationalization / translating these strings.

Rule actors, the A4J bot user and Connections

The default actor of a new rule is still a special “Automation for Jira” bot user. This leads to a lot of confusion, because when running asUser(), your app will run as that bot user.

That the bot user will soon disappear, though, because all Forge actions will soon require a Connection to be created (which will decide which user the action is run as).

Forge tunnel

To make forge tunnel work for Automation actions, you should change the default actor Automation for Jira of the rule to be the "Personal User Account" instead. To access this configuration, click the "Rule details" button in the Rule configuration view. After you have done that, you can run forge tunnel and the action execution logs should be visible in the terminal. Also code reloading should work as usual.

Known defects

  • Forge CLI linter may complain about the new automation module during the deployment. Use --no-verify flag to bypass it.
  • Audit log filtering by Forge actions doesn't work.

Rate this page: