Interfaces

Interfaces are facts that specify API endpoints.

There are many places in the Factern API where interfaces can be used. They may provide storage, decision making or data consumption.

Examples include

  • Stores for data specified in the write API
  • APIs for allowing or denying permission requests
  • Interfaces for consuming watch events

There are two types of interfaces, Direct and Indirect.

Type Description
Direct Default type, where Factern calls the specified endpoint passing a nodeId as a query parameter
Indirect Where Factern calls the templated endpoint with parameters written in a previous call

Creating an Interface

The createinterface API is used for creating interfaces.

Example:

POST /createinterface
{
    "name": "ExampleStorage",
    "addData": {"url": "https://example.com/add"},
    "getData": {"url": "https://example.com/get"},
    "deleteData": {"url": "https://example.com/delete"}
}

registers the interface ExampleStorage with Factern. The interface indicates HTTPS endpoints for interacting with the interface, for adding, getting and deleting data.

Endpoint Property Description
addData For setting/updating data
getData For getting data
deleteData For deleting data

An interface, just on its own, does not DO anything. It comes into action when it is referenced by other API calls.

An interface can be referenced either using its node id, or using one of the following FRNs.

Description FRI
Full FRI frn:interface:0000000009C2602130DEC995888A2CE624A7FCA1513E1B48:ExampleStorage
Login understood frn:interface::ExampleStorage
Short FRI :ExampleStorage

Information Data Stores

Interfaces are used when working with information. When writing an information node, the id of the storage interface can be specified. This interface is used for writing, reading and obliterating data associated with the information node.

The endpoints will be called with a nodeId query parameter that uniquely identifies the information node associated with the data.

    ?nodeId=

For the addData endpoint, the Content-Type header will be set to application/octet-stream, and the request body will include the data to persist.

For the getData endpoint, the interface implementation should return the data associated with the nodeId.

It is up to the interface implementation to implement correct semantics.

Example:

POST /write
{
    "nodeId": "00000000404AE4402DF969ACE2C2DA9133A23EA48773AF8A",
    "document": {
        "FullName": {
            "#data": "John Watson",
            "#storageId": "MyExampleStore"
        }
    }
}

The example store end-point would receive the request:

POST https://acme.com/add?nodeId=0000000079C27D120B58F74C683639EE72EA2E33B93293CB
John Watson

For a read

POST /read
{
    "nodeId": "00000000404AE4402DF969ACE2C2DA9133A23EA48773AF8A",
    "template": [
        "FullName"
    ]
}

The example store end-point would receive the request:

POST https://acme.com/get?nodeId=0000000079C27D120B58F74C683639EE72EA2E33B93293CB

and be expected to return the associated data, so that the read API response would be:

Response
[{
    "readItem": {
        "data": "John Watson",
        "nodeId": "00000000DD15095A49A56DFF7F9F3C3D18092B7513505394",
        "fieldId": "00000000119385347C3E5723B094E4271AF01FD5540AC54A"
    },
    "status": 200
}]

For an obliterate request:

POST /obliterate
{
    "nodeId": "00000000DD15095A49A56DFF7F9F3C3D18092B7513505394"
}

The example store end-point would receive the request:

POST https://acme.com/delete?nodeId=0000000079C27D120B58F74C683639EE72EA2E33B93293CB

The Default Storage

For all data persisted, where a storage interface is not specified, Factern provides a default store.

Other methods

The default is for Factern to use the HTTP method POST on calls to the interface endpoints. You can change this by specifying the method.

Example:

POST /createinterface
{
    "name": "PutGetStorage",
    "addData": {
        "method": "PUT",
        "url": "https://example.com/add"
    },
    "getData": {
        "method": "GET",
        "url": "https://example.com/get"
    }
}

Writing an information node having that interface will be executed as a PUT:

PUT https://acme.com/get?nodeId=0000000079C27D120B58F74C683639EE72EA2E33B93293CB
John Watson

While reading will be executed as a GET:

GET https://acme.com/get?nodeId=0000000079C27D120B58F74C683639EE72EA2E33B93293CB

Additional Http Headers

You can supply additional Http headers to your endpoint calls using the "headers" property of the endpoint.

Example:

POST /createinterface
{
    "name": "JsonStorage",
    "addData": {
        "method": "PUT",
        "headers": [
            {
                "key": "Content-Type",
                "value": "application/json"
            }
        ],
        "url": "https://example.com/add"
    },
    "getData": {
        "method": "GET",
        "url": "https://example.com/get"
    }
}

Supplying a Request Body

You can specify a request body on your endpoint calls, though, only on getData endpoints.

Example:

POST /createinterface
{
    "name": "TokenStorage",
    "addData": {
        "url": "https://example.com/add"
    },
    "getData": {
        "method", "PUT",
        "url": "https://example.com/get",
        "body": "requestToken = \"123\""
    }
}

Indirect interfaces

In some cases, you may need to work with an endpoint that cannot accept the nodeId parameter, and, in fact, needs parameters that must vary depending on context. For example, there may be an endpoint that can supply additional data about your entity that takes an id relevant to that endpoint.

You specify that an interface is Indirect using the "type" property of the endpoint.

Example:

POST /createinterface
{
    "name": "AdditionalDataStorage",
    "addData": {
        "type": "Indirect",
        "url": "https://example.com/get/{userId}"
    },
    "getData": {
        "type": "Indirect",
        "method": "GET",
        "url": "https://example.com/get/{userId}",
    }
}

The addData and getData endpoints must both be of type Indirect and their url properties must be identical.

When you write an information node having an Indirect Storage interface, the written values are interpreted as a JSON document containing parameter values.

Example:

POST /write
{
    "nodeId": "frn:entity::JohnWatson",
    "document": {
        "AdditionalData": {
            "#data": "{\"userId\": \"john-watson-789\"}",
            "#storageId": "AdditionalDataStorage"
        }
    }
}

Subsequent reads against that information node will use the previously supplied parameters in calls against the getData endpoint.

Following on from the previous example a read:

POST /read
{
    "nodeId": "frn:entity::JohnWatson",
    "template": [
        "AdditionalData"
    ]
}

would return the result of calling the getData endpoint as

GET https://acme.com/get/john-watson-789