Restful API

The WebApi extension from jobbrIO/jobbr-webapi adds a Rest-style management interface to jobbr.

Installation

Install the NuGet Jobbr.Server.WebAPI to the project where you host you Jobbr-Server. The extension already comes with a small webserver based on OWIN/Katana. The referenced HttpListenr will be installed by NuGet automatically.

Install-Package Jobbr.Server.WebAPI

Registration

The Library comes with an extension method for the JobbrBuilder. To add the Web API to a Jobbr-Server you need to register it prior calling start as you see below. Please note that this is not an ASP.NET WebAPI when registering it to an OWIN Pipeline, although we’re using the same principle. (In fact, we’re using WebAPI internally)

 1using Jobbr.Server.WebAPI;
 2
 3// ....
 4
 5// Create a new builder which helps to setup your JobbrServer
 6var builder = new JobbrBuilder();
 7
 8// Register the extension
 9builder.AddWebApi();
10
11// Create a new instance of the JobbrServer
12var server = builder.Create();
13
14// Start
15server.Start();

Plugin Configuration

If you don’t specify any value for BackendAddress the server will try to find a free port automatically and bind to all available interfaces. The endpoint is logged and usually shown in the console, but this approach is not recommended for production scenarios, see below:

[WARN]  (Jobbr.Server.WebAPI.Core.WebHost) There was no BackendAdress specified. Falling back to random port, which is not guaranteed to work in production scenarios
....
[INFO]  (Jobbr.Server.JobbrServer) The configuration was validated and seems ok. Final configuration below:
JobbrWebApiConfiguration = [BackendAddress: "http://localhost:1903"]

You can override this behavior, by explicitly providing your own URI prefix, for instance http://localhost:8765/api. See example below:

1builder.AddWebApi(config => 
2{
3	config.BaseUrl = "http://localhost:8765/api";
4});

Note: Please refer to the MSDN Documentation for HttpListener for the supported URI prefixes depending on your operating system and .NET Runtime version.

Rest API Reference

/jobs Endpoint

You could either configure your Jobs by the RepositoryBuilder in C# code or create them by using the API, or both. The followig APIs are made for managing Jobs

Get all Jobs

The following endpoint returns all registered jobs as an array:

GET http://localhost:8765/api/jobs HTTP/1.1
Successful Response

Is indicated by

HTTP/1.1 200 OK
Content-Type: application/json

and the following example payload

[
    {
        "id": 1,
        "uniqueName": "ProgressJob",
        "type": "Demo.MyJobs.ProgressJob",
        "parameters": { 
            "param1" : "test", 
            "param2" : "anothervalue" 
        },
        "createdDateTimeUtc": "2015-03-04T17:40:00",
        "createdDateTimeUtc": "2017-07-30T13:40:00"
    }
]

Single Job details

Details about a single job (including triggers) can be retrieved by accessing the detail route of a job

GET http://localhost:8765/api/jobs/1 HTTP/1.1
Sucessful Response

If the job is found, the response is indicated by

HTTP/1.1 200 OK
Content-Type: application/json

and the following example data is returned. Note the addition of Triggers

{
    "id": 1,
    "uniqueName": "ProgressJob",
    "type": "Demo.MyJobs.ProgressJob",
    "parameters": { 
        "param1" : "test", 
        "param2" : "anothervalue" 
    },
    "createdDateTimeUtc": "2015-03-04T17:40:00",
    "createdDateTimeUtc": "2017-07-30T13:40:00",
    "trigger": [
        {
            "triggerType": "Recurring",
            "id": 1,
            "isActive": true
        }    
    ]
}        
Error Responses

If no job exists with the given id, an error will be returned

Add a new Job

Besides the RepositoryBuilder in C#, there’s also a possibility to create jobs by the Rest API.

Parameters

  • uniqueName: Unique name of the job (Required)

  • type : Name of the CLR Type that should be used (Required)

  • parameters: Object that will be used as JobParameters

Sample Request
POST http://localhost:8765/api/jobs HTTP/1.1
Content-Type: application/json
[
    {
        "id": 1,
        "uniqueName": "ProgressJob",
        "type": "Demo.MyJobs.ProgressJob",
        "parameters": { 
            "param1" : "jobParameter1", 
            "param2" : "anothervalue" 
        }
    },
]
Successful Response
201 Created 
Location: /api/jobs/2
Content-Type: application/json

and the payload

{
    "uniqueName": "ProgressJob2",
    "type": "Demo.MyJobs.ProgressJob"
}    
Error Responses

If there is already a job with the same UniqueName, the error 409 CONFLICT will be returned.

JobRuns for Job

If you wan’t to get all runs for a specific job, you can use the subresource runs on every job. A job can be identified by its internal id or the user specific uniqueName.

Url Parameters

  • id : Internal Id of the Job (Required)

  • uniqueName: Unique name if the job (Required)

GET http://localhost:8765/api/jobs/1/runs

GET http://localhost:8765/api/jobs/progressJob/runs
Successful Response

which will be replied with

HTTP/1.1 200 OK
Content-Type: application/json
[
    {
        "jobId": 1,
        "triggerId": 1,
        "jobRunId": 1,
        "state": "Completed",
        "progress": 100,
        "plannedStartUtc": "2017-07-30T10:53:00Z",
        "auctualStartUtc": "2017-07-30T10:53:00.5132852Z",
        "auctualEndUtc": "2017-07-30T10:53:19.335589Z"
    },
]

Note: Only single entry shown.

/triggers Endpoint

Triggers are used to either add future or instant triggers to a job. The trigger is causing a job to run. Triggers are a subresource of /jobs

There are different types of triggers, each with additional attributes:

  • instant: Schedules a job run that runs immediately (can be delayed by the property delayedMinutes)

  • scheduled: Schedules a a jobrun that starts on the specified startDateTimeUtc

  • recurring: Recurring trigger with a cron definition in definition which is valid between startDateTimeUtc and endDateTimeUtc. If noParallelExecution flag is set to true, the trigger won’t trigger a new job run if one instance of that job is already running (job run is omitted).

The trigger type itself is specified by the property triggerType.

Get all Triggers for a Job

Triggers are attached to a job and therefore accessible as a subresource of a job identified by its internal id or the specified uniqueName.

Url Parameters

  • id : Internal Id of the Job (Required)

  • uniqueName: Unique name if the job (Required)

Jobs can be identified by both their id or uniqueName

GET http://localhost:8765/api/jobs/1/triggers

GET http://localhost:8765/api/jobs/progressJob/triggers
Successful Response

which will be replied with a list of all triggers for this job where some of the properties are valid for all types while others are only applicable to certain types

HTTP/1.1 200 OK
Content-Type: application/json
[
    {
        "id": 1,
        "jobId": 1,
        "isActive": true,
        "comment": "Send the report now!!!",
        "parameters": {
            "printReportId" : "12", 
            "recpient": "me@inbox.com",
        },
        "userId": "user123",
        "userDisplayName": "Michael Schnyder",

        "delayedMinutes": 0,
    },
    {
        "id": 2,
        "jobId": 1,
        "isActive": true,
        "comment": "Send report once at 3pm",
        "parameters": {
            "printReportId" : "12", 
            "recpient": "anotherguy@inbox.com",
        },
        "userId": "user123",
        "userDisplayName": "Michael Schnyder",

        "startDateTimeUtc": "2017-07-30T15:00:00Z",
    },
    {
        "id": 3,
        "jobId": 1,
        "isActive": true,
        "comment": "Daily Report @ 9pm",
        "parameters": {
            "printReportId" : "12", 
            "recpient": "anotherguy@inbox.com",
        },
        "userId": "user123",
        "userDisplayName": "Michael Schnyder",

        "definition": "0 15 * * *",
        "startDateTimeUtc": "2017-01-1T00:00:00Z",
        "endDateTimeUtc": "2022-12-31T00:00:00Z",
        "noParallelExecution": true
    },
]

Please note that the API does not expose the trigger types on the list level.

Single Trigger details

If you want to get one specific trigger only, you need to address the trigger directly as a subresource of the job.

Schema: http://localhost:8765/api/jobs/[jobId]/triggers/[triggerId] where:

  • jobId : Internal Id of the Job (Required)

  • triggerId: Id of the trigger (Required)

Example request
GET http://localhost:8765/api/jobs/1/triggers/3
Successful response
HTTP/1.1 200 OK
Content-Type: application/json

With payload

{
    "id": 3,
    "triggerType": "recurring",
    "jobId": 1,
    "isActive": true,
    "comment": "Daily Report @ 9pm",
    "parameters": {
        "printReportId" : "12", 
        "recpient": "anotherguy@inbox.com",
    },
    "userId": "user123",
    "userDisplayName": "Michael Schnyder",

    "definition": "0 15 * * *",
    "startDateTimeUtc": "2017-01-1T00:00:00Z",
    "endDateTimeUtc": "2022-12-31T00:00:00Z",
    "noParallelExecution": true
}

Note: The type of the trigger is now exposed by the property triggerType.

Add a trigger

Please note that each trigger type has a couple of properties that are common, especially

  • triggerType: Type of the trigger, see above (Required)

  • isActive: Specifies if the trigger is active (Required)

  • parameters: Object that will be used as RunParameters

  • comment: Any arbitrary comment or additional information for this trigger

  • userId: The principal under which the job should be started

  • userDisplayName: Any arbitrary that could be the name of the user that triggered the job

Both uniqueName and internal job id is supported on this route. Examples only show identification of job by internal id

Example Request (Instant)

The minimal example to add an instant trigger is shown below:

POST http://localhost:8765/api/jobs/1/triggers HTTP/1.1
Content-Type: application/json
{ 
    "triggerType" : "instant"
}

isActive is not required and will be automatically set to true, because a non-active instant trigger would be a joke.

Optional Properties (Type related)

  • delayedMinutes: Amount of time (in minutes) in which the start shall be delayed

Example Request (Scheduled)

The minimal example to add an scheduled trigger is shown below:

POST http://localhost:8765/api/jobs/1/truggers HTTP/1.1
Content-Type: application/json
{
    "triggerType" : "instant",
    "isActive": true,
    "startDateTimeUtc": "2017-07-30T15:00:00Z",
}

Optional Properties (Type related)
none

Example Request (Recurring)

The minimal example to add an recurring trigger is shown below:

POST http://localhost:8765/api/jobs/1/triggers HTTP/1.1
Content-Type: application/json
{
    "triggerType" : "recurring",
    "isActive": true,
    "definition": "0 23 * * *",
}

Optional Properties (Type related)

  • startDateTimeUtc: Start of time range when trigger is valid

  • endDateTimeUtc: End of time range when trigger is valid

  • noParallelExecution: Indicates if this trigger is allowed to cause a new job while a job caused by the same trigger is still running.

Successful Response

A successful response will return the created trigger and the location to its details by a 201 Created Response.

Example

201 Created 
Location: /api/jobs/1/triggers/4
Content-Type: application/json

and the payload

{
    "id": 4,
    "triggerType" : "recurring",
    "isActive": true,
    "definition": "0 23 * * *",
}

Update a Trigger

In the rare cases you’ll need to update a trigger before the job run has actually started, there is an endpoint for patching existing triggers of a job

Schema: http://localhost:8765/api/jobs/[jobId]/triggers/[triggerId] where:

  • jobId : Internal Id of the Job (Required)

  • triggerId: Id of the trigger (Required)

Only the following changes are possible

  • Change isActive from true to false or vice-versa

  • Adjust Cron-Definition of a Recurring-Trigger

  • Change StarteDateUtc to any value in the future of a ScheduledTrigger

Each request needs to contain the following properties

  • isActive: Specifies if the trigger is active (Required)

  • triggerType: Type of the trigger, see above (Required for non-instant trigger types)

Sample Request (Instant)

To disable the trigger with id 1, the following call needs to be executed:

PATCH http://localhost:8765/api/jobs/1/triggers/1 HTTP/1.1
Content-Type: application/json
{
    "isActive": false,
},
Sample Request (Scheduled)

To update the scheduled start of a trigger to anything else in future, the following call is required:

PATCH http://localhost:8765/api/jobs/1/triggers/2 HTTP/1.1
Content-Type: application/json
{
    "triggerType": "scheduled",
    "isActive": true,
    "startDateTimeUtc": "2017-08-04T23:00:00Z",
},
Sample Request (Recurring)

To update the scheduled start of a trigger to anything else in future, the following call is required:

PATCH http://localhost:8765/api/jobs/1/triggers/3 HTTP/1.1
Content-Type: application/json
{
    "triggerType": "recurring",
    "isActive": true,
    "definition": "0 22 * * *",
},

/jobruns Endpoint

Jobruns are a result of a job and a trigger that has caused the jobrun itself. JobRuns are read-only.

Query JobRuns

There is no possibility to get all jobruns, but there are query options that allow querying jobruns by

  • userId: Example: http://localhost:8765/api/jobruns?userId=user123

  • userDisplayName: Example: http://localhost:8765/api/jobruns?userDisplayName=Testuser

  • jobId and triggerId: Example: http://localhost:8765/api/jobruns?jobId=3&triggerId=4

Successful Response

A list of matching jobruns will be returned:

HTTP/1.1 200 OK
Content-Type: application/json
[
    {
        "jobId":1,
        "triggerId":3,
        "jobRunId":1,
        "jobName":"ProgressJob",
        "state":"Completed",
        "progress":100.0,
        "plannedStartUtc":"2017-08-02T20:25:00Z",
        "auctualStartUtc":"2017-08-02T20:25:00.4674296Z",
        "auctualEndUtc":"2017-08-02T20:25:18.8927592Z",
        "jobTitle":"ProgressJob",
        "jobParameter": {
            "param1" : "jobParameter1", 
            "param2" : "anothervalue" 
        },
        "instanceParameter": {
            "printReportId" : "12", 
            "recpient": "anotherguy@inbox.com",
        },
        "artefacts": [
            {
                "filename": "report.log",
                "size": 1258,
                "contentType": "text/plain"
            },
            {
                "filename": "sent-mail.eml",
                "size": 258478,
                "contentType": "message/rfc822"
            }            
        ]
    },
    {
        "jobId":1,
        "triggerId":3,
        "jobRunId":2,
        "jobName":"ProgressJob",
        "state":"Processing",
        "progress":78.0,
        "plannedStartUtc":"2017-08-02T20:26:00Z",
        "auctualStartUtc":"2017-08-02T20:26:00.4682476Z",
        "jobTitle":"ProgressJob",
        "jobParameter": {
            "param1" : "jobParameter1", 
            "param2" : "anothervalue" 
        },
        "instanceParameter": {
            "printReportId" : "12", 
            "recpient": "anotherguy@inbox.com",
        }        
    },
]

Note that the second job has not yet completed and thus did not collect any artefacts from the Run-Directory.

Single JobRun details

More detailed information about a specific JobRun can be found on the JobRun details route.

GET http://localhost:8765/api/jobruns/1 HTTP/1.1
Sucessful Response

If the jobrun is found, the response is indicated by

HTTP/1.1 200 OK
Content-Type: application/json

and the following example data is returned. Note the addition of Triggers

{
    "jobId":1,
    "triggerId":3,
    "jobRunId":1,
    "jobName":"ProgressJob",
    "state":"Completed",
    "progress":100.0,
    "plannedStartUtc":"2017-08-02T20:25:00Z",
    "auctualStartUtc":"2017-08-02T20:25:00.4674296Z",
    "auctualEndUtc":"2017-08-02T20:25:18.8927592Z",
    "jobTitle":"ProgressJob",
    "jobParameter": {
        "param1" : "jobParameter1", 
        "param2" : "anothervalue" 
    },
    "instanceParameter": {
        "printReportId" : "12", 
        "recpient": "anotherguy@inbox.com",
    },
    "artefacts": [
        {
            "filename": "report.log",
            "size": 1258,
            "contentType": "text/plain"
        },
        {
            "filename": "sent-mail.eml",
            "size": 258478,
            "contentType": "message/rfc822"
        }            
    ]
}      

Download Artefact

Artefacts are listed as object array in the property artefacts. To download a specific artefact, a GET-Request is required.

http://localhost:8765/api/jobruns/1/artefact/report.log HTTP/1.1

and the file with the approriate headers will be returned

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Lenght: 1258

...

Generic Endpoints

/status

The status endpoint is for testing purposes only. Even if it does not perform health checking it can be used to determine the HTTP bindings and general availability of the Http-Endpoint.

GET http://localhost:8765/api/status HTTP/1.1

Does usually just return:

HTTP/1.1 200 OK

"Fine"

/configuration

If you need to check the current configuration, use the /configuration endpoint.

GET http://localhost:8765/api/status

Which will return a object that represents the current Web-API configuration.

HTTP/1.1 200 OK 

{
    "backendAddress": "http://localhost:8765/api"
}

/fail

If you are curious if logging is setup correctly or if any reverse proxies are interfering with HTTP 500 error codes, you can use this endpoint

GET /fail HTTP/1.1

Will raise an unhandled exception and usually shows an error response similar to

HTTP/1.1 500 Internal Server Error

{
    "message": "An error has occurred.",
    "exceptionMessage": "This has failed!",
    "exceptionType": "System.Exception",
    "stackTrace": "   at Jobbr.Server.WebAPI.Core.Controller.DefaultController.Fail() in DefaultController.cs:line 42
    ...
}

Static Typed C#-Client

There is also a static typed client available which you can use to interact with any Jobbr Rest API. Install the client by using the following commands

Install-Package Jobbr.Client

After installation, you must provide the base url where the API can be found. See example below

1using Jobbr.Client;
2using Jobbr.Server.WebAPI.Model;
3
4// ...
5
6var jobbrClient = new JobbrClient("http://localhost:8765/api");
7
8var allJobs = jobbrClient.GetAllJobs();

Limitations

Authentication

Currently there is no authentication middleware available and the API should therefore not be exposed to the public. However this feature is planned in the future