Snap-in developmentTutorials

Using a snap-in to perform a DevRev action

Introduction

The objective is to build a snap-in that creates a new ticket every 10 minutes.

Background context

In the Getting Started tutorial, you learned how to create a hello-world snap-in which prints a log message when a work item is created. In this tutorial, you will make the following changes to the snap-in:

  1. Update the trigger condition to run every 10 minutes instead of work creation.
  2. Create a ticket whenever the snap-in is triggered.

Ensure that you have at least one part in your organization since every ticket must be linked to a part and have an owner.

Since a DevRev ticket is created whenever the function is triggered, DevRev APIs need to be called that allow this functionality. Although one can directly make HTTP requests to the endpoint, it’s strongly suggested to use the DevRev TypeScript SDK since it provides additional types and helper methods which ease development.

Installation guide

If you did not follow the getting started tutorial then follow these steps to authenticate and initialize the snap-in TypeScript template:

$devrev profiles authenticate -o <dev-org-slug> -u <youremail@yourdomain.com>
$devrev snap_in_version init

Trigger

The trigger condition for the snap-in is dictated by the Event Sources section in the manifest. The timer-events event source is suitable for the use-case, since it allows trigger of snap-ins using CRON expression.

Action

The hello-world snap-in prints a log message whenever the snap-in is triggered. Here, the snap-in should create a work-item of type ticket when triggered. To do that, use the DevRev TypeScript SDK to make API calls for creating the ticket.

Creating the snap-in

Updating the manifest

Update the manifest file to reflect the objective. Update the name, the description, and the service account’s display name to better reflect the snap-in’s behavior.

manifest.yaml
1version: "2"
2
3name: "Timely Ticketer"
4description: "Snap-in to create ticket every 10 minutes"
5
6service_account:
7 display_name: Automatic Ticket Creator Bot

Next, update the event_sources section to use the timer-events event source. The timer-events source type takes a config of type cron or interval_seconds as mentioned in the documentation. The cron config is used here.

manifest.yaml
1event_sources:
2 organization:
3 - name: timer-event-source
4 description: Event source that sends events every 10 minutes.
5 display_name: Timer Event Source
6 type: timer-events
7 config:
8 # CRON expression for triggering every 10 minutes.
9 cron: "*/10 * * * *"
10 metadata:
11 event_key: ten_minute_event

Finally, update the function name to better reflect the behavior and automationname to use the event type corresponding to the timer-events event source.

manifest.yaml
1functions:
2 - name: ticket_creator
3 description: Function to create a new ticket when triggered.
4
5automations:
6 - name: periodic_ticket_creator
7 description: Automation to create a ticket every 10 minutes
8 source: timer-event-source
9 event_types:
10 - timer.tick
11 function: ticket_creator

After these changes, the final version of the manifest can be found here.

Renaming the function

Next, the function name in the src/functions folder needs to be renamed to ‘ticket_creator’ which is the one put in the manifest.

src/functions/on_work_created => src/functions/ticket_creator

These changes need to be reflected in the following files as well:

  • src/function-factory.ts
  • src/test-runner/example.test.ts
  • src/functions/ticket_creator/index.test.ts

Event received by the snap-in function

In the hello-world snap-in, the ID of the created work-item gets extracted from the input event as such.

1console.info(
2 `The work ${events[0].payload.work_created.work.id} has been created.`
3);

The schema for a event received by the snap-in is:

1{
2 payload:Record<string,any>
3 context: {
4 "dev_oid": string,
5 "source_id":string,
6 "snap_in_id": string,
7 "snap_in_version_id":string,
8 "service_account_id":string,
9 "secrets": {
10 "service_account_token": [SECRET],
11 ...
12 },
13 },
14 execution_metadata: {
15 "request_id":string,
16 "function_name": string,
17 "event_type": string,
18 "devrev_endpoint": string
19 },
20 input_data: {
21 "global_values": Record<string,string>,
22 "event_sources":Record<string,string>,
23 "keyrings":Record<string,string>,
24 "resources": Record<string,string>
25 }
26}

The payload differs by the event_source and the event_type received. There are two fields to be concerned about in the input payload:

  1. devrev_endpoint the endpoint of the API call.
  2. service_account_token which is a short-lived token provisioned by each snap-in. This is required when making API calls to DevRev.

Updating the code

Update the code in src/functions/ticket_creator/index.ts to reflect the behavior.

Firstly, import the DevRev TypeScript SDK in the index.ts file

index.ts
1import {client, publicSDK} from "@devrev/typescript-sdk";

Next, update the run function in the hello-world example. Since the ticket gets created frequently, set some creation time in the title and the body. The example uses the part as PROD-1 and keeps the owner as DEVU-1. Feel free to edit those values to actual IDs for parts and users in your dev org.

The complete index.ts file looks like this

1import { client, publicSDK } from "@devrev/typescript-sdk";
2
3export const run = async (events: any[]) => {
4 for (const event of events) {
5 const endpoint = event.execution_metadata.devrev_endpoint;
6 const token = event.context.secrets.service_account_token;
7
8 // Initialize the public SDK client
9 const devrevSDK = client.setup({ endpoint, token });
10
11 // Create a ticket. Name the ticket using the current date and time.
12 const date = new Date();
13 const ticketName = `Ticket created at ${date.toLocaleString()}`;
14 const ticketBody = `This ticket was created by a snap-in at ${date.toLocaleString()}`;
15
16 const reponse = await devrevSDK.worksCreate({
17 title: ticketName,
18 body: ticketBody,
19 // The ticket is created in the PROD-1 part. Rename this to match your part.
20 applies_to_part: "PROD-1",
21 // The ticket is owned by the DEVU-1 user. Rename this to match the required user.
22 owned_by: ["DEVU-1"],
23 type: publicSDK.WorkType.Ticket,
24 });
25
26 console.log(reponse);
27 }
28};
29
30export default run;

Deploying the snap-in to your organization

Once satisfied with the code changes, move to the code folder, and run

$npm install
>
>npm run build
>
>npm run package

This builds and packages your snap-in and creates a build.tar.gz. The tar.gz and manifest.yaml file is used when creating the snap-in version.

Always remember to build and package the snap-in whenever there are code changes and it needs to be re-deployed.

Steps for deploying this snap-in have been discussed in the Getting Started section.

Resources

The final snap-in code and manifest can be found here