I'm trying to get the userId during the Alexa Discovery.
This param is not provided by Alexa SmartHome skill (unlike how an Alexa Custom Skill does), so I'm trying to get it using the access token.
This is an example of Alexa.Discovery request:
{
"directive": {
"header": {
"namespace": "Alexa.Discovery",
"name": "Discover",
"payloadVersion": "3",
"messageId": "1bd5d003-31b9-476f-ad03-xxxx"
},
"payload": {
"scope": {
"type": "BearerToken",
"token": "eyJraWQiOiJGcSt4RDQyZDdxxxx..."
}
}
}
}
In other answers ( such as this Logon with Amazon for Alexa Skills (LWA) ) the token in the payload is used to retrieve user profile through the https://api.amazon.com/user/profile endpoint.
My request is like:
var config = {
method: 'get',
url: 'https://api.amazon.com/user/profile',
headers: {
'Authorization': 'Bearer eyJraWQiOiJGcSt4RDQyZDdxxxx...'
}
};
Every time I get an error 400
{
"error_description": "The request has an invalid parameter : access_token",
"error": "invalid_token"
}
I'm sure that the token is still valid when I execute the request.
My skill uses account linking and my oauth provider is Amazon Cognito.
Any idea?
Related
I have a test helper function createUser that makes an HTTP request to the backend API to create a user. The backend API calls a method on Auth0 client to create a user. The responses I have are set for Auth0 client URL path /api/v2/users.
I am testing a service that has two types of users - an owner of the account and the invitee user. I am calling a helper twice before all the tests:
const owner = await createUser('owner#email.com');
const invitee = await createUser('invitee#email.com');
And I expect owner to have owner#email.com as an email and an invitee#email.com email for invitee .
In order to return two different responses I used Wiremock scenarios. And currently I have the responses like this:
User #1
{
"scenarioName": "user-scenario",
"requiredScenarioState": "Started",
"newScenarioState": "owner-user",
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "owner#email.com"
}
}
}
User #2
{
"scenarioName": "user-scenario",
"requiredScenarioState": "owner-user",
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "invitee#email.com"
}
}
}
When I run the tests Wiremock returns the second response for both createUser calls. The second response was created after the first and Wiremock prioritizes the most recently created response. I decided to set the priority: 1 on the first response to force Wiremock to return it and therefore change the scenario state. That worked only on the first test run but not for the subsequent ones.
Sometimes if I delete the Wiremock Docker image and start the container from scratch it returns the responses as expected but then continues returning the second response for both calls.
What am I doing wrong and if it's not how I set up the scenarios what could be the possible reasons for such inconsistency?
Multiple ways to skin this cat - third (response templating) is probably best.
Understanding Scenarios
The first request moves the scenario into state owner-user, and after that all requests will return user #2 for the lifetime of the WireMock instance - in your case, the docker container - unless it is reset.
You can reset it as so: PUT /__admin/scenarios/user-scenario/Started.
The state of a scenario is held in memory, so restarting the container should also reset the state to Started.
See WireMock | Stateful Behaviour | Resetting a single scenario
Using better matching to avoid scenarios
You may not need to use scenarios at all. You can use the request body of the POST to decide which stub to call, based on the email address you are sending.
Assuming your request payload looks like this:
{
"email": "owner#email.com"
}
You can match on the request body as so:
User #1
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users",
"bodyPatterns": [
{
"matchesJsonPath": {
"expression": "$.email",
"equalTo": "owner#email.com",
}
}
]
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "owner#email.com"
}
}
}
User #2
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users",
"bodyPatterns": [
{
"matchesJsonPath": {
"expression": "$.email",
"equalTo": "invitee#email.com",
}
}
]
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "invitee#email.com"
}
}
}
See WireMock | Request Matching | JSON Path for details.
Using response templating to reduce number of stubs
You can use values passed to WireMock in the request in the response using response templating as so:
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"transformers": ["response-template"],
"jsonBody": {
"email": "{{jsonPath request.body '$.email'}}"
}
}
}
See WireMock | Response Templating | JSONPath helper for details.
There are also a variety of helpers for generating random data in various formats should you need each request to return some different value (e.g. a UUID) - see WireMock | Response Templating | Random value helper for details.
I am running into an issue with posting a json doc via request from my nodeJs app.
The problem is for me how to pass the json doc to post. Here is what works for me
const options = {
url: 'http://localhost/message/send/981f1507-2df2-4011-8f55-5460897fcde6',
method: 'POST',
json: true,
body: { "message": {
"subject": "Meet for lunch?",
"body": {
"contentType": "html",
"content": "</p>\r\n\r\n<ul type=\"disc\">\r\n\t<li><strong>Ready to Use Experiences</strong> – Get instant access to our fully configured and integrated environments allowing you to use and experience the products as a customer would in their own deployment. We’ve set up the products alongside partner solutions so you can experience the entire ecosystem including Office 365, Salesforce, and more. </li>\r\n</ul>"
},
"toRecipients": [
{
"emailAddress": {
"address": "test#ueser.com"
}} ]}}}
request(options, function(err, res, body) {
console.log(res.statusCode)
})
the problem i have is if i want to pass the body from a string like this
var mybody = `{ "message": {
"subject": "Meet for lunch?",
"body": {
"contentType": "html",
"content": "This is a test"
},
"toRecipients": [
{
"emailAddress": {
"address": "test#user.com"
}
}]}}`
and then call it like this
const options = {
url: 'http://localhost/message/send/981f1507-2df2-4011-8f55-5460897fcde6',
method: 'POST',
json: true,
body: mybody
}
request(options, function(err, res, body) {
console.log(res.statusCode)
})
which throws errors like
SyntaxError: Unexpected token " in JSON at position 0
Have you tried using stringify? Node.js has a a built in module called querystring.
const querystring = require("querystring");
const myStringBody = querystring.stringify(myBody);
Then write it to your request.
req.write(myStringBody);
In an AWS lambda written in Node.js, I want to extract the following part of a URL when I do a GET call through the API gateway:
/devices/{id} --> {id} will be replaced by a value, and that is the value I want!
I know that to get QueryStringParameters you just use
event.queryStringParameters.[parameter name]
But how will I do this for path parameters, like for {id} above.
Also is there a good place where I can comprehensively learn about writing lambdas for APIs in Node.js?
Short answer:
const { id } = event.pathParameters;
I recently released a short training video that demonstrates in detail how to create API Gateway REST APIs and integrate them with AWS Lambda (NodeJS). Please check it out here:
Serverless Architecture: AWS API Gateway & Lambda
I'm assuming you are using lambda proxy here i'm pasting the event object sample for lambda proxy.
{
"message": "Good day, John of Seattle. Happy Friday!",
"input": {
"resource": "/{proxy+}",
"path": "/Seattle",
"httpMethod": "POST",
"headers": {
"day": "Friday"
},
"queryStringParameters": {
"time": "morning"
},
"pathParameters": {
"proxy": "Seattle"
},
"stageVariables": null,
"requestContext": {
"path": "/{proxy+}",
"accountId": "123456789012",
"resourceId": "nl9h80",
"stage": "test-invoke-stage",
"requestId": "test-invoke-request",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": "123456789012",
"cognitoIdentityId": null,
"caller": "AIDXXX...XXVJZG",
"apiKey": "test-invoke-api-key",
"sourceIp": "test-invoke-source-ip",
"accessKey": "ASIXXX...XXDQ5A",
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": "arn:aws:iam::123456789012:user/kdeding",
"userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_131)",
"user": "AIDXXX...XXVJZG"
},
"resourcePath": "/{proxy+}",
"httpMethod": "POST",
"apiId": "r275xc9bmd"
},
"body": "{ \"callerName\": \"John\" }",
"isBase64Encoded": false
}
}
the path can be extracted from "path" key in event object, it can be accessed from event.path and after that you can use string manipulation function to further manipulate it.
I hope it helps !
Use brackets in the resource path, as image above. Then in node.js user the code below:
exports.handler = async function(event) {
let serviceId = event.pathParameters.id;
}
The solution is very similar to what you mentioned in the first place. Just use event.pathParameters instead of event.queryStringParameters.
Here I'm using /api/test/{id} as my resource path and using Lambda Proxy integration. I'm getting the following event when I hit https://www.dummyapi.com/dev/api/test/id-123456
This has taken me awhile to figure out on my own so hoping this helps someone. After defining the Path Parameter on the Resource in API Gateway.
AWS Guide including PathParameter Steps: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-step-by-step.html
If using CloudFormation: https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-proxy-path-parameter-error/
My path /user/{userId}
My Path Parameter: userId
Then in my lambda function you can access the path via event.path which will have your parameters in object form: path: { userId: 9812 } }. Lots of documentation has it labeled as pathParameters but for whatever reason with this setup it doesn't come through that way.
export const getUser = async (event) => {
console.log(event);
const { userId } = event.path;
}
serverless.yaml
events:
- http:
path: user/{userId}
method: get
request:
parameters:
paths:
userId: true
integration: lambda
I have successful setup the elasticsearch x-pack security. And I am planning to use the generated token to limit requests.
I generated token following this page:
https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-tokens.html
And tested it in curl:
curl -H "Authorization: Bearer k7vvAiCHUtrYVNNuLFPv***************EOIXQi+L7FevycU=" http://localhost:9200/plugin/database/_search?q=*:*
It is working well. But when I try to use the same token in my node.js controller (using elasticsearch.js), I do not know where I should put it, for instance in "client.index":
return client.index({
index: indexName,
type: typeName,
body: {
authorization: "Bearer k7vvAiCHUtrYV*****XQi+L7FevycU=",
workspace: content
}
});
It does not work, and returning error msg:
<- 401
{
"error": {
"root_cause": [
{
"type": "security_exception",
"reason": "missing authentication token for REST request [/plugin/database]",
"header": {
"WWW-Authenticate": "Basic realm=\"security\" charset=\"UTF-8\""
}
}
],
"type": "security_exception",
"reason": "missing authentication token for REST request [/plugin/database]",
"header": {
"WWW-Authenticate": "Basic realm=\"security\" charset=\"UTF-8\""
}
},
"status": 401
}
So how should I pass this generated token in my node.js request to elasticsearch?
I'm actually implementing Social sharing in my project, so i am doing google+ sharing. While posting message to Google+ using nodejs,I'm getting 403 forbidden error do i need to configure anything on google+ account so that posted message is seen at google+
var params = { "object": {
"originalContent": "hello"
},
"access": {
"items": [
{
"type": "mycircle"
}
],
"domainRestricted": true
}
};
var headers = {
Authorization: 'Bearer ' + google_access_token
};
request.post(shareApiUrl, {
url: 'https://www.googleapis.com/plusDomains/v1/people/{{userid}}/activities',
headers: headers,
body: params,
json: true
}, function (err, response, body) {
console.log(body)
)}
Error Description:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Forbidden"
}
],
"code": 403,
"message": "Forbidden"
}
}
For Posting a feed/or message we should have Google Apps account, but it is impossible with regular GMail account, or your Apps admin hasn't enabled Google+ for the Apps domain.