@vendia/block-subscription-handler
v2.0.0
Published
A library to simplify the handling of Vendia Share Block Notifications in AWS Lambda
Downloads
13
Keywords
Readme
@vendia/block-subscription-handler
A TypeScript library to simplify the handling of Vendia Share Block Notifications in AWS Lambda
What is it?
This library helps Vendia Share Customers build integrations using Block Notifications. Inside a Share Uni (universal application), any mutating changes (add,create,update,etc.) result in a block being written to the ledger supporting the Uni. When a block is written, notifications are emitted and can be consumed by a number of methods like AWS Lambda, providing a great opportunity for integrations.
See: Vendia Share Integrations for details regarding the configuration and permissions needed to use an AWS Lambda function with Vendia Share Unis as it is not covered in this README
The Problem?
When subscribing to block notifications from a Share Uni, the only information available regards the block itself and not the data that relates to it.
The Solution
This library parses block notifications and can provide data relating to the block through two methods. The first involves parsing the Graphql mutations that resulted in the block creation. The second involves introspecting these mutations to dynamically query the related objects from the Uni's world state. The How It Works section below shows these processes in more detail.
Usage
Install the module
//Typescript
import * as blockSubscriptionHandler from '@vendia/block-subscription-handler';
//CommonJS
const blockSubscriptionHandler = require('@vendia/block-subscription-handler');
Functions
Getting Mutations
Parse Amazon SNS or SQS messages for Block Notification Mutations
Using the library's GetMutationsFromSnsBlockEvent or GetMutationsFromSqsBlockEvent functions, the SNS or SQS messages containing block notifications can easily be parsed within the AWS Lambda service to return the GraphQL Mutations related to the block. This will require the ability to query the Uni that emitted the notifications.
The GetMutationsFromSnsBlockEvent function requires three arguments:
| Argument | Description | | -------------- | -------------------------------------------------------- | | event | The SNS event object presented to an AWS Lambda function | | vendiaShareURL | The Uni's GraphQL URL | | vendiaShareKey | The Uni's GraphQL API Key |
The GetMutationsFromSqsBlockEvent function requires three arguments:
| Argument | Description | | -------------- | -------------------------------------------------------- | | event | The SQS event object presented to an AWS Lambda function | | vendiaShareURL | The Uni's GraphQL URL | | vendiaShareKey | The Uni's GraphQL API Key |
Getting Block Data
Sometimes it is necessary to retrieve mutations along with Block-level fields like Owner and Commit Time. In these cases, parsing the entire block from SQS or SNS messages can be achieved with the GetBlockFromSqsBlockEvent and GetBlockFromSnsBlockEvent functions
The GetBlockFromSqsBlockEvent and GetBlockFromSnsBlockEvent functions accept the same inputs as the above GetMutationsFromSnsBlockEvent and GetMutationsFromSqsBlockEvent functions. Instead of returning a list of string mutations, these functions return an object with the following interface:
{
_TX: [
Owner: string,
Mutations: ReadonlyArray<string>,
TxId: string,
]
CommitTime: string;
}
ParseMutation
Parse the object data for each mutation
The ParseMutation function is the fastest way to retrieve a typed object from the GraphQL mutation. This function uses only the Graphql Mutation AST to build a typed object. This is useful for mutations with operations like Add, Create, and Put. In the case of Update mutations, which are allowed to include partial data, the GetMutationObjectFromAPI function may be more desirable as it queries the API for all fields of a specific type regardless of their use in an Update.
The ParseMutation function requires one argument:
| Argument | Description | | -------- | ----------------- | | mutation | A string mutation |
Example Output
//input mutation
"addtopList(id:\"01797be0-cb8d-9bad-aa08-cc8356c11f95\",input: {innerList: [{name: \"innerListName\", object: {objectName: \"ObjectName\"}}], name: \"listName\"}){error}"
//output
{"__typename":"Event","arguments":{"id":"b4de7525-623b-11eb-a0cb-0db0d645b658","input":{"animal_id":"b434d448-623b-11eb-afea-59074c0526d3","organization_id":"6fe94056-5bd4-11eb-a9fc-0bb70a7f9c77","timestamp":1611929411261,"node_created":"Node-2","type":"intake","nested":{"thing":["intake"]},"sub_type":"Stray/OTC","location_description":"","three_legged":false,"address1":"","address2":"","city":"","state":"","zipcode":"","geo_location":[0,1]}},"operation":"add"}
GetMutationObjectFromAPI
Get the object data via GraphQL API Introspection
The GetMutationObjectFromAPI function uses GraphQL introspection to dynamically build a Get query for the type referenced in a mutation. This has the advantage of querying all fields on a type regardless of its mention in the mutation itself. This function requires access the to Uni's API and makes several API calls (one per type or nested type, and one for the Query).
The GetMutationObjectFromAPI function requires three arguments:
| Argument | Description | | -------------- | ------------------------- | | mutation | A string mutation | | vendiaShareURL | The Uni's GraphQL URL | | vendiaShareKey | The Uni's GraphQL API Key |
Example Output
//input mutation
"addtopList(id:\"01797be0-cb8d-9bad-aa08-cc8356c11f95\",input: {innerList: [{name: \"innerListName\", object: {objectName: \"ObjectName\"}}], name: \"listName\"}){error}"
//output
{"arguments":{"id":"01797be0-cb8d-9bad-aa08-cc8356c11f95","innerList":[{"name":"innerListName","object":{"objectName":"ObjectName"}}],"name":"listName"},"operation":"add","__typename":"topList"}
Full Example
import * as blockSubscriptionHandler from '@vendia/block-subscription-handler';
//or for commonjs
//const blockSubscriptionHandler = require('@vendia/block-subscription-handler');
const url = process.env.vendiaShareURL;
const key = process.env.vendiaShareKey;
exports.handler = async function (event) {
console.log('EVENT' + JSON.stringify(event));
//Parse mutations from AWS Lambda Event
//Note an object like {CommitTime:string, Mutations[...string...]} is returned
const Mutations =
await blockSubscriptionHandler.GetMutationsFromSnsBlockEvent(
event,
url,
key
);
/* If using SQS and not the default SNS
await blockSubscriptionHandler.GetMutationsFromSqsBlockEvent(
event,
url,
key
);
*/
console.log('Mutations ' + JSON.stringify(mutationData));
console.log('Commit Time ' + mutationData.CommitTime);
await Promise.all(
Mutations.map(async function (m) {
try {
console.log('Processing mutation ' + m);
const blockFromShare =
await blockSubscriptionHandler.GetMutationObjectFromAPI(m, url, key);
//Using ParseMutation
const blockFromAST = blockSubscriptionHandler.ParseMutation(m);
console.log('Block from AST Parsing: ' + JSON.stringify(blockFromAST));
console.log('Block from API Query: ' + JSON.stringify(blockFromShare));
} catch (ex) {
console.error('Error ->' + ex);
}
})
);
};
How it Works
This section details the inner working of each function used to parse mutations
ParseMutation
The ParseMutation function uses only the GraphQL AST of a mutation to generate a typed object. This approach uses the GraphQL-js Library to traverse the AST tree and build an typed object based upon the arguments in the mutation.
GetMutationObjectFromAPI
The GetMutationObjectFromAPI function uses GraphQL introspection to build a new Get Query for the type referenced by a graphql mutation. This process involves several steps:
Step 1: Parse the mutation for the Graphql Type Name and the Identifier reported in the Block mutation
Step 2: Introspect the input object type via Graphql Queries
Step 3: Observe any nested types and recursively query them via Graphql
Step 4: Generate a new Query to retrieve the type and its nested types and fields
Step 5: Execute the Query and return a typed object based upon the result.