Functional Service & API

Overview

Broadly speaking, a Functional Service coordinates one or more data exchanges such that they have a beginning, middle, and an end. Other commonly used terms or concepts covered with Functional Services are workflows, long running jobs or transactions.

A Functional Service consists of:

  • a ‘functional service’ definition describing the service

  • a ‘Job Object’ format for communicating intent, status, and results

  • one or more ‘Phases’ - which accomplish the Job step-by-step

Basically a Functional Service has a start state, phases to walk through (intermediate states) and an end state. Functional Services in SIF perform in a similar manner to Data Object Services. The SIF 3 infrastructure routes the Function Service message, and data defines a ‘use case driven’ Job Object, in order to coordinate the multiple phases necessary to accomplish the desired result (end state).

Typical examples where a Functional Service might be useful include but are not limited to:

  • End-Of-Year Rollover: This includes several phases/steps such as end-date current enrollments, create new enrollments etc.

  • Assessment Grading Service: The purpose of a service is often adding a way to know when a task has been completed and final results can be understood.

Supported Operations

The Functional service supports the operations listed below. Detailed Open API documentation of each endpoint of the Environment API can be found on the Open API page.

Operation Name

HTTP Verb

URL Segment

DELAYED Supported

Events Supported

Operation Name

HTTP Verb

URL Segment

DELAYED Supported

Events Supported

Job Operations

 

 

 

 

Create Single Job

POST

jobs/{serviceNames}/{serviceName}

No

Conditional: A Job Create Event might be published by the Functional Service Provider for monitoring purposes.

Create multiple Jobs

POST

jobs/{serviceNames}

Yes

No

Get Single Job

GET

jobs/{serviceNames}/{id}

No

No

Get a list of Jobs

GET

jobs/{serviceNames}

Yes

No

Delete Single Job

DELETE

jobs/{serviceNames}/{id}

No

Yes

Get Info about Service

HEAD

jobs/{serviceNames}

No

No

Update Job

 

 

 

Yes (Update events by provider). Consumer cannot update a job's overall data.

Phase Operations

 

 

 

 

Create Data for a Phase

POST

jobs/{serviceNames}/{id}/{phaseName}

No

No

Get Data for a Phase

GET

jobs/{serviceNames}/{id}/{phaseName}

No

No

Update Data for a Phase

PUT

jobs/{serviceNames}/{id}/{phaseName}

No

No

Delete Data for a Phase

DELETE

jobs/{serviceNames}/{id}/{phaseName}

No

No

State Operations

 

 

 

 

Get States for a phase

GET

jobs/{serviceNames}/{id}/{phaseName}/states

No

No

Update State for a phase

POST

jobs/{serviceNames}/{id}/{phaseName}/states/state

No

No

Job Owner

It is important to note that only a Service Consumer can start/create a Functional Service and therefore is the ultimate owner of the Functional Service. This is represented as an instance of the Functional Service through a specific Job Object. All data exchanges throughout the lifetime of the Functional Service are between the Service Consumer that created the Functional Service and the Functional Service Provider. It is noteworthy that each Functional Service type can have a different Service Provider.

Functional Service Payload (Job Payload)

The core payload that defines the Functional Service, its phases and states is the Job Object defined in the Infrastructure Data Model. Below is a sample Job payload. Note that the sample contains all elements. The elements marked with <!-- CREATE REQUEST --> indicate that they are required to be provided by the Service Consumer when a Functional Service is created/started (HTTP POST). Details about each element are documented on the Infrastructure Data Model site or the SIF Open API site for the Job API.

<job id="7173fcf3-d3cd-4a84-af5b-75e573914726" xmlns="http://www.sifassociation.org/infrastructure/3.4"> <name>rolloverStudent</name> <!-- CREATE REQUEST --> <description>Rollover Student from year X to year Y</description> <state>NOTSTARTED</state> <stateDescription>Not Started</stateDescription> <created>2018-07-31T10:45:11.903+08:00</created> <timeout>P30D</timeout> <phases> <phase> <name>oldYearEnrolment</name> <states> <state> <type>NOTSTARTED</type> </state> <state id="76ec3e49-146d-4a85-89b5-e189bc7c7b85"> <type>PENDING</type> <created>2023-01-04T09:11:57.180+08:00</created> <lastModified>2023-01-04T09:11:57.180+08:00</lastModified> </state> <state id="09888eaf-6d70-4532-a54f-c5b56c102661"> <type>INPROGRESS</type> <created>2023-01-04T10:00:00.208+08:00</created> <lastModified>2023-01-04T10:00:00.208+08:00</lastModified> </state> </states> <required>true</required> <rights> <right type="UPDATE">APPROVED</right> </rights> <statesRights> <right type="QUERY">APPROVED</right> <right type="CREATE">APPROVED</right> </statesRights> </phase> <phase> <name>newYearEnrolment</name> <states> <state> <type>NOTSTARTED</type> </state> </states> <required>true</required> <rights> <right type="CREATE">APPROVED</right> </rights> <statesRights> <right type="QUERY">APPROVED</right> <right type="CREATE">APPROVED</right> </statesRights> </phase> </phases> <initialization> <!-- Optional: CREATE REQUEST --> <phaseName>oldYearEnrolment</phaseName> <payload> <parameters> <parameter> <key>studentRefID</key> <value>45c22938-7217-4d99-832d-0e61b836ba9e</value> </parameter> <parameter> <key>schoolRefID</key> <value>1a76fe32-29a6-4dd9-90b5-0a2980ad5e8f</value> </parameter> </parameters> <payload> </initialization> </job>

Sections of the Job Object

The Job Object has several sections. They are shortly discussed below. Please refer to the payload example listed above when each section is discussed.

Core Data

The core data is all the data except the <phases> and <initialization> section. It main elements in the core are the id (attribute in above example), name, description, overall state and the timeout of the job. When a functional service consumer (owner of the functional service) creates a new job all it must provide is the name of the job, which is the same as the serviceName of the functional service URL. All other elements will be populated and returned by the functional service provider, including the id. It is also important to note that the fingerprint HTTP header will be returned in the response. Once a functional service is created all subsequent calls to the functional service endpoint will require the job id as this defines the resource.

For example if a functional service consumer wants to get the latest state of a functional service it needs to call the HTTP GET with the serviceName and id in the URL. Note that the serviceName must be the plural form (add an 's' at the end of the name)!

Example based on above payload: HTTP GET .../job/rolloverStudents/7173fcf3-d3cd-4a84-af5b-75e573914726

The timeout element indicates how long the job will be valid for. Once the time is expired the job overall state might be change to FAILED if hasn't completed or COMPLETED if it has. The timeout element is an XML duration type as defined here: http://www.datypic.com/sc/xsd/t-xsd_duration.html.

Phases & Phase States

Phases are the “steps” a functional service passes through. Data can be exchanged in a phase. Each phase has a unique phase name that is defined in the <name> node. This name must be used as a URL segment to perform any operations for or of a phase. Any phase operations will have the following URL pattern: .../job/{serviceNames}/{id}/{phaseName}.

Example based on above payload: HTTP PUT .../job/rolloverStudents/7173fcf3-d3cd-4a84-af5b-75e573914726/oldYearEnrolment

There can be 0 or as many phases as required. If a phase is required then the <required> element must be set to true otherwise the phase is considered optional. A phase can either be executed by the functional service consumer or provider. Each phase has a set of rights and states. The <rights> node indicates what operations are allowed in the respective phase. For example if the following right is set:

<rights> <right type="UPDATE">APPROVED</right> </rights>

… then a HTTP PUT (update) is allowed in this phase. The <states> node holds the various states a particular phase has gone through. Note that this is a list since a phase can pass through a series of states. There is also a set of rights called <stateRights> that do apply for the states of a phase. It defines if a functional service consumer can add new states (CREATE right) to a phase (e.g. move a phase into a new state) or if a functional service consumer is only allowed to retrieve the states (QUERY right) of a phase.

As indicated data can be exchanged in a phase. However the structure of the actual payload is not defined as part of the functional service definition. The contract of the exchanged payload is based on specific use-cases and specified or defined out of bound of the functional service definition.

Initialization Data

At creation time (create request) of the functional service a functional service consumer can provide initialisation parameter in the <initialization> section. A phase name can be provided but it is generally assumed and encouraged that the initialisation parameters relate to the initialisation rather than to a phase. The <payload> node is defined as a xs:any. This allows for any sub-structure to be provided. However it is strongly encouraged to use an as simple as possible structure in this section due to the fact that many if not all libraries struggle to translate an xs:any construct to a meaningful internal data structure. The example above would be a reasonable compromise between flexibility and complexity.

Endpoints

There are three (3) levels of endpoints for functional services, specifically for the Job object. The first level are the endpoints for the core Job object, the second level endpoints evolve around the phases while the third level endpoints operate on the state of phases. Each available set of endpoints and their naming structure and available operations are shortly outlined in the following sections. As it is with all SIF endpoints the naming convention is base on the SIF object name. For functional services the name of the object is ‘job’ hence the ‘job’ is the base of all endpoints.

Job Endpoints & Operations

Job endpoints evolve around the core Job object functionality. The base URL for all job related operations is .../jobs/{serviceNames}. The jobs segment indicates what the SIF object is while the {serviceNames} segment is the actual name (in plural form) of the functional service as listed in the SIF environment. Most operations, including batch operations, as with a standard SIF Object are supported with the exception of the update (HTTP PUT) operation. The core data of a job object cannot be updated by a service consumer once it is created. Only the service provider can update the overall <state> node of the job based on actions in phases.

Phase Endpoints & Operations

Phases support all CRUD operations. It is important to note that data might or might not be exchanged in a particular phase. Further a phase may require activity by the service consumer or by the service provider. Finally if any data exchange is executed as part of a phase the data structure (payload) is not fixed to a locale’s data model. It might use SIF Objects of a locale’s data model or it might use any other data structure. The Job object that wraps the functional service does not specify the data structure that might be exchanged in a phase. The specification/definition of data types and structures needs to be agreed upon out-of-bound for each functional service and its phases. This approach provides a high degree of flexibility for the usage of functional services.

The URL for a phase has the following structructure: .../jobs/{serviceNames}/{id}/{phaseName}

serviceNames: The name of the functional service.

jobId: The unique id of the job as returned in the job create response.

phaseName: The name of the phase

HTTP GET, PUT, POST and DELETE are all valid verbs but the phase rights (ACL) will specify which of these HTTP verbs are applicable for a specific phase.

Example

<phases> <phase> <name>oldYearEnrolment</name> <rights> <right type="UPDATE">APPROVED</right> </rights> <statesRights>...</statesRights> </phase> <phase> <name>newYearEnrolment</name> <rights> <right type="CREATE">APPROVED</right> </rights> <statesRights>...</statesRights> </phase> </phases>

The above example indicates that in phase oldYearEnrolment the only operation available to the service consumer is UPDATE which equates to a HTTP PUT. It would assume that the service consumer is expected to ‘update' some data as part of this phase by means of providing some payload. However in the phase newYearEnrolment the service consumer has the CREATE right which equates to a HTTP POST. As with all ACLs in the SIF specification it must be assumed if a 'right’ is not specifically listed in the ACL then that right is not available. For above example neither phase has the QUERY or DELETE right set to APPROVED, hence the service consumer is prevented to issue a HTTP GET or HTTP DELETE request. If it does then a HTTP Error Status 403 (Forbidden) must be returned by the service provider.

Phase State Endpoints & Operations

As a service consumer or provider work through a particular phase the state of a phase can change accordingly. A state cannot be overwritten, though (no update functionality). A service consumer or provider can only add a new state to a phase (create operation). Of course a service consumer or provider can, at anyone time, query the state of a phase. The response to a query request of the phase state will always return a list of the states the phase as passed through. The latest state (based on timestamp or last element in the list) must be considered the current state of the phase. Since these states belong to a a phase of a particular job the name of the phase appears in the URL.

The URL has the following structure: .../jobs/{serviceNames}/{jobId}/{phaseName}/states

serviceNames: The name of the functional service.

jobId: The unique id of the job as returned in the job create response.

phaseName: The name of the phase

Query States

Request

URL and HTTP Verb: HTTP GET .../jobs/{serviceNames}/{jobId}/{phaseName}/states

Example: Query the state(s) of phase newYearEnrolment of job rolloverStudent

HTTP GET .../jobs/{jobId}/rolloverStudent/newYearEnrolment/states

Response

A list of states.

Create State

Request

URL and HTTP Verb: HTTP POST .../jobs/{serviceNames}/{jobId}/{phaseName}/states/state

Example: Add the state to the phase newYearEnrolment of job rolloverStudent

HTTP POST .../jobs/{jobId}/rolloverStudent/newYearEnrolment/states/state

Payload: <state><type>INPROGRESS</type></state>

Note the final segment of state in the URL. It follows that standard naming convention and URL structure for the create operation of all SIF Objects.

Response

The details of the newly created state, including an id of the state and the create timestamp.

Environment Setup/Configuration

Environment ACL

As with all endpoints and operations the overall SIF Environment must contain applicable entries (service) to provide access to a specific functional service to a service consumer.

Example

The above will provide access to the functional service called RolloverStudents to a service consumer. The service consumer has the rights to create, query and delete jobs related to this functional service type (note that there is no update right as this operation is not supported for functional services). It is important to note that the type="FUNCTIONAL" is required to indicate that this service is a functional service and not an object service or any other type of service.

Phase and State ACLs vs Environment ACLs

A functional service has ACLs defined that apply to phases and states. They are listed in the actual job object and not in the service node of the respective environment. Please refer to section Phases & Phase States for details on how to define rights for phases and states.

However, it is important to note that service ACLs in the consumer environment are independent to phase and state ACLs. While the service ACLs may not enable the ‘update’ operation (not available for job objects) such an update operation might be available for a phase. This would indicate that an HTTP PUT is supported for the data that might be exchanged in a phase.

Events

Events are supported for the Job Object but not for data objects exchanged in phases as phases are agnostic in terms of the data exchanged in a phase. However a change to a phase state will trigger a Job Update Event. The Job Object may be changed only indirectly, through: Phase operations, State creation requests, and the Provider’s own logic. It is important to note the end-point of the Job Events is the standard event connector, using the HTTP POST.

The table below provides a summary of supported event types and when they are intended to be used.

Event Type

Usage/Constraints

Event Type

Usage/Constraints

CREATE

Sent upon job creation. Typically in response to servicing a job creation request successfully.

UPDATE

Sent upon change in the Job Object. Generally used to track the progress of a Job, sometimes causing the receiving service consumer to act. Note that the service consumer cannot update a job (update operation is not supported for service consumers). However, the service provider can update the overall state of a job and this in return may cause an applicable update event to be published. This event can be picked up by service consumer who owns the job and can act upon it.

DELETE

Send upon Job deletion. Adapters should use this to cleanup the specified Job.

HTTP Header ‘fingerprint’

In a Brokered Architecture the Broker will add the service consumer environment’s fingerprint HTTP header to all job creation requests. This fingerprint HTTP header is passed to the Functional Service Provider in each request. The service provider must include that fingerprint in any related service event intended for the owner of the functional service instance (job). Events published without specifying a fingerprint will be delivered to all subscribed consumers. Basically adding the fingerprint to an event message ensures that only the owning service consumer of the functional service instance will receive the even but no other service consumer.

In a direct environment the Functional Service provider may create/add the service consumer environment’s fingerprint to any Functional Service responses.

Event Subscribing

A service consumer can subscribe to Functional Service events in the same manner as subscribing to an Object Service events. Also refer to Subscription Service & API for details. A sample subscription payload to subscribe to functional service events is listed below. Note that the serviceType element is set to FUNCTIONAL. The serviceNameelement must match the nameelement of the job object of the corresponding functional service.

Note: If the HTTP Header fingerprint is set in an event then only the Functional Service consumer that relates to the fingerprint, the owner of the functional service, shall receive the event.

Monitoring a Job

Service Consumer

A Functional Service consumer can monitor the progress of a job by either subscribing to events of the Functional Service (see previous section for details) or by regularly query the job object (HTTP GET .../jobs/{serviceNames}/{id}).

“Monitoring” Zone(s)

As noted in previous sections events are generally directed to the owner of the functional service instance by adding the HTTP Header fingerprintto each event. However events can be published without the fingerprint HTTP Header. There might be use-cases where a “monitoring” zone is set up that needs to know about activities of all instances of a functional service. In such a case the functional service provider may also publish events to a specific zone or zones without the fingerprint HTTP Header. All events of all functional service instance may be sent to that dedicate zone(s).