Skip to content

API Documentation

Miro Kubicek edited this page Jan 23, 2021 · 36 revisions

Rest API Documentation

Job API
Start Job
Start Job via a Webhook (Simplified API)
Suspend a running workflow job
Repository API
Get all job definitions
List head revisions' numbers
List all revisions
Get a job definition
Get specific revision of a job definition
Create/update job definition

Job API

Start Job

Starts a new job in an asynchronous mode and immediately returns job id. Can be also run in synchronous mode using :sync request attribute.

Request

Request method Content type Syntax
POST application/json
application/transit+json
application/transit+msgpack
/systems/{system id}/jobs

Request Attributes

Request Attribute Type Use
:jobdef-name String job definition name
:revision String job definition revision
:jobdef map Alternatively, an anonymous job definition can be provided instead of job definition name
:properties map Overwrite values of job properties
:files map Files to be stored into job folder before the job is started. The format of the attribute is {"file name" #bytes}; in json format the format is a base64 encoded string.
:sync Boolean Should block till the job is finished? Defaults to false.

Example Request Body (JSON)

{"jobdef-name":"test","sync":true,"properties":{"name":"World"},"files":{"testfile.txt":"aGVsbG8gaGVsbG8gdHVybiB5b3VyIHJhZGlvIG9u"}}

Response

Returns job id of the newly created job.

Success Return Code
201

Example Response - Decoded Transit

{:jobid "517823b5-ecee-4326-ad5e-f8e03d6214c5"}

Example Response for Synchronous Execution - Decoded Transit

{:properties {:name "World", :message "Hello World!"},
        :step-start #inst"2018-10-19T07:16:59.562-00:00",
        :step-retries {},
        :tracking-id nil,
        :jobdef {:first-step "step1",
                 :name "test",
                 :type nil,
                 :properties {:name "World"},
                 :steps [{:id "step1",
                          :type :custom,
                          :supertype :tasklet,
                          :next [[false "step2"] [true "step3"]],
                          :workload-fn #titanoboa.exp.Expression{:value "(fn [p] 
                                                                         {:message (str \"Hello \" (:name p)) :return-code (nil? (:name p))})",
                                                                 :type nil},
                          :properties {}}
                         {:id "step2",
                          :type :custom,
                          :supertype :tasklet,
                          :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"!\")})",
                                                                 :type nil},
                          :next [],
                          :properties {}}
                         {:id "step3",
                          :type :custom,
                          :supertype :tasklet,
                          :workload-fn #titanoboa.exp.Expression{:value "(fn [p]
                                                                         {:message (str (:message p) \"nobody!\")})",
                                                                 :type nil},
                          :next [],
                          :properties {}}],
                 :revision 2},
        :commands-ch nil,
        :step-state :completed,
        :jobdir "/home/miro/Dropbox/titanoboa/dev-resources/job-folders/91230d16-4d51-4f78-b3bc-c6486fd6b8fb",
        :start #inst"2018-10-19T07:16:59.553-00:00",
        :history [{:step-state :completed,
                   :start #inst"2018-10-19T07:16:59.554-00:00",
                   :duration 2,
                   :result false,
                   :node-id "127.0.1.1:3000",
                   :id "step1",
                   :next-step "step2",
                   :exception nil,
                   :end #inst"2018-10-19T07:16:59.556-00:00",
                   :retry-count nil,
                   :thread-stack nil,
                   :message "Step [step1] finshed with result [false] of type class java.lang.Boolean\n"}
                  {:id "step2",
                   :step-state :running,
                   :start #inst"2018-10-19T07:16:59.562-00:00",
                   :node-id "127.0.1.1:3000",
                   :retry-count 0}
                  {:step-state :completed,
                   :start #inst"2018-10-19T07:16:59.562-00:00",
                   :duration 2,
                   :result nil,
                   :node-id "127.0.1.1:3000",
                   :id "step2",
                   :next-step nil,
                   :exception nil,
                   :end #inst"2018-10-19T07:16:59.564-00:00",
                   :retry-count nil,
                   :thread-stack nil,
                   :message "Step [step2] finshed with result [] of type \n"}],
        :duration 11,
        :state :finished,
        :map-steps nil,
        :aggregator-notif-ch nil,
        :jobid "91230d16-4d51-4f78-b3bc-c6486fd6b8fb",
        :callback-ch "clojure.core.async.impl.channels.ManyToManyChannel@358ec3bb",
        :create-folder? true,
        :node-id "127.0.1.1:3000",
        :next-step nil,
        :end #inst"2018-10-19T07:16:59.564-00:00",
        :step {:id "step2",
               :type :custom,
               :supertype :tasklet,
               :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"!\")})", :type nil},
               :next [],
               :properties {}}}

Example Response for Synchronous Execution - JSON

{
    "properties": {
        "name": "Miro",
        "message": "Hello Miro!"
    },
    "step-start": "2018-10-19T12:18:34Z",
    "step-retries": {},
    "tracking-id": null,
    "jobdef": {
        "first-step": "step1",
        "name": "test",
        "type": null,
        "properties": {
            "name": "World"
        },
        "steps": [
            {
                "id": "step1",
                "type": "custom",
                "supertype": "tasklet",
                "next": [
                    [
                        false,
                        "step2"
                    ],
                    [
                        true,
                        "step3"
                    ]
                ],
                "workload-fn": {
                    "value": "(fn [p] \n  {:message (str \"Hello \" (:name p)) :return-code (nil? (:name p))})",
                    "type": null
                },
                "properties": {}
            },
            {
                "id": "step2",
                "type": "custom",
                "supertype": "tasklet",
                "workload-fn": {
                    "value": "(fn [p]\n  {:message (str (:message p) \"!\")})",
                    "type": null
                },
                "next": [],
                "properties": {}
            },
            {
                "id": "step3",
                "type": "custom",
                "supertype": "tasklet",
                "workload-fn": {
                    "value": "(fn [p]\n  {:message (str (:message p) \"nobody!\")})",
                    "type": null
                },
                "next": [],
                "properties": {}
            }
        ],
        "revision": 2
    },
    "commands-ch": null,
    "step-state": "completed",
    "jobdir": "/home/miro/Dropbox/titanoboa/dev-resources/job-folders/4b645958-d48a-4feb-813a-8a323064d93e",
    "start": "2018-10-19T12:18:34Z",
    "history": [
        {
            "step-state": "completed",
            "start": "2018-10-19T12:18:34Z",
            "duration": 2,
            "result": false,
            "node-id": "127.0.1.1:3000",
            "id": "step1",
            "next-step": "step2",
            "exception": null,
            "end": "2018-10-19T12:18:34Z",
            "retry-count": null,
            "thread-stack": null,
            "message": "Step [step1] finshed with result [false] of type class java.lang.Boolean\n"
        },
        {
            "id": "step2",
            "step-state": "running",
            "start": "2018-10-19T12:18:34Z",
            "node-id": "127.0.1.1:3000",
            "retry-count": 0
        },
        {
            "step-state": "completed",
            "start": "2018-10-19T12:18:34Z",
            "duration": 2,
            "result": null,
            "node-id": "127.0.1.1:3000",
            "id": "step2",
            "next-step": null,
            "exception": null,
            "end": "2018-10-19T12:18:34Z",
            "retry-count": null,
            "thread-stack": null,
            "message": "Step [step2] finshed with result [] of type \n"
        }
    ],
    "duration": 5,
    "state": "finished",
    "map-steps": null,
    "aggregator-notif-ch": null,
    "jobid": "4b645958-d48a-4feb-813a-8a323064d93e",
    "callback-ch": "clojure.core.async.impl.channels.ManyToManyChannel@57b84b6e",
    "create-folder?": true,
    "node-id": "127.0.1.1:3000",
    "next-step": null,
    "end": "2018-10-19T12:18:34Z",
    "step": {
        "id": "step2",
        "type": "custom",
        "supertype": "tasklet",
        "workload-fn": {
            "value": "(fn [p]\n  {:message (str (:message p) \"!\")})",
            "type": null
        },
        "next": [],
        "properties": {}
    }
}

Start Job via a Webhook (simplified api)

Starts a new job in an asynchronous mode and immediately returns job id. Job definition name is specified in the URL and there is no required schema or a structure for the request body. The whole request body will be automatically transformed into a properties map (EDN) dynamically. If a revision number is not provided in the URL the latest revision is automatically used.

Note: Each Webhook can be separately secured via Security Extensions

Request

Request method Content type Syntax
POST application/json
application/transit+json
application/transit+msgpack
/systems/{system id}/jobs/{jobdef-name}
POST application/json
application/transit+json
application/transit+msgpack
/systems/{system id}/jobs/{jobdef-name}/{revision}

Request Attributes

Request Attribute Type Use
any any the whole request body will be automatically transformed into a properties map

Suspend a running workflow job

The workflow job will be suspended as soon as possible - usually at the start of its next step. If multiple job threads are running (via fork/join) the suspension signal will be sent to all of these, regardless of physical node they are being executed on.

If a particular step is stuck on I/O the way to expedite its suspension would be to stop the worker it is being executed on. This way there is no need to wait for the current step to finish.

For suspension to take effect the job has to contain property :suspendable? set to true, either on the job or job's properties level: https://github.com/mikub/titanoboa/wiki/Designing-Workflows#suspendable

Suspended job will be placed on suspended channel defined under :suspended-ch property - common best practice is to use the same queue as for archival. Suspended jobs will be thus archived and can be resumed via archive API.

Request

Request method Content type Syntax
PATCH application/transit+json
application/transit+msgpack
/systems/{system id}/jobs/{jobid}

Request Attributes

Request Attribute Type Use
:command string or key To suspend a job, the command should be one of following: :pause "pause" :suspend "suspend"

Response

This operation is asynchronous and returns 201 as soon as the suspension signal is sent to the job, regardless of the result.

Success Return Code
201

Repository API

Get all job definitions

Get latest revision of all job definitions in the repository.

Request

Request method Content type Syntax
GET application/transit+json
application/transit+msgpack
/repo/jobdefinitions

Response

Returns a map of all job definitions that exist in the repository. For each job definition, only the latest (head) revision is returned.

Example Response - Decoded Transit

{"test" {:first-step "step1",
                :name "test",
                :type nil,
                :properties {:name "World"},
                :steps [{:id "step1",
                         :type :custom,
                         :supertype :tasklet,
                         :next [[false "step2"] [true "step3"]],
                         :workload-fn #titanoboa.exp.Expression{:value "(fn [p] 
                                                                        {:message (str \"Hello \" (:name p)) :return-code (nil? (:name p))})",
                                                                :type nil},
                         :properties {}}
                        {:id "step2",
                         :type :custom,
                         :supertype :tasklet,
                         :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"!\")})",
                                                                :type nil},
                         :next [],
                         :properties {}}
                        {:id "step3",
                         :type :custom,
                         :supertype :tasklet,
                         :workload-fn #titanoboa.exp.Expression{:value "(fn [p]
                                                                        {:message (str (:message p) \"nobody!\")})",
                                                                :type nil},
                         :next [],
                         :properties {}}],
                :revision 2}}

List head revisions' numbers

Request

Request method Content type Syntax
GET application/transit+json
application/transit+msgpack
/repo/jobdefinitions/heads

Response

Returns a map of the latest revision number of each job definition that exists in the repository.

Example Response - Decoded Transit

{"test" 2}

List all revisions

Request

Request method Content type Syntax
GET application/transit+json
application/transit+msgpack
repo/jobdefinitions/revisions

Response

Returns a map of job definitions. For each job definition name as key, the map contains their respective list of revisions which in turn consists of a vector of revision number, its timestamp, author and revision notes.

Example Response - Decoded Transit

{"test" ([2 #inst"2018-10-03T10:28:27.000-00:00" "miro" "dolor sit amet, consectetur adipiscing elit"]
         [1 #inst"2018-10-03T10:28:26.000-00:00" "anonymous" "lorem ipsum"])}

Get a job definition

Request

Request method Content type Syntax
GET application/transit+json
application/transit+msgpack
/repo/jobdefinitions/{job definition's name}

Response

Returns a head revision of given job definitions.

Example Response - Decoded Transit

{:first-step "step1",
        :name "test",
        :type nil,
        :properties {:name "World"},
        :steps [{:id "step1",
                 :type :custom,
                 :supertype :tasklet,
                 :next [[false "step2"] [true "step3"]],
                 :workload-fn #titanoboa.exp.Expression{:value "(fn [p] 
                                                                {:message (str \"Hello \" (:name p)) :return-code (nil? (:name p))})",
                                                        :type nil},
                 :properties {}}
                {:id "step2",
                 :type :custom,
                 :supertype :tasklet,
                 :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"!\")})",
                                                        :type nil},
                 :next [],
                 :properties {}}
                {:id "step3",
                 :type :custom,
                 :supertype :tasklet,
                 :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"nobody!\")})",
                                                        :type nil},
                 :next [],
                 :properties {}}],
        :revision 2}

Get specific revision of a job definition

Request

Request method Content type Syntax
GET application/transit+json
application/transit+msgpack
/repo/jobdefinitions/{job definition's name}/{revision}

Response

Returns the specified revision of given job definitions.

Example Response - Decoded Transit

{:first-step "step1",
        :name "test",
        :type nil,
        :properties {:name "World"},
        :steps [{:id "step1",
                 :type :custom,
                 :supertype :tasklet,
                 :next [[false "step2"] [true "step3"]],
                 :workload-fn #titanoboa.exp.Expression{:value "(fn [p] 
                                                                {:message (str \"Hello \" (:name p)) :return-code (nil? (:name p))})",
                                                        :type nil},
                 :properties {}}
                {:id "step2",
                 :type :custom,
                 :supertype :tasklet,
                 :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"!\")})",
                                                        :type nil},
                 :next [],
                 :properties {}}
                {:id "step3",
                 :type :custom,
                 :supertype :tasklet,
                 :workload-fn #titanoboa.exp.Expression{:value "(fn [p]\n{:message (str (:message p) \"nobody!\")})",
                                                        :type nil},
                 :next [],
                 :properties {}}],
        :revision 1}

Create/update job definition

Adds a new revision of a job definition. If the job definition does not exists yet, it will be created.

Request

Request method Content type Syntax
POST application/transit+json
application/transit+msgpack
/repo/jobdefinitions/{job definition's name}

Request Attributes

Request Attribute Type Use
:definition map job definition map
:notes String revision notes

Response

Returns number of new revision that was created.

Example Response - Decoded Transit

3