Establishing webhooks

Establishing and using webhooks.

This chapter demonstrates how to set up webhooks for Skedulo using the Skedulo Lens API and ngrok, which is a useful tool for locally testing and viewing webhook responses.

Prerequisites

  • You have a valid API access token and have configured this as an environment variable. This example uses $AUTH_TOKEN to represent the API authentication environment variable.
  • Node/NPM is installed.
  • (Optional) jq is installed.

This example uses .json files executed using cURL commands for readability and simpler query creation

Create a webhook

  1. Create and open a webhooks folder from the terminal:

    $ mkdir webhooks && cd webhooks
    
  2. Download and install ngrok in the webhooks folder:

    1. Go to https://ngrok.com/ and click Download.
    2. Download the version of ngrok for your operating system.
    3. Unzip ngrok into the /webhooks folder.
  3. Create a file called httpserver.js with the following configuration:

    const http = require("http");
          
    http.createServer((req, res) => {
      const body = [];
      req
        .on('data', (chunk) => {
          body.push(chunk);
        })
        .on('end', () => {
          const bodyStr = Buffer.concat(body).toString();
          const obj = {
            headers: req.headers,
            body: JSON.parse(bodyStr)
          }
          console.log(JSON.stringify(obj, null, 2))
          res.write(JSON.stringify(obj, null, 2))
          res.end()
        });
         
      res.writeHead(200, {"Content-Type": "application/json"})
    }).listen(8080)
    

    4. Start the HTTP server in one terminal: node httpserver.js

    5. Start ngrok in another terminal:

    ./ngrok http 8080
    

    Ngrok started in terminal.

    Take note of the HTTPS address of your ngrok server. For example: https://b14b2804.ngrok.io

    6. In another terminal, create a file called webhook.js. This is just so you can create your queries in a readable way. It’s not necessary if you want to just send your JSON queries using a cURL command. Add the following content to your webhook.js file, using the HTTPS ngrok server address as the url.

    const url = "https://b14b2804.ngrok.io"
    
    const json = {
    name: "test",
    url: url,
    type: "graphql",
    query: `
    subscription {
      schemaJobs {
        operation
        timestamp
        data {
          UID
          Duration
        }
        previous {
          Duration
        }
      }
    }
    `
    }
    
    console.log(JSON.stringify(json, null, 2))
    
    • One of the fields in the data block needs to change to call the webhook. In this case, this includes UID and Duration.
    • The schemaJobs field also accepts filter and extendedFilter parameters.
    • The operation parameter can be used to ignore events that do not have the operation value. The operation parameter accepts an array of values: INSERT, UPDATE or DELETE.

7. Create a webhook request body:

node webhook.js > temp.json 

8. Create a POST request to the Skedulo API that references the temp.json file containing the GraphQL subscription request:

curl -s -X POST -H "Authorization: Bearer $AUTH_TOKEN"  -H "Content-Type: application/json" -d @temp.json 'https://api.skedulo.com/webhooks' | jq

This returns the following response in the terminal:

{
  "result": {
    "id": "9849cceb-c426-488b-89dc-6ff02b33802d",
    "name": "test",
    "url": "https://b14b2804.ngrok.io",
    "headers": {},
    "query": "\n    subscription {\n      schemaJobs {\n        operation\n        timestamp\n        data {\n          UID\n          Duration\n        }\n        previous {\n          Duration\n        }\n      }\n    }\n  ",
    "customFields": {},
    "type": "graphql"
  }
}

9. Create a new job using a GraphQL query.

The following response should appear in the terminal window that is listening on port 8080:

  {
  "headers": {
    "user-agent": "Skedulo-Webhook",
    "skedulo-webhook-id": "9849cceb-c426-488b-89dc-6ff02b33802d",
    "skedulo-request-id": "317cc297-c9cb-41e5-a741-366b3724cb8e",
    "content-length": "182",
    "content-type": "application/json; charset=UTF-8",
    "accept-encoding": "gzip, deflate",
    "host": "b14b2804.ngrok.io",
    "accept": "*/*",
    "x-forwarded-proto": "https",
    "x-forwarded-for": "34.215.60.60"
  },
  "body": [
    {
      "data": {
        "schemaJobs": {
          "data": {
            "UID": "00145d1e-974a-4f97-9cd7-7cd280f824a8",
            "Duration": 60
          },
          "previous": {
            "Duration": 60
          },
          "operation": "INSERT",
          "timestamp": "2019-07-02T03:29:35.969Z"
        }
      }
    }
  ]
}

You can also view the webhook trigger request responses in the local ngrok web interface.

10. Update a job to change the Duration to 30. The response displays in the terminal:

{
  "headers": {
    "user-agent": "Skedulo-Webhook",
    "skedulo-webhook-id": "9849cceb-c426-488b-89dc-6ff02b33802d",
    "skedulo-request-id": "fdf22ac0-b6d5-4453-9bfa-9735c7e2cdde",
    "content-length": "182",
    "content-type": "application/json; charset=UTF-8",
    "accept-encoding": "gzip, deflate",
    "host": "b14b2804.ngrok.io",
    "accept": "*/*",
    "x-forwarded-proto": "https",
    "x-forwarded-for": "34.215.60.60"
  },
  "body": [
    {
      "data": {
        "schemaJobs": {
          "data": {
            "UID": "00145d1e-974a-4f97-9cd7-7cd280f824a8",
            "Duration": 30
          },
          "previous": {
            "Duration": 60
          },
          "operation": "UPDATE",
          "timestamp": "2019-07-02T03:31:27.970Z"
        }
      }
    }
  ]
}

11. A record of the HTTP request appears in the terminal tab that is running ngrok:

Ngrok operations terminal.

The ngrok web interface also shows both of the operations and responses:

Ngrok web interface.

Deferred webhooks

You can set your webhooks to only respond after a certain time and/or when a job matches the specific criteria that you want to appear in your webhook.

The following example demonstrates how to create a deferred webhook that fires 10 seconds after the job has been created. It will not fire if the job Description field is cancel or changes to cancel within 10 seconds of the job being created.

  1. Modify your webhook.js file to add the following configuration:

    const url = "https://b14b2804.ngrok.io"
    
    const jsonDeferred = {
      name: "test_deferred",
      url: url,
      type: "graphql_deferred",
      field: "CreatedDate",
      offset: 10000,
      filter: "Description != 'cancel'",
      query: `
        subscription {
          jobs {
            UID
            Name
            Description
            Duration
            Start
            End
            CreatedDate
            JobAllocations {
              UID
            }
          }
        }
      `
    }
    
    console.log(JSON.stringify(jsonDeferred, null, 2))
    • The field must be a field with an INSTANT value, such as Start, End, ActualStart, EstimatedEnd, and so on. In the case above, the webhook is being deferred on the basis of the CreatedDate value.
    • The offset is the number of milliseconds being deferred before the webhook fires. In this case, it is 10000 milliseconds. The minimum deferred time is 5000 milliseconds. The offset value can also be negative.
    • The filter field is used to filter jobs so that only relevant results are returned by the webhook after the offset period. In this case, jobs with cancel in the Description will not be returned by the webhook.

    2. Create a webhook request body:

    node webhook.js > temp.json

3. Check that the temp.json file now contains the jsonDeferred GraphQL query. The file should include the following configuration:

{
"name": "test_deferred",
"url": "https://b14b2804.ngrok.io",
"type": "graphql_deferred",
"field": "CreatedDate",
"offset": 10000,
"filter": "Description != 'cancel'",
"query": "\n      subscription {\n        jobs {\n          UID\n          Name\n          Description\n          Duration\n          Start\n          End\n          CreatedDate\n          JobAllocations {\n            UID\n          }\n        }\n      }\n    "
}

4. Create the deferred webhook using the cURL command:

curl -s -X POST -H "Authorization: Bearer $AUTH_TOKEN"  -H "Content-Type: application/json" -d @temp.json 'https://dev-api.test.skl.io/webhooks' | jq

This returns the following response confirming that the webhook is established, including the webhook id:

{
  "result": {
    "id": "6b6d32e0-ad1f-45fa-9f03-b5264ca02ecb",
    "name": "test_deferred",
    "url": "https://b14b2804.ngrok.io",
    "headers": {},
    "schemaName": "Jobs",
    "fieldName": "CreatedDate",
    "offset": 10000,
    "filter": "Description != 'cancel'",
    "query": "\n      subscription {\n        jobs {\n          UID\n          Name\n          Description\n          Duration\n          Start\n          End\n          CreatedDate\n          JobAllocations {\n            UID\n          }\n        }\n      }\n    ",
    "customFields": {},
    "type": "graphql_deferred"
  }
}

5. Create a new job using GraphQL to confirm that the webhook has been created with a deferred offset of 10000 milliseconds (10 seconds).

If you still have the test webhook running, you will receive two responses for this job in the terminal that is listening on port 8080. The first response is from the test webhook, which fires the response immediately when the job is created. The second response is sent 10 seconds later from the test_deferred webhook. Both responses are identical except for the skedulo-webhook-id and skedulo-request-id numbers that the webhook that sent the request.

6. Confirm that the webhook filters jobs with cancel in the Description field. Create a new job or update an existing job to have the value cancel in the subscription field.

The deferred webhook will not show a response as jobs with cancel in the Description field are being ignored.

If your test webhook is still active, you will still receive an immediate response from that webhook.

Retrieve webhooks

You can retrieve webhooks that are currently set up for your tenancy using the following cURL command:

curl -s -X GET -H "Authorization: Bearer $AUTH_TOKEN" https://api.skedulo.com/webhooks

Because I still have both the test and test_deferred webhooks active, the response returns both webhooks:

{
  "result": [
    {
      "id": "9849cceb-c426-488b-89dc-6ff02b33802d",
      "name": "test",
      "url": "https://b14b2804.ngrok.io",
      "headers": {},
      "query": "\n    subscription {\n      schemaJobs {\n        operation\n        timestamp\n        data {\n          UID\n          Duration\n        }\n        previous {\n          Duration\n        }\n      }\n    }\n  ",
      "customFields": {},
      "type": "graphql"
    },
    {
      "id": "6b6d32e0-ad1f-45fa-9f03-b5264ca02ecb",
      "name": "test_deferred",
      "url": "https://b14b2804.ngrok.io",
      "headers": {},
      "schemaName": "Jobs",
      "fieldName": "CreatedDate",
      "offset": 10000,
      "filter": "Description != 'cancel'",
      "query": "\n      subscription {\n        jobs {\n          UID\n          Name\n          Description\n          Duration\n          Start\n          End\n          CreatedDate\n          JobAllocations {\n            UID\n          }\n        }\n      }\n    ",
      "customFields": {},
      "type": "graphql_deferred"
    }
  ]
}

Delete a webhook

To delete your webhook when you no longer require the connection, you can delete the webhook using the following cURL command with the webhook id.

curl -s -X DELETE -H "Authorization: Bearer $AUTH_TOKEN" 'https://api.skedulo.com/webhooks/a0cd6a80-8a9d-4ed5-92f6-9c0e03b6281e'

Last modified August 1, 2019: updated to end of connected pages (8161217)