Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send graphQL mutation based on DynamoDb stream with NodeJS

I'm trying to update a GraphQL subscription when a DynamoDb table receives a new row. I got the following code working with only the RekognitionId, but I'm not trying to send the entire NewImage object, and I cannot make it work. I get all sorts of type problems, but with no real information to solve it with. The most telling was:

"Error: GraphQL error: Variable 'input' has an invalid value. Expected type 'Map' but was 'String'. Variables for input objects must be an instance of type 'Map'."

Unfortunately, I can't find a single reference to a GraphQL type called "map", so it's probably scrambled.

Does anyone have any experience of this? This is my Lambda function, like I said it worked with only RekognitionId formatted as a dynamoDb semi-json-string {"S": "id"}

global.WebSocket = require('ws');
require('es6-promise').polyfill();
require('isomorphic-fetch');
const AWS = require('aws-sdk');
const aws_exports = require('./aws-exports').default;
const AWSAppSyncClient = require('aws-appsync').default;

exports.handler = async (event, context, callback) => {
    if(!event.Records.length){
        console.log('no records');
        return false;
    }
    const AUTH_TYPE = require('aws-appsync/lib/link/auth-link').AUTH_TYPE;
    const url = aws_exports.ENDPOINT;
    const region = aws_exports.REGION;
    const type = AUTH_TYPE.AMAZON_COGNITO_USER_POOLS;

    AWS.config.update({
        region: aws_exports.REGION,
        credentials: new AWS.Credentials({
            accessKeyId: aws_exports.AWS_ACCESS_KEY_ID,
            secretAccessKey: aws_exports.AWS_SECRET_ACCESS_KEY
        })
    });
    const cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });

    const initiateAuth = async ({ clientId, username, password }) => cognitoIdentityServiceProvider.initiateAuth({
        AuthFlow: 'USER_PASSWORD_AUTH',
        ClientId: clientId,
        AuthParameters: {
            USERNAME: username,
            PASSWORD: password,
        },
    }).promise();
    const { AuthenticationResult } = await initiateAuth({
        clientId: '*******',
        username: '*******',
        password: '*******',
    });
    const accessToken = AuthenticationResult && AuthenticationResult.AccessToken;

    // Import gql helper and craft a GraphQL query
    const gql = require('graphql-tag');
    const query = gql(`
    mutation faceAdded($input: UpdateFaceInput!) {
        updateFace(input: $input) { 
            RekognitionId
            Emotions {
                Confidence
                Type
            }
            AgeRange
            Beard
            Mustache
            Gender
            Eyeglasses
        }
    }`);
    // Set up Apollo client
    const client = new AWSAppSyncClient({
        url: url,
        region: region,
        auth: {
            type: type,
            jwtToken: accessToken,
        },
        disableOffline: true      //Uncomment for AWS Lambda
    });
    return runMutationInClient(client, query, event.Records[0], (res) => {
        return res;
    });
}

const runMutationInClient = async (client, query, RekognitionObj, callback) => {
    await client.hydrated().then( async (client) => {
        console.log('Running mutation in Lambda: ', query, 'RekognitionId: ', RekognitionObj);

        var inputObj = AWS.DynamoDB.Converter.unmarshall(RekognitionObj.dynamodb.NewImage)

        await client.mutate({
            mutation: query,
            variables: {
                input: {
                    RekognitionId: {"S": inputObj.RekognitionId},
                    Emotion: {"L": []}
                }
            },
            fetchPolicy: 'no-cache'
        }).then(function logData(data) {
            console.log('LAMBDA results of query: ', data);
            callback(data);
        })
        .catch((error)=>{
            console.log('LAMBDA ERROR:', error);
            callback(error);
        });

    });
};
like image 247
Jesper Bylund Avatar asked Mar 13 '19 21:03

Jesper Bylund


2 Answers

I have seen this error a lot (sadly!) and it is poorly worded as it bears no reference to mapping the way we might think. The error is that your graphql connection string is badly formed or it has a key piece of data missing.

There are two solutions. The first is to try out the graphql in a sandbox of some kind AWS Amplify and Apollo both have them in the console. In the case of AWS you can also mock your database and run it from your machine - although you need to create some data in your mocked environment - and clear it before you progress if you are developing on a local machine as the two sets of data - real and mock get intertwined! Yup, lost a few hours on that one!

The other solution is simply to console out the data string, whatever data you are relying on in the query to make sure the data is present. As occasionally it all looks good, but that error tends to occur when data is missing.

You can test the data. Get a good data string and then deliberately take the required! element out or another and you will at some point get the mapping error. This refers to the graphql not being able to map the data, rather than the javascript meaning of map.

like image 146
David White Avatar answered Nov 19 '22 12:11

David White


I had the same error this week and lots a lot of time. The cause for me was just checking what I was passing in as the variable to my GraphQL mutation. First of all I was passing in a number rather than an object (ie I was accidently passing the key while doing a for x in loop) and then secondly I was doing a JSON.stringify on the input variable for my mutation. Those together caused lots of head scratching. Adding it here just in case anyone else comes across this error. CHECK WHAT YOU ARE PASSING IN!

like image 39
Pete Duncanson Avatar answered Nov 19 '22 13:11

Pete Duncanson