The interface section of the API is dynamic based on the and their relationships set up through Architect. Molten exclusively queries devices and device data through interface. If you have access to a systemId, making calls to interface is the best way to query for devices. In the UI context, it is required for the permissioning structure, and in other cases, it ensures that needed relationships exist. The other way to get device data is through /v1/project/:projectId/device and /v1/projectId/:projectId/device/:deviceId, which requires higher level permissions than the interface does, and doesn’t have all of additional blueprint and relationship structuring that the interface does.
Concepts
Root
Devices and their relationships can be visualized as a tree, where each node is a device, and each child device of that device is a branch. Interface calls can start at any device in the tree, and follow the children down to any leaf. The root refers to the device that you choose to start your interface call from.
The root device will be the first device to be referenced in the route, and is important because it affects permissions, and context.
Groups
Groups are when you target more than one device, and occur when you only make your interface call against a blueprint or you follow a relationship path that ends in a 1:many relationship, and don’t specify a device. Group calls go to the elasticsearch database to fetch the list of applicable devices based on the relationship path in the interface call.
Items
Items are another way to refer to single devices. An interface call will return an item whenever the interface path ends with a deviceId. It will also return an item if the interface path ends on a relationship that is 1:1. Item calls go to firebase to fetch the specified device.
Routes / Paths
All calls to interface start with /v1/interface/:systemId. Where systemId is the id of the system you are querying through devices. The next part of the url is /:blueprintAlias or /:blueprintId is the id or defined alias of the blueprint that you want to be the root. If you end the slug here, it will result in a call to get all the devices in the system of the given blueprint. From there, you can add /:deviceId to the call to get a specific device of the given blueprint. The /:blueprintId/:deviceId will be the root of your call as defined above.
From there you can add to the url to fetch children of the root. Adding a /:childRelationshipAttributeName will then fetch the group or item of the child relationship depending on if it is a 1:many or a 1:1 relationship. For a 1:many relationship you can then add /:deviceId to get an item.
Every time you are pointed at an item, you can specify a subsequent /:childRelationshipAttributeName, and every time you are on a group, you can specify a subsequent /:deviceId to get an item. In this way you can walk the devices from the desired root to the desired leaf.
Paths are anything after the /v1/interface/:systemId and in our tree example refers to the “path” through the branches the call is taking.
Examples
For This Example we are going to assume a blueprint structure similar to the stack trainings demo with a Corporation, Farm, and Cow blueprint.
Our relationships are going to be:
Corporations > Farms (1:many) corporationFarms Corporations > Cows (1:many) corporationCows Farms > Cows (1:many) farmCows Some use cases and the corresponding calls would be:
List all cows in a corporation /v1/interface/:systemId/corporation/:corporationId/corporationCows List all cows in a farm in a corporation /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId/farmCows List all cows in a farm /v1/interface/:systemId/farm/:farmId/farmCows List all cows in the system /v1/interface/:systemId/cow It is important to note that while the 3 above calls all get a list of Cows, they are different in functionality. For the first call, all the cows for the corporation will be returned independent upon whether or which farm they belong to.
Assuming the second and third calls have the same :farmId they are going to return the same subset of Cows, but the functionality will be different. First of all, the permissions check is going to be different. For the second call, the permissions check is going to be against the corporation, and for the third, the permissions check is going to be against the farm. The other notable difference is that the API enforces that there is an existing relationship between the corporation and farm that you are querying.
In a similar vein
/v1/interface/:systemId/corporation/:corporationId/corporationCows/cowId /v1/interface/:systemId/farm/:farmId/farmCows/:cowId /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId/farmCows/:cowId /v1/interface/:systemId/cow/:cowId will all return the same cow but their functionality on the the API side is different. Similar to the cow list examples above, the permissions check for each of these calls is different. Also, the API enforces that the path exists, and the devices and relationships in the path are valid.
* Queries
Deprecated. Not Recommended for use. Use at your own risk. (Also possibly suffer the wrath of the stack team)
* can be used in place of any id as a wildcard to specify all possible ids for that device type.
/v1/interface/corporations/:corporationId/corporationFarms/*/farmCows will return a table that contains all the farms that belong to the corporation and all the cows that belong to those farms.
* queries were depreacated when the stack moved to ElasticSearch due to its low performance. It is recommended to get the desired farms, and then provide an ES filter Query with your interface call to get the desired list of cows.
API Lib
The API lib has a lot of methods to make calling the interface easier.
interface - returns an object with the context of the params passed in with the initiation systemId - the id of the system that you are fetching devices from blueprintId or blueprintAlias - the id or alias of the blueprint The Methods of the returned interface object are:
Edited List:
.obj(deviceId) - Target a specific device (returns item context) .grp(relationshipPath) - Target a relationship group .list(options) - List items in a group .get(options) - Get a single item .search(options) - Search with filters (ES-based) .create(data) - Create device or relationship .update(data) - Update device data/header .delete(options) - Delete device or relationship .history(options) - Query historical data .parent(pathToChild, childId) - Get parent device from child .forwardParent(pathToChild, childId) - Cached parent lookup Objects are items
List, get, history, events, create
obj, grp
// Get all cows in a farm
api.interface(systemId, 'farm').obj(farmId).grp('farmCows').list()
// Get a specific cow
api.interface(systemId, 'farm').obj(farmId).grp('farmCows').obj(cowId).get()
// Search with filter
api.interface(systemId, 'cow').search({
filter: { type: 'equals', field: 'data/status', value: 'active' }
})
Molten
Molten - delegates child, parent
Permissions
Permission checks occur at the root device, the system, and the last child device. If a call is made at the group of root blueprints, the api will return a list with all the items that the user has permissions for. A user needs to have access to all the devices along a path in order for the call to return successfully. (There is an edge case when the path has the root and one child device. Even if the user only has access to the child device, and not the root, the API is able to use the parent reference on the child to verify that the root is a correct path.) The is because the API will attempt to access each item to validate the path, and when accessing each item, will validate that the user can access that item. The API only checks the relationships that are in the interface path, so if you have access on a parent device, but don’t have it set as the root in the path, the api call will return a forbidden.
Permissions examples
Expanding on our Corporations, Farms and Cows example above, there is a user with only admin permissions on a Farm. Assuming references to :farmId are a farm that the user has access to.
/v1/interface/:systemId/farm - would be a group with the farms that the user has permissions on /v1/interface/:systemId/corporation - /v1/interface/:systemId/cow - similar behavior to corporation /v1/interface/:systemId/corporation/:corporationId/corporationFarms - will return a forbidden error /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId - will return the farm due to the user having permissions on the farm, and through the parent reference being able to validate the corporation is correct even though the user doesn’t have access to the corporation /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId/farmCows/:cowId - forbidden because the user does not have access to the system, or corporation. /v1/interface/:systemId/farm/:farmId - returns the farm /v1/interface/:systemId/farm/:farmId/farmCows - returns all the cows that are children of the farm /v1/interface/:systemId/farm/:farmId/farmCows/:cowId - returns the cow assuming it is a child of the farm If we are looking at a user with only admin permissions on a cow
/v1/interface/:systemId/farm - double check - empty list or forbidden /v1/interface/:systemId/corporation - double check - empty list or forbidden /v1/interface/:systemId/cow - would return all the farms that the user has access to /v1/interface/:systemId/corporation/:corporationId/corporationFarms - will return a forbidden error /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId - forbidden /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId/farmCows/:cowId - forbidden because the user does not have access to the system, or corporation. /v1/interface/:systemId/farm/:farmId - returns the farm /v1/interface/:systemId/farm/:farmId/farmCows - forbidden /v1/interface/:systemId/farm/:farmId/farmCows/:cowId - returns the cow assuming it is a child of the farm similare to the /v1/interface/:systemId/corporation/:corporationId/corporationFarms/:farmId case above /v1/interface/:systemId/corporation/:corporationId/corporationCows/:cowId same as previous If we are looking at a user with only admin permissions on a corporation
Due to the Corporation being the highest level, all calls where the corporation is the root will return the corresponding devices, and any that do not have the corporation as the root will return a forbidden. Query Params
offset - when included with limit, specifies the start index of the returned devices limit - specifies the number of devices that the api will respond to the call with startIndex - (deprecated for offset) when included with perPage, specifies the start index of the returned devices perPage - (deprecated for limit) specifies the number of devices that the api will respond to the call with nextToken - a call that returns without the end of the list of devices, it will include a nextToken, when then sent back to the api, it will respond with the next set of devices from the initial call. Usage with LOL
Terms