I have access to
com.amazonaws.services.lambda.runtime.Context;
object and by extension the invoked function Arn. The arn contains the account Id where the lambda resides.
My question is simple, I want the cleanest way to extract the account Id from that.
I was taking a look
com.amazon.arn.ARN;
It has a whole bunch of stuff, but no account ID (which i presume is due to the fact that not all arns have account ids ?)
I want to cleanly extract the account Id, without resorting to parsing the string.
If your lambda is being used as an API Gateway proxy lambda, then you have access to event.requestContext.accountId (where event is the first parameter to your handler function).
Otherwise, you will have to split the ARN up.
From the AWS documentation about ARN formats, here are the valid Lambda ARN formats:
arn:aws:lambda:region:account-id:function:function-name
arn:aws:lambda:region:account-id:function:function-name:alias-name
arn:aws:lambda:region:account-id:function:function-name:version
arn:aws:lambda:region:account-id:event-source-mappings:event-source-mapping-id
In all cases, account-id is the 5th item in the ARN (treating : as a separator). Therefore, you can just do this:
String accountId = arn.split(":")[4];
You no longer need to parse the arn anymore, sts library has introduced get_caller_identity for this purpose.
Its an overkill, but works!.
Excerpts from aws docs.
python
import boto3
client = boto3.client('sts')
response = client.get_caller_identity()['Account']
js
/* This example shows a request and response made with the credentials for a user named Alice in the AWS account 123456789012. */
var params = {
};
sts.getCallerIdentity(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
/*
data = {
Account: "123456789012",
Arn: "arn:aws:iam::123456789012:user/Alice",
UserId: "AKIAI44QH8DHBEXAMPLE"
}
*/
});
More details here & here
I use this:
ACCID: { "Fn::Join" : ["", [{ "Ref" : "AWS::AccountId" }, "" ]] }
golang
import (
"github.com/aws/aws-lambda-go/lambdacontext"
)
func Handler(ctx context.Context) error {
lc, ok := lambdacontext.FromContext(ctx)
if !ok {
return errors.Errorf("could not get lambda context")
}
AwsAccountId := strings.Split(lc.InvokedFunctionArn, ":")[4]
Related
I would like to add conditional function on context depending on which mutation is being called in Node.js, Apollo Server.
How would I do that efficiently?
When configuring context, I have access to the request body and all the graphql request information is stored at “req.body.query”, which also contains what I need.
To make use of this, I would have to parse with the “parse” function from “graphql” module. But I dont think this is efficient, cuz now the same request is basically getting parsed twice(once by me and once by Apollo Server). Also the parsed result is not very user friendly with all the nested values.
Is there a clean way to know what mutation is being requested?
One common solution to this is the use of operation names. The client can provide an operation name to the server in the request to identify what it is doing.
{
"query": "{ mutation AddToBasket($myVariable: AddToBasketInput!) { addToBasket(input: $myVariable) { id } } }",
"operationName": "AddToBasket",
"variables": { "myVariable": "someValue" }
}
I think you can access the operation name from the body like this:
const server = new ApolloServer({
context: ({ req }) => {
if (req.body.operationName === 'AddToBasket') {
console.log("Found it!")
}
}
})
You can also create a plugin for Apollo Server that uses the didResolveOperation event to get the operation name value.
export class MyPlugin implements ApolloServerPlugin {
async requestDidStart() {
return {
async didResolveOperation(
requestContext
) {
console.log(requestContext.operationName);
},
}
}
}
Using operation name avoids a need to parse the GraphQL operation to determine which mutation is included in the request, but also requires the client to provide a known operation name value, which may or may not work for your use case.
So I am making an app and need AWS API Gateway. I want to use HTTP API instead of REST API. My code looks like this
package main
import (
"database/sql"
"fmt"
"strings"
"github.com/aws/aws-lambda-go/lambda"
_ "github.com/lib/pq"
)
here I make a connection to the database
func fetch(inte string, input string) string {
if err != nil {
panic(err)
}
switch inte {
case "00":
{
res = append(res, response)
}
switch len(res) {
case 0:
return "401"
}
case "01":
}
switch len(res) {
case 0:
return "402"
}
}
return "404"
}
type LambdaEvent struct {
Req string `json:"req"`
Num string `json:"num"`
}
type LambdaResponse struct {
Res string `json:"res"`
}
func LambdaHandler(event LambdaEvent) (LambdaResponse, error) {
res := fetch(event.Num, event.Req)
return LambdaResponse{
Res: res,
}, nil
}
func main() {
lambda.Start(LambdaHandler)
}
So as you see this is not the full code. I make a connection to the database and and work with the requests string query. so I tried the same with http api but it just gives me the 404 meaning the http api doesn't pass the query string to the lambda so how do I make my api pass the data to the lambda. Rest api works HTTP doesn't.
Thanks for any help.
I'm not familiar with Serverless Frameworks for APIGW, but manipulating QueryString parameters is built into the APIGW Console. Just login to AWS and search for APIGateway. Edit your HTTP API and select Integrations from the menu on the left. Select the integration that maps to your Lambda function and Edit the Parameter Mappings on the right
If you are deploying your lambdas and api-gateway with serverless framework you can do something like this:
hello:
handler: src/hello.handler
name: hello
events:
- http:
path: car/{id}/color/{color}
method: get
Assuming you are planning to use Lambda Proxy Integration in API Gateway, here are the changes that needs to be done to access the query parameters.
import github.com/aws/aws-lambda-go/events (This has all the relevant structs)
Change the lambda handler to func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
Now you can access the query parameters as a Map at request.QueryStringParameters and execute your selection logic
When you return a response for API Gateway, ensure you follow the events.APIGatewayProxyResponse struct i.e. at least return a status code along with optional body, headers etc.
No changes/config required at the API Gateway to pass the query parameters with Lambda proxy integration
You can use your own structs for request and response, but they need to use the appropriate keys as defined in the events.APIGatewayProxyRequest and events.APIGatewayProxyResponse.
e.g. add the following in LambdaEvent struct to access the query string parameters.
QueryStringParameters map[string]string `json:"queryStringParameters"`
If you are getting started with AWS Lambda, have a look at AWS SAM to keep things simple.
I have a problem I don't know how to solve properly.
I'm working on a project where we use a graphql server to communicate with different apis. These apis are old and very difficult to update so we decided to use graphql to simplify our communications.
For now, two apis allow me to get user data. I know it's not coherent but sadly I can't change anything to that and I need to use the two of them for different actions. So for the sake of simplicity, I would like to abstract this from my front app, so it only asks for user data, always on the same format, no matter from which api this data comes from.
With only one api, the resolver system of graphql helped a lot. But when I access user data from a second api, I find very difficult to always send back the same object to my front page. The two apis, even though they have mostly the same data, have a different response format. So in my resolvers, according to where the data is coming from, I should do one thing or another.
Example :
API A
type User {
id: string,
communication: Communication
}
type Communication {
mail: string,
}
API B
type User {
id: string,
mail: string,
}
I've heard a bit about apollo-federation but I can't put a graphql server in front of every api of our system, so I'm kind of lost on how I can achieve transparency for my front app when data are coming from two different sources.
If anyone has already encounter the same problem or have advice on something I can do, I'm all hear :)
You need to decide what "shape" of the User type makes sense for your client app, regardless of what's being returned by the REST APIs. For this example, let's say we go with:
type User {
id: String
mail: String
}
Additionally, for the sake of this example, let's assume we have a getUser field that returns a single user. Any arguments are irrelevant to the scenario, so I'm omitting them here.
type Query {
getUser: User
}
Assuming I don't know which API to query for the user, our resolver for getUser might look something like this:
async () => {
const [userFromA, userFromB] = await Promise.all([
fetchUserFromA(),
fetchUserFromB(),
])
// transform response
if (userFromA) {
const { id, communication: { mail } } = userFromA
return {
id,
mail,
}
}
// response from B is already in the correct "shape", so just return it
if (userFromB) {
return userFromB
}
}
Alternatively, we can utilize individual field resolvers to achieve the same effect. For example:
const resolvers = {
Query: {
getUser: async () => {
const [userFromA, userFromB] = await Promise.all([
fetchUserFromA(),
fetchUserFromB(),
])
return userFromA || userFromB
},
},
User: {
mail: (user) => {
if (user.communication) {
return user.communication.mail
}
return user.mail
}
},
}
Note that you don't have to match your schema to either response from your existing REST endpoints. For example, maybe you'd like to return a User like this:
type User {
id: String
details: UserDetails
}
type UserDetails {
email: String
}
In this case, you'd just transform the response from either API to fit your schema.
I'm trying to customize the email that AWS Cognito sends if a user has forgotten their password.
It requires {####} placeholder for the verification code in the email message. For example, if you do
event['response']['emailMessage'] = "Your code is {####}", you'll receive a message Your code is 123456.
Here's an example of my AWS Lambda function:
def custom_message_handler(event, context):
event['response']['emailSubject'] = 'Custom subject'
event['response']['emailMessage'] = 'Custom email'
# verification_code = event[...] ???
return event
It seems like Cognito generates the verification code after your lambda returned the message with the placeholder. Is it possible to get the verification code inside your lambda to use it?
Amazon Cognito's Custom Message Lambda Trigger's Event JSON does not get the numerical verification code. The data of the Event available to the trigger, as stated in the official documentation is stated as follows:
{
"version": 1,
"triggerSource": "CustomMessage_AdminCreateUser",
"region": "<region>",
"userPoolId": "<userPoolId>",
"userName": "<userName>",
"callerContext": {
"awsSdk": "<calling aws sdk with version>",
"clientId": "<apps client id>",
...
},
"request": {
"userAttributes": {
"phone_number_verified": false,
"email_verified": true,
...
},
"codeParameter": "####",
"usernameParameter": "username"
},
"response": {
"smsMessage": "<custom message to be sent in the message with code parameter and username parameter>"
"emailMessage": "<custom message to be sent in the message with code parameter and username parameter>"
"emailSubject": "<custom email subject>"
}
}
You would be able to use Cognito data in a Lambda trigger only if it is available in an Event, or if there is a separate API call for the same. But given Amazon Cognito's design, this does not seem to be possible.
What is a common pattern to have AWS AppSync resolvers cache their output?
I'm writing an API that fronts data that will not change at all over time. The API returns the contents of books (title, author, chapters, etc.).
My initial idea was to have the resolver request some JSON payload from CloudFront. If the requested document is not in CloudFront, CloudFront would trigger a Lambda function, which would know how to fetch the JSON document (from a database), then put the payload in CloudFront. This seems weird conceptually, but it would solve the caching problem.
Example
const query = `{
bookById(bookID: "468c95") {
bookID
title
author
chapters {
title
text
}
}
}`;
const book = query(query);
// book => {
// bookId: "468c95",
// title: "AppSync for Normal People",
// author: null,
// chapters: [
// {
// title: "Chapter 1: Dawn of Men",
// text: [
// "It was the best of times, it was the worst of times.",
// "..."
// ]
// },
// { ... }
// ]
// }
}
In other words, calling the fictitious query method will trigger some resolver in AppSync. That resolver will absolutely always return the same data. Thus, why not have the data that the resolver works with (I guess you can view that as the input to the resolver) be cached in CloudFront so it can be served from memory instead of having to hit some backend storage (like a database) or trigger a Lambda?!