AppSync check if DynamoDB record exists - amazon-web-services

I am trying to write a resolver for AppSync that derives the value for a Boolean field based on the existence of a record in DynamoDB.
I currently have the following request mapping template:
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"field1": $util.dynamodb.toDynamoDBJson($ctx.args.field1),
"field2": $util.dynamodb.toDynamoDBJson($ctx.args.field2)
}
}
And the following response mapping template:
#if($util.isNull($ctx.result))
#set($exists = false)
#else
#set($exists = true)
#end
$util.toJson({
"field1": $ctx.args.field1,
"field2": $ctx.args.field2,
"exists": $exists
})
This works correctly if the record exists but if it does not then AppSync simply returns "null" for the entire API call and does not seem to evaluate the response mapping template at all. Is there any way I can instruct it not to do this?
Another option would be to perform a query and look at the length of the response but I have no idea how to check length in these templates.

This is an expected behavior for the 2017 version of the Request template. If you would like the $ctx.result to be evaluated, switch to the 2018 version as below:
{
"version": "2018-05-29",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
},
}
Refer to this change log for additional details.

Related

Appsync as Proxy to another Graphql server

I have a existing graphql server provied by 3rd party. I also have my own backend running on EC2 to provide APIs.
I'm trying to build the appsync with aws-cdk for connecting to both 3rd party graphql and my backend instance also.
With the graphql server, appsync will act as proxy to forward queries only. My questions are:
Do we have anyway to load remote schema and populate it in appsync along with its schema?
How can we forward the requests to another graphql server using aws-cdk? I'm trying something like this:
private get _requestMappingTemplate(): string {
return `
{
"version": "2018-05-29",
"method": "GET",
"resourcePath": $util.toJson("/graphql"),
"params": {
"headers": {
"Authorization": "Bearer $ctx.request.headers.Authorization"
},
"body": {
"query": "$util.escapeJavaScript($ctx.info.getSelectionSetGraphQL())"
}
}
}`;
}
But from the aws doc, getSelectionSetGraphQL returns string representation of the selection set, formatted as GraphQL schema definition language (SDL). Although fragments aren't merged into the selection set
Is that possible to setup AppSync for forwarding request to another GraphQL servers? Any best practice to follow?
It's quite a bit more complicated. I'm still working on it, and the best I got so far is given below. It still drops query arguments, so has limited use.
#* TODO: Add some more interesting info to the operation name, e.g. a timestamp *#
#set($operationName = $context.info.parentTypeName)
#set($payloadBody = {
"query": "$util.str.toLower($context.info.parentTypeName) $operationName { $context.info.fieldName $context.info.selectionSetGraphQL }",
"operationName": $operationName,
"variables": $context.info.variables
})
{
"version" : "2018-05-29",
"operation": "Invoke",
"payload":{
"path": "/graphql",
"httpMethod": "POST",
"headers": $util.toJson($ctx.request.headers),
"requestContext": {
"authorizer": {
"claims": $context.identity.claims
}
},
"body": "$util.escapeJavaScript($util.toJson($payloadBody))",
"isBase64Encoded": false
},
}

API Gateway Mapping Template for context.requestTime is Blank

I'm attempting to to have an API Gateway act as a proxy to DynamoDB and I'm currently testing with a simple POST call to append the $context.requestId and $context.requestTime to my table. I am getting the following error message:
{
"__type": "com.amazon.coral.validate#ValidationException",
"message": "One or more parameter values were invalid: An AttributeValue may not contain an empty string"
}
and what is sent is:
Mon Apr 15 19:10:24 UTC 2019 : Endpoint request body after transformations: {
"TableName": "BurgerOrders",
"Item": {
"OrderId": {
"S": "1f54a90b-5fb2-11e9-8b31-c9003bb71ec2"
},
"RequestTime": {
"S": ""
}
}
}
The mapping template within Integration Request that I have is:
{
"TableName": "BurgerOrders",
"Item": {
"OrderId": {
"S": "$context.requestId"
},
"RequestTime": {
"S": "$context.requestTime"
},
}
}
I've tried to change $context.requestTime to $context.requestTimeEpoch and I get the same error.
I know this was posted a while back, but according to an AWS dev:
"At this time, [$context.requestTime] is only available when the API has been deployed and API call was invoked to deployed stage."
Source: https://forums.aws.amazon.com/thread.jspa?messageID=697652

AWS Api Gateway - Validate incoming XML payload

I am trying to validate an incoming XML payload via API gateway. To be specific, I actually don't even care about the schema, I just want to make sure that the body is not empty (and maybe that it is valid XML if I can get that functionality). I see a variety of posts from years ago stating that XML input validation is not yet supported in API Gateway.
Can somebody confirm if this is still the case? To provide a specific example, I have a model like this:
{
"$schema" : "http://json-schema.org/draft-04/schema#",
"title" : "Test Schema",
"type" : "object",
"minProperties": 1,
"properties": {
"account_id": {
"type": "string",
"pattern": "[A-Za-z]{6}[0-9]{6}"
}
},
"required": ["account_id"]
}
If I add request body validation using this model for content type "application/json" all is well, but if I do the same for content type "application/xml" no validation is performed.
Yes APIGW only supports json payload validation.

HIVE_INVALID_METADATA in Amazon Athena

How can I work around the following error in Amazon Athena?
HIVE_INVALID_METADATA: com.facebook.presto.hive.DataCatalogException: Error: : expected at the position 8 of 'struct<x-amz-request-id:string,action:string,label:string,category:string,when:string>' but '-' is found. (Service: null; Status Code: 0; Error Code: null; Request ID: null)
When looking at position 8 in the database table connected to Athena generated by AWS Glue, I can see that it has a column named attributes with a corresponding struct data type:
struct <
x-amz-request-id:string,
action:string,
label:string,
category:string,
when:string
>
My guess is that the error occurs because the attributes field is not always populated (c.f. the _session.start event below) and does not always contain all fields (e.g. the DocumentHandling event below does not contain the attributes.x-amz-request-id field). What is the appropriate way to address this problem? Can I make a column optional in Glue? Can (should?) Glue fill the struct with empty strings? Other options?
Background: I have the following backend structure:
Amazon PinPoint Analytics collects metrics from my application.
The PinPoint event stream has been configured to forward the events to an Amazon Kinesis Firehose delivery stream.
Kinesis Firehose writes data to S3
Use AWS Glue to crawl S3
Use Athena to write queries based on the databases and tables generated by AWS Glue
I can see PinPoint events successfully being added to json files in S3, e.g.
First event in a file:
{
"event_type": "_session.start",
"event_timestamp": 1524835188519,
"arrival_timestamp": 1524835192884,
"event_version": "3.1",
"application": {
"app_id": "[an app id]",
"cognito_identity_pool_id": "[a pool id]",
"sdk": {
"name": "Mozilla",
"version": "5.0"
}
},
"client": {
"client_id": "[a client id]",
"cognito_id": "[a cognito id]"
},
"device": {
"locale": {
"code": "en_GB",
"country": "GB",
"language": "en"
},
"make": "generic web browser",
"model": "Unknown",
"platform": {
"name": "macos",
"version": "10.12.6"
}
},
"session": {
"session_id": "[a session id]",
"start_timestamp": 1524835188519
},
"attributes": {},
"client_context": {
"custom": {
"legacy_identifier": "50ebf77917c74f9590c0c0abbe5522d2"
}
},
"awsAccountId": "672057540201"
}
Second event in the same file:
{
"event_type": "DocumentHandling",
"event_timestamp": 1524835194932,
"arrival_timestamp": 1524835200692,
"event_version": "3.1",
"application": {
"app_id": "[an app id]",
"cognito_identity_pool_id": "[a pool id]",
"sdk": {
"name": "Mozilla",
"version": "5.0"
}
},
"client": {
"client_id": "[a client id]",
"cognito_id": "[a cognito id]"
},
"device": {
"locale": {
"code": "en_GB",
"country": "GB",
"language": "en"
},
"make": "generic web browser",
"model": "Unknown",
"platform": {
"name": "macos",
"version": "10.12.6"
}
},
"session": {},
"attributes": {
"action": "Button-click",
"label": "FavoriteStar",
"category": "Navigation"
},
"metrics": {
"details": 40.0
},
"client_context": {
"custom": {
"legacy_identifier": "50ebf77917c74f9590c0c0abbe5522d2"
}
},
"awsAccountId": "[aws account id]"
}
Next, AWS Glue has generated a database and a table. Specifically, I see that there is a column named attributes that has the value of
struct <
x-amz-request-id:string,
action:string,
label:string,
category:string,
when:string
>
However, when I attempt to Preview table from Athena, i.e. execute the query
SELECT * FROM "pinpoint-test"."pinpoint_testfirehose" limit 10;
I get the error message described earlier.
Side note, I have tried to remove the attributes field (by editing the database table from Glue), but that results in Internal error when executing the SQL query from Athena.
This is a known limitation. Athena table and database names allow only underscore special characters#
Athena table and database names cannot contain special characters, other than underscore (_).
Source: http://docs.aws.amazon.com/athena/latest/ug/known-limitations.html
Use tick (`) when table name has - in the name
Example:
SELECT * FROM `pinpoint-test`.`pinpoint_testfirehose` limit 10;
Make sure you select "default" database on the left pane.
I believe the problem is your struct element name: x-amz-request-id
The "-" in the name.
I'm currently dealing with a similar issue since my elements in my struct have "::" in the name.
Sample data:
some_key: {
"system::date": date,
"system::nps_rating": 0
}
Glue derived struct Schema (it tried to escape them with ):
struct <
system\:\:date:String
system\:\:nps_rating:Int
>
But that still gives me an error in Athena.
I don't have a good solution for this other than changing Struct to STRING and trying to process the data that way.

How to make Swashbuckle generate correct OpenAPI schema for a Dictionary parameter?

I have an ASP.NetCore action method that is defined as:
[HttpGet]
public async Task<IActionResult> Get([FromQuery]Dictionary<string,string> values)
{
return Ok(JsonConvert.SerializeObject(values));
}
The expected query would be something like:
http://localhost:36541/api?values[someProperty]=123&values[someOther]=234
When I use Swashbuckle the resulting swagger.json file would end up like:
{
"swagger": "2.0",
"info": {
"version": "v1",
"title": "Test API"
},
"paths": {
"/api/Api": {
"get": {
"tags": [
"Api"
],
"operationId": "ApiApiGet",
"consumes": [],
"produces": [],
"parameters": [{
"name": "values",
"in": "query",
"required": false,
"type": "object"
}],
"responses": {
"200": {
"description": "Success"
}
}
}
}
},
"definitions": {}
}
But this doesn't validate using autorest or http://editor.swagger.io/
The error is:
Schema error at paths['/api/Api'].get.parameters[0]
should NOT have additional properties
additionalProperty: type, name, in, required
Jump to line 14
Schema error at paths['/api/Api'].get.parameters[0].required
should be equal to one of the allowed values
allowedValues: true
Jump to line 14
Schema error at paths['/api/Api'].get.parameters[0].in
should be equal to one of the allowed values
allowedValues: body, header, formData, path
Jump to line 15
Schema error at paths['/api/Api'].get.parameters[0].type
should be equal to one of the allowed values
allowedValues: string, number, boolean, integer, array, file
Jump to line 17
It seems that it's missing the additionalProperties property according to https://swagger.io/docs/specification/data-models/dictionaries/
How can I make this query parameter be a valid OpenAPI / Swagger definition?
Following up on the comment from #Helen
Swashbuckle does not support the OpenAPI Specification (OAS) 3.0 yet, so you should not have objects (such as dictionaries) on the query string.
My recommendation change that action to a Post and get the values from the Body, also since your response is IActionResult consider using SwaggerResponse something like this:
[HttpPost]
[SwaggerResponse(200, typeof(Dictionary<string, string>), "This returns a dictionary.")]
public async Task<IActionResult> Post([FromBody]Dictionary<string,string> values)
{
return Ok(JsonConvert.SerializeObject(values));
}
Update:
I was also looking into the possibility of sending the GET with a body, and there seems to be a lot of debate about it:
HTTP GET with request body