Failed use apigwManagementApi.postToConnection in $connect route - amazon-web-services

I want to return connectionId to a client after the client connect to aws websocket.
I'm using apigwManagementApi.postToConnection to send a response to a client, but I always get an absurd error message.
I already try to debug & search in google, but I can't find a solution for this.
patch.js
require('aws-sdk/lib/node_loader');
var AWS = require('aws-sdk/lib/core');
var Service = AWS.Service;
var apiLoader = AWS.apiLoader;
apiLoader.services['apigatewaymanagementapi'] = {};
AWS.ApiGatewayManagementApi = Service.defineService('apigatewaymanagementapi', ['2018-11-29']);
Object.defineProperty(apiLoader.services['apigatewaymanagementapi'], '2018-11-29', {
get: function get() {
var model = {
"metadata": {
"apiVersion": "2018-11-29",
"endpointPrefix": "execute-api",
"signingName": "execute-api",
"serviceFullName": "AmazonApiGatewayManagementApi",
"serviceId": "ApiGatewayManagementApi",
"protocol": "rest-json",
"jsonVersion": "1.1",
"uid": "apigatewaymanagementapi-2018-11-29",
"signatureVersion": "v4"
},
"operations": {
"PostToConnection": {
"http": {
"requestUri": "/#connections/{connectionId}",
"responseCode": 200
},
"input": {
"type": "structure",
"members": {
"Data": {
"type": "blob"
},
"ConnectionId": {
"location": "uri",
"locationName": "connectionId"
}
},
"required": [
"ConnectionId",
"Data"
],
"payload": "Data"
}
}
},
"shapes": {}
}
model.paginators = {
"pagination": {}
}
return model;
},
enumerable: true,
configurable: true
});
module.exports = AWS.ApiGatewayManagementApi;
index.js
const AWS = require('aws-sdk');
require('./patch.js');
exports.handler = async(event) => {
const connectionId = event.requestContext.connectionId;
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
});
await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: connectionId }).promise();
return {};
};
client.js
const WebSocket = require('ws');
const ws = new WebSocket('wss://****');
ws.on('open', () => {
console.log('connected ===================>')
ws.on('message', data => console.warn(`From server: ${data}`));
});
Error in cloudwatch
{
"errorMessage": "410",
"errorType": "UnknownError",
"stackTrace": [
"Object.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:48:27)",
"Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/rest_json.js:52:8)",
"Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:105:20)",
"Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:77:10)",
"Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:683:14)",
"Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)",
"AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)",
"/var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10",
"Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)",
"Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:685:12)"
]
}
I don't know why, but if I'm trying in a custom route, this code can work.
Does anyone know how to solve this?

I'd suggest to look into this example from AWS, there is on connect response for subprotocol confirmation, but I think any payload can be provided.
The most important bit is the route integration settings in the template, basically, the following two lines in the route integration properties:
IntegrationMethod: POST
ConnectionType: INTERNET
then response will be sent to the connected client.

The only way I've found to make this work is to use a DynamoDB table to store connections, then set up a trigger from the table back to a Lambda function.
There are a few catches though. This Lambda function wont work like your index.js file above. You'll have to use NPM install --save aws-sdk on a folder with your index.js file, zip it and upload it to the lambda function, so that the SDK is localized.
You will also need to set up a user with proper access and put the credentials into a your Lambda function.
Note, if you see a 410 error, that means the connection is no longer there, so you're going in the right direction at that point.
const AWS = require('aws-sdk');
require('./patch.js');
var log = console.log;
AWS.config.update({
accessKeyId: "YOURDATAHERE",
secretAccessKey: "YOURDATAHERE"
});
let send = undefined;
function init() {
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint: "HARDCODEYOURENDPOINTHERE"
});
send = async (connectionId, data) => {
await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: `${data}` }).promise();
}
}
exports.handler = async (event, context) => {
init();
console.log('Received event:', JSON.stringify(event, null, 2));
for (const record of event.Records) {
//console.log(record.eventID);
console.log(record.eventName);
console.log('DynamoDB Record: %j', record.dynamodb);
if(record.eventName == "INSERT"){
var connectionId = record.dynamodb.NewImage.connectionId.S;
try{
await send(connectionId, connectionId);
}catch(err){
log("Error", err);
}
log("sent");
}
}
return `Successfully processed ${event.Records.length} records.`;
};

Related

Not receiving SMS when sending it through SNS in lambda

I am sending the OTP using AWS SNS service when in a lambda function my code is this
const otp = generateOTP();
const message = `Your XXXX Verification Code is ${otp} .`;
var params = {
Message: message,
PhoneNumber: "+91xxxxxxxxxx",
};
AWS.config.update({region: 'ap-south-1'});
var publishTextPromise = new AWS.SNS({apiVersion: '2022-04-01'}).publish(params).promise();
return new Promise((resolve, reject) => {
publishTextPromise.then((data) => {
console.log("MessageID is " + data.MessageId);
const response = {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
};
console.log('Server response function');
resolve(response);
}).catch((error) => { reject(Error(error)); });
});
and i am getting this response when invoking this lambda function
Server response function
{
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"ResponseMetadata": {
"RequestId": "29796d67-f32e-5e42-a761-37a72809164d"
},
"MessageId": "2f8ffee0-7e0d-5cd3-a5b0-a420a52a14dc"
}
}
But I am not receiving the SMS on that number I don't know what is the problem here cause the I am getting resolved response in the console which means the SMS is send but I am not received it.
Do I have to config the sns manual in aws sns mobile -> text messaging or something??
or is their any other way to send verification otp ?

finding user on aws cognito throws error cognitoidentityserviceprovider is not defined

I am new to AWS services and I am trying out various services provided by AWS. I have this lambda function on which I am trying to find a user with the phone number on AWS Cognito -
const AWS = require('aws-sdk');
async function findCognitoUser(userId) {
console.log('CognitoUser-FindCognitoUser');
var params = {
UserPoolId: 'poolId',
AttributesToGet: [
'phone_number','displayName'
],
Filter: `phone_number = \"${userId}\"`,
};
cognitoidentityserviceprovider.listUsers(params, function(err, data) {
if (err) {
return 'ERROR OCCURED';
}
else {
return data.Users[0].displayName;
}
});
}
module.exports = findCognitoUser;
I do get an error saying cognitoidentityserviceprovider is not defined
Here is the error on CloudWatch -
"errorType": "ReferenceError",
"errorMessage": "cognitoidentityserviceprovider is not defined",
"stack": [
"ReferenceError: cognitoidentityserviceprovider is not defined",
" at findCognitoUser (/var/task/user-queries/findCognitoUser.js:13:5)",
" at Runtime.exports.handler (/var/task/index.js:14:19)",
" at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
CODE UPDATED -
const AWS = require('aws-sdk');
async function findCognitoUser(userId) {
console.log(userId);
var csp = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
var params = {
UserPoolId: 'poolid',
AttributesToGet: [
'phone_number','given_name'
],
Filter: `phone_number = \"${userId}\"`,
};
csp.listUsers(params, function(err, data) {
console.log(data);
if (err) {
return 'ERROR OCCURED';
}
else {
return data.Users[0].given_name;
}
});
}
module.exports = findCognitoUser;

loopback rest connection headers authorization

I'm trying to access the Shopify Orders API in a Loopback application. I have the following data source:
"ShopifyRestDataSource": {
"name": "ShopifyRestDataSource",
"connector": "rest",
"operations": [{
"template": {
"method": "GET",
"url": "https://mystore.myshopify.com/admin",
"headers": {
"accepts": "application/json",
"content-type": "application/json"
}
},
"headers": {
"Authorization": "Basic MzdiOD..."
},
"functions": {
"find": []
}
}]
}
And then I attempt a simple call:
var ds = app.dataSources.ShopifyRestDataSource;
ds.find(function(err, response, context) {
if (err) throw err;
if (response.error) {
next('> response error: ' + response.error.stack);
}
console.log(response);
next();
});
I'm getting the following exception message:
Error: {"errors":"[API] Invalid API key or access token (unrecognized login or wrong password)"}
at callback (/order-api/node_modules/loopback-connector-rest/lib/rest-builder.js:529:21)
The Shopify API authenticates by basic HTTP authentication and I'm sure my request works since the same data works with curl. What am I doing wrong?
I couldn't find the "Loopback way" to do this and I couldn't wait, so I just wrote a simple https Node call. I'll paste this in here but I won't accept it as the answer. I'm still hoping someone will provide the right answer.
let response;
const options = {
hostname: 'mystore.myshopify.com',
port: 443,
path: '/admin/orders.json',
method: 'GET',
auth: `${instance.api_key}:${instance.password}`
};
const req = https.request(options, (res) => {
res.setEncoding('utf8');
let body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
let jsonResponse = JSON.parse(body);
// application logic goes here
response = 'ok';
});
});
req.on('error', (e) => {
response = e.message;
});
req.end();

"Converting circular structure to JSON" while reading data from MYSQL and putting it in DynamoDB

I am reading data from read replica of Aurora and putting it into the DynamoDB. I getting data from RDS correctly but facing problems while inserting it into DynamoDB. I am sharing my code and error message please guide me....
var AWS = require("aws-sdk");
var mysql = require('mysql');
const client = new AWS.DynamoDB.DocumentClient({region : 'eu-west-1'});
var connection = mysql.createPool({
host : "**********",
user : "****",
password : "*****",
database : "mydb",
port : "3306"
});
exports.handler = (event, context, callback) => {
connection.query('select * from demo where id=2;', function (error, results, fields) {
if (error) {
if (error) throw error;
} else {
var data = {results};
var params = {
Item:{
test_id:data.id,
name:data.name,
result:data.result
},
TableName: 'demo_rds_dynamoDB'
};
connection.end(function (err) {
callback(err, client.put(params, function(error,data){
if(error){
callback(error,null);
}else{
callback(null,data);
}
})
);
});
}
});
};
Following is the data I am getting from Aurora and I want to put in DynamoDB
Response:
[
{
"id": 2,
"name": "Display",
"result": "Pass"
}
]
And the response m getting is as follow and sometimes i get error as Cannot read property "id" undefined,
Response:
{
"errorMessage": "Converting circular structure to JSON",
"errorType": "TypeError",
"stackTrace": []
}
Request ID:
"896a64e3-573e-11e8-bb94-67c4be71eb3d"
Function Logs:
START RequestId: 896a64e3-573e-11e8-bb94-67c4be71eb3d Version: $LATEST
Unable to stringify response body as json: Converting circular structure to JSON: TypeError
at Object.stringify (native)
Item:{
test_id:results[0].id,
name:results[0].name,
result:results[0].result
}
results[0].columnname

Searching DynamoDB for non primary keys and integrating into Alexa Skills

I am trying to search a non primary key using AWS Lambda and integrating it into the Alexa Skills Kit. I am very new to using DynamoDB and Alexa Skills Kit and I'm struggling to find any solutions to this online. The basic premise for what I am trying to do is querying the table yesno with two columns, id and message. Only looking through the message column to find a match with the text i specify in params.
Here is the Lambda code I am working with:
const AWSregion = 'eu-west-1';
const Alexa = require('alexa-sdk');
const AWS = require('aws-sdk');
//params for searching table
const params = {
TableName: 'yesno',
Key:{ "message": 'Ben Davies' }
};
AWS.config.update({
region: AWSregion
});
exports.handler = function(event, context, callback) {
var alexa = Alexa.handler(event, context);
// alexa.appId = 'amzn1.echo-sdk-ams.app.1234';
// alexa.dynamoDBTableName = 'YourTableName'; // creates new table for session.attributes
alexa.registerHandlers(handlers);
alexa.execute();
};
const handlers = {
'LaunchRequest': function () {
this.response.speak('welcome to magic answers. ask me a yes or no question.').listen('try again');
this.emit(':responseReady');
},
'MyIntent': function () {
var MyQuestion = this.event.request.intent.slots.MyQuestion.value;
console.log('MyQuestion : ' + MyQuestion);
readDynamoItem(params, myResult=>{
var say = MyQuestion;
say = myResult;
say = 'you asked, ' + MyQuestion + '. I found a reckord for: ' + myResult;
this.response.speak(say).listen('try again');
this.emit(':responseReady');
});
},
'AMAZON.HelpIntent': function () {
this.response.speak('ask me a yes or no question.').listen('try again');
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak('Goodbye!');
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.response.speak('Goodbye!');
this.emit(':responseReady');
}
};
// END of Intent Handlers {} ========================================================================================
// Helper Function =================================================================================================
function readDynamoItem(params, callback) {
var AWS = require('aws-sdk');
AWS.config.update({region: AWSregion});
var dynamodb = new AWS.DynamoDB();
console.log('reading item from DynamoDB table');
dynamodb.query(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else{
console.log(data); // successful response
callback(data.Item.message);
}
});
}
I know I am probably doing this completely wrong but there isn't much online for integrating DynamoDB with an Alexa Skill and the only thing i was able to find was searching by ID. This doesn't work for what i want to do without pulling all the items from the table into a map or a list, and seeing as I want to create a big database it seems quite inefficient.
On the Alexa side of things I am receiving the following service request when testing the code:
{
"session": {
"new": true,
"sessionId": "SessionId.f9558462-6db8-4bf5-84aa-22ee0920ae95",
"application": {
"applicationId": "amzn1.ask.skill.9f280bf7-d506-4d58-95e8-b9e93a66a420"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.AF5IJBMLKNE32GEFQ5VFGVK2P4YQOLVUSA5YPY7RNEMDPKSVCBRCPWC3OBHXEXAHROBTT7FGIYA7HJW2PMEGXWHF6SQHRX3VA372OHPZZJ33K7S4K7D6V3PXYB6I72YFIQBHMJ4QGJW3NS3E2ZFY5YFSBOEFW6V2E75YAZMRQCU7MNYPJUMJSUISSUA2WF2RA3CIIDCSEY35TWI"
}
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.7310073b-981a-41f8-9fa5-03d1b28c5aba",
"intent": {
"name": "MyIntent",
"slots": {
"MyQuestion": {
"name": "MyQuestion",
"value": "erere"
}
}
},
"locale": "en-US",
"timestamp": "2018-01-25T14:18:40Z"
},
"context": {
"AudioPlayer": {
"playerActivity": "IDLE"
},
"System": {
"application": {
"applicationId": "amzn1.ask.skill.9f280bf7-d506-4d58-95e8-b9e93a66a420"
},
"user": {
"userId": "amzn1.ask.account.AF5IJBMLKNE32GEFQ5VFGVK2P4YQOLVUSA5YPY7RNEMDPKSVCBRCPWC3OBHXEXAHROBTT7FGIYA7HJW2PMEGXWHF6SQHRX3VA372OHPZZJ33K7S4K7D6V3PXYB6I72YFIQBHMJ4QGJW3NS3E2ZFY5YFSBOEFW6V2E75YAZMRQCU7MNYPJUMJSUISSUA2WF2RA3CIIDCSEY35TWI"
},
"device": {
"supportedInterfaces": {}
}
}
},
"version": "1.0"
}
And I am receiving a service response error simply saying 'The response is invalid'
Any help with this would be greatly appreciated
I would like to help you in dynamo db part.
In order to access non primary key columns in dynamodb you should perform scan operation.
For your table (yesno), id is a primary key and message is an additional column.
Snippet to access non primary key column [Message]
var dynamodb = new AWS.DynamoDB();
var params = {
TableName: 'yesno',
FilterExpression: 'message = :value',
ExpressionAttributeValues: {
':value': {"S": "Ben Davies"}
}
};
dynamodb.scan(params, function(err, data) {
if (err) // an error occurred
else console.log(data); // successful response
});
Snippet to access primary key column [Id]
var docClient = new AWS.DynamoDB.DocumentClient();
//Get item by key
var params = {
TableName: 'sis_org_template',
Key: { "id": "1"}
};
docClient.get(params, function(err, data) {
if (err) // an error occurred
else console.log(data); // successful response
});