Nebulous Puffy Marshmallows

    @vendia/block-subscription-handler
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.0 • Public • Published

    @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.

    Keywords

    Install

    npm i @vendia/block-subscription-handler

    DownloadsWeekly Downloads

    1

    Version

    2.0.0

    License

    Apache-2.0

    Unpacked Size

    3.1 MB

    Total Files

    64

    Last publish

    Collaborators

    • silversteez
    • davidwells
    • brettstack
    • vendibot