Skip to main content
Version: 13.x (Current)

Timer Service Usage

The Timer Service is very easy to use.

It provides REST APIs to interact with it, or:

  • /schedule a new expiration event (a timer)

  • /abort an existing expiration

Moreover, it provides a Swagger with the REST APIs details.

Create a timer

With this API you can schedule an expiration that, when expires, triggers a consequent action.

To schedule a new timer, the client needs to do a POST call to the /schedule API by passing a specific JSON file, or:

{
"payload": {
"myCustomPayload": {
"key1": "value1"
}
},
"startDate": "2020-04-06T08:57:04.650Z",
"expirationIntervalMs": 600000,
"applicantService": "myCustomMicroservice",
"outputMode": {
"type": "kafka",
"topics": [
"topic1",
"topic2"
],
"key": "kafkaMessageKey",
"headers": {
"header1": "value1"
}
}
}

The result of this call will be the expirationId, or, the ObjectId of the JSON saved on the dedicated CRUD; following an example of response:

{
"expirationId": "1234567someId"
}

Following the fields description:

  • payload: an Object that contains the data that will be send when the timer expires (it can be an empty JSON); just the content of the field will be send, without the payload keyword; it will be:
    • body if the outputMode is of type rest
    • message value if the outputMode is of type kafka
  • startDate: a String of datetime_format that contains the date of start of the timer; this date will be used to calculate theexpirationDate
  • expirationIntervalMs: a Number that contains the milliseconds to sum at the startDate to calculate the expirationDate
  • applicantService: a String that represents the service who sets the timer
  • outputMode: an Object with the output modality; see the dedicated section

The created expiration, on the CRUD, will have two more fields added by the service, or:

  • expirationDate: an UTC Date calculated by startDate and expirationIntervalMs
  • expirationStatus: an Object with that represents the current status of the timer, or:
    • id: the id of the state, one of the following:
      • 0 the timer is pending (not expired)
      • 1 the timer expired and the consequent action was made
      • 2 the timer is aborted
      • 3 the timer is in error state
    • description: a simple description of the state

On the CRUD, the created expiration will be like the following one (plus the CRUD fields):

{
"payload": {
"myCustomPayload": {
"key1": "value1"
}
},
"startDate": "2020-04-06T08:57:04.650Z",
"expirationIntervalMs": 600000,
"applicantService": "myCustomMicroservice",
"outputMode": {
"type": "kafka",
"topics": [
"topic1",
"topic2"
],
"key": "kafkaMessageKey",
"headers": {
"header1": "value1"
}
},
"expirationDate": "2020-04-06T09:07:04.650Z",
"expirationStatus": {
"id": 0,
"description": "pending"
}
}

Output Mode

The output mode is very important because contains the type of the output that will be trigger when the timer expires.

Right now, there are just two types of outputs:

  • rest when the timer expires, a REST API will be call
  • kafka when the timer expires, a kafka message will be send (NB. the service must have the kafka configurations to accept the kafka output mode)

For each of this output modes, there is a specific schema, or:

  • rest schema:

    • type: it must be rest

    • method: the REST Verb to use; now it can be one of the following: post, put or patch

    • protocol: the protocol to use; it can be one of the following: http or https

    • hostname: the hostname to call; e.g. www.my-service.com

    • path: the path to call; e.g. /notify-expiration

    • headers: an object with the headers (optional field)

      {
      "type": "rest",
      "method": "post",
      "protocol": "http",
      "hostname": "www.my-service.com",
      "path": "/notify-expiration",
      "headers": {
      "header1": "value1"
      }
      }
  • kafka schema:

    • type: it must be kafka

    • topics: an Array of strings that contains the topics list; the same message will be publish on each topic of the list

    • key: the kafka key to use for the message to publish

    • headers: an object with the headers (optional field)

      {
      "type": "kafka",
      "topics": [
      "topic1",
      "topic2"
      ],
      "key": "kafkaMessageKey",
      "headers": {
      "header1": "value1"
      }
      }

Schedule examples

Following two examples of a curl to schedule a timer with the two output modes:

  • rest:

    curl --location --request POST 'http://timer-service/schedule' \
    --header 'Content-Type: application/json' \
    --data-raw '{
    "payload": {
    "myCustomPayload": {
    "key1": "value1"
    }
    },
    "startDate": "2020-04-06T08:57:04.650Z",
    "expirationIntervalMs": 600000,
    "applicantService": "myCustomMicroservice",
    "outputMode": {
    "type": "rest",
    "method": "post",
    "protocol": "http",
    "hostname": "www.my-service.com",
    "path": "/notify-expiration",
    "headers": {
    "header1": "value1"
    }
    }
    }'
  • kafka:

    curl --location --request POST 'http://timer-service/schedule' \
    --header 'Content-Type: application/json' \
    --data-raw '{
    "payload": {
    "myCustomPayload": {
    "key1": "value1"
    }
    },
    "startDate": "2020-04-06T08:57:04.650Z",
    "expirationIntervalMs": 600000,
    "applicantService": "myCustomMicroservice",
    "outputMode": {
    "type": "kafka",
    "topics": [
    "topic1",
    "topic2"
    ],
    "key": "kafkaMessageKey",
    "headers": {
    "header1": "value1"
    }
    }
    }'

Abort a timer

To abort an existing timer the client needs to do a POST call to the /abort route, by passing the expirationId, returned by the /schedule API.

The expirationId must be pass into the body of the call, like the following schema:

{
"expirationId": "1234567someId"
}

The happy response will be a simple 204 No Content response.

Abort example

Following the example of a curl to abort a timer:

curl --location --request POST 'http://timer-service/abort' \
--header 'Content-Type: application/json' \
--data-raw '{
"expirationId": "1234567someId"
}'

Usage examples

Following some usage example.

Timer that expires after 10 minutes and does a REST call

A client needs, after 10 minutes, to send the payload

{
"user": {
"name": "John",
"surname": "Smith"
},
"_id": "1234567someId",
"orderId": "abc123def456"
}

using a POST call to the REST API https://user-orders:3000/order-expired.

This can be done by scheduling a timer by calling the POST route timer-service/schedule with the following body:

{
"payload": {
"user": {
"name": "John",
"surname": "Smith"
},
"_id": "098abc765def",
"orderId": "abc123def456"
},
"startDate": "2020-04-06T08:57:04.650Z",
"expirationIntervalMs": 600000,
"applicantService": "user-orders",
"outputMode": {
"type": "rest",
"method": "post",
"protocol": "https",
"hostname": "user-orders",
"port": 3000,
"path": "/order-expired"
}
}

and the result will be:

{
"expirationId": "1234567abcdef"
}

After 10 minutes, the indicated route https://user-orders:3000/order-expired will receive the payload and the timer status on the CRUD will be the following:

{
...
...
"expirationStatus": {
"id": 1,
"description": "expired"
}
}

Timer that expires after 10 minutes but is aborted before the expiration

Like the example above a client can schedule a timer to send a payload to a REST API after 10 minutes by calling the POST route timer-service/schedule with the following body:

{
"payload": {
"user": {
"name": "John",
"surname": "Smith"
},
"_id": "098abc765def",
"orderId": "abc123def456"
},
"startDate": "2020-04-06T08:57:04.650Z",
"expirationIntervalMs": 600000,
"applicantService": "user-orders",
"outputMode": {
"type": "rest",
"method": "post",
"protocol": "https",
"hostname": "user-orders",
"port": 3000,
"path": "/order-expired"
}
}

and the result will be:

{
"expirationId": "1234567abcdef"
}

After 5 minutes an event occurs and the client doesn't need the timer anymore, so the client should abort the timer.

To do this, the client can do a POST call to the timer-service/abort route with the following body:

{
"expirationId": "1234567abcdef"
}

and the result will be a 204 No Content, that means the successful timer abortion.

After the abortion, the timer status on the CRUD will be:

{
...
...
"expirationStatus": {
"id": 2,
"description": "aborted"
}
}

Timer that expires after 5 minutes and send a payload on kafka

A client needs, after 5 minutes, to send the following payload:

{
"eventName": "timerExpired",
"eventContent": {
"orderId": "abc123def456"
}
}

on the kafka topics expiredOrders and usersOrders, with the userId into the headers and using userId_orderId as message key.

This can be done by scheduling a timer by calling the POST route timer-service/schedule with the following body:

{
"payload": {
"eventName": "timerExpired",
"eventContent": {
"orderId": "abc123def456"
}
},
"startDate": "2020-04-06T08:57:04.650Z",
"expirationIntervalMs": 300000,
"applicantService": "user-orders",
"outputMode": {
"type": "kafka",
"topics": ["expiredOrders", "usersOrders"],
"key": "098abc765def_abc123def456",
"headers": {
"userId": "098abc765def"
}
}
}

and the result will be:

{
"expirationId": "1234567abcdef"
}

After 5 minutes the timer will expire and the topics expiredOrders and usersOrders will receive a message with data indicates above.

Timer with kafka output but error because kafka is not configured

As described into the environment variables section, the kafka configuration is optional (not all projects use kafka).

If the Timer Service has not kafka configured and a client schedules a timer with kafka output by calling the POST route timer-service/schedule with the following body:

{
"payload": {
"eventName": "timerExpired",
"eventContent": {
"orderId": "abc123def456"
}
},
"startDate": "2020-04-06T08:57:04.650Z",
"expirationIntervalMs": 300000,
"applicantService": "user-orders",
"outputMode": {
"type": "kafka",
"topics": ["expiredOrders", "usersOrders"],
"key": "098abc765def_abc123def456",
"headers": {
"userId": "098abc765def"
}
}
}

the timer will expire, the service will not find the kafka publisher and the timer will be set to error on CRUD:

{
...
...
"expirationStatus": {
"id": 3,
"description": "error"
}
}

Postman collection

You can use the following Postman collection to call the Timer Service REST APIs:

  {
"info": {
"_postman_id": "86e237e6-023a-4a1d-99be-e781665dfc7c",
"name": "Timer Service",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [{
"name": "Schedule a 5 minutes timer with kafka outputMode with headers",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"payload\": {\n \"eventName\": \"timerExpired\",\n \"eventContent\": {\n \"orderId\": \"abc123def456\"\n }\n },\n \"startDate\": \"2020-04-06T08:57:04.650Z\",\n \"expirationIntervalMs\": 300000,\n \"applicantService\": \"user-orders\",\n \"outputMode\": {\n \"type\": \"kafka\",\n \"topics\": [\n \"expiredOrders\",\n \"usersOrders\"\n ],\n \"key\": \"098abc765def_abc123def456\",\n \"headers\": {\n \"userId\": \"098abc765def\"\n }\n }\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://timer-service/schedule",
"protocol": "http",
"host": [
"timer-service"
],
"path": [
"schedule"
]
},
"description": "API to schedule a new expiration on the timer service"
},
"response": []
},
{
"name": "Schedule a 10 minutes timer with rest outputMode without headers",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"payload\": {\n \"user\": {\n \"name\": \"John\",\n \"surname\": \"Smith\"\n },\n \"_id\": \"098abc765def\",\n \"orderId\": \"abc123def456\"\n },\n \"startDate\": \"2020-04-06T08:57:04.650Z\",\n \"expirationIntervalMs\": 600000,\n \"applicantService\": \"user-orders\",\n \"outputMode\": {\n \"type\": \"rest\",\n \"method\": \"post\",\n \"protocol\": \"https\",\n \"hostname\": \"user-orders\",\n \"port\": 3000,\n \"path\": \"/order-expired\"\n }\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://timer-service/schedule",
"protocol": "http",
"host": [
"timer-service"
],
"path": [
"schedule"
]
},
"description": "API to schedule a new expiration on the timer service"
},
"response": []
},
{
"name": "Abort a timer",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"expirationId\": \"1234567someId\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://timer-service/abort",
"protocol": "http",
"host": [
"timer-service"
],
"path": [
"abort"
]
},
"description": "Route to call the abort a timer"
},
"response": []
}
],
"protocolProfileBehavior": {}
}

Obviously you must change the baseUrl and add your possibles custom headers/data to the calls.