AWS AppSync access parent resolver properties in nested resolver (interpolation problem?) - amazon-web-services

I have a GraphQL API which works like this:
mutation {
customer(id: "123") {
someMutation(new: "data") {
id name email # from customer
}
}
}
We use nested resolver style because we have a large schema, and it helps keep things clean.
This means we need to resolve "args" from the someMutation, and inherit the ID from the parent resolver.
AWS AppSync docs claims that you can do this with the $context.source.id field, but there are so far as I can tell zero documented options. We have tried this Velocity Template:
{
"version": "2018-05-29",
"method": "POST",
"params": {
"headers": {
"Content-Type": "application/json"
},
"query": {
"command_name": "set_email",
"new": $util.toJson($context.arguments.new),
}
},
"resourcePath": $util.toJson("/customers/$context.source.id")
}
Scant little documentation exists (except this "resolver template mapping guide") about interpolation, or string concatenation, it is pretty inadequate.
According to the "Resolver mapping template context reference" $context.source should "A map that contains the resolution of the parent field."
The failure mode here is that my downstream HTTP resolver is receiving the literal string "/customers/$context.source.id" rather than the interpolated variable.
Try as I might I cannot figure a way to get an interpolated value with or without any $util...() helpers for JSONification string concatenation, any combination of quoting, etc.

So, I figured this out in the end. The parent resolver wasn't responding with the {id: '123'...} data.
I found out that AppSync has a test console, which helped me verify that, yes, indeed my VTL template was working correctly with the expected payload.
What I found incredibly unintuitive, however was that unlike most template languages which would interpolate an empty variable to "" (empty string), VTL templates seem to behave as though you had no interpolation what so ever leading me to question the interpolation syntax in general.
See the screenshot from the AppSync test console below.
There should also be a way to write tests in that use the AWS CLI/SDK but I didn't bother looking into it, ergonomics are bad for that, needing us to configure the CI with a test account on AWS, and we test our application logic at a higher level, anyway.

Related

How do I find out where the response from dialogflow comes from?

I'm not a developer, so this is a little above my head.
My team has implemented a project in dialogflow, one for an old app and one from a new app. I have basic access to the old dialogflow account and I can see that it has an intent called glossaries, same intent name as in the new one. In glossaries, there is a training phrase called "What is a red talk?". This phrase only works in one of my apps and I need to know why.
There is no default response or anything under context. If I copy that curl link into a terminal, the payload doesn't return with any information.
I found the API for the new app and red talks is definitely not in the payload when I do a GET/all. There may be an old API somewhere, but no one knows where.
Where can I find this information? I'm very confused and all the basic training for dialogflow points to default response, which we're not using. I have read through the docs. I have searched the three company github repos that have the application in the name but I have not found anything. I am looking for an app.intent phrase with glossaries in it or just the word glossaries.
I have found only this json and a glossaryTest.php that doesn't seem helpful:
"meta": {
"total": 2,
"page": 1,
"limit": 10,
"sort": "createdAt",
"direction": "desc",
"load-more": false
},
"results": [
{
"term": "This is a term",
"definition": "This is a definition",
"links": [
{
"id": "1",
"url": "http:\/\/example.com\/1",
"title": "KWU Course: Lead Generation 36:12:3",
"ordering": "1"
},
{
"id": "2",
"url": "http:\/\/example.com\/2",
"title": "",
"ordering": "2"
}
]
}
]
}
There is also a json with a lot data for API calls but no glossaries there either.
If we're using fulfillment to handle these intents, I don't see a fullfillment header like google docs say there should be. I may not have full access so perhaps I would be viewing more information in the screen if I had that, I have no idea. The devs who created this are long gone. The devs who also created the new app are also long gone.
Am I missing an API in my environment documentation? Is the intent hard coded? I suspect it was. How do I prove that or move forward?
Yes, your intent are somehow hard-coded [0], or defined through the UI.
Each intent has a setting to enable fulfillment. If an intent requires
some action by your system or a dynamic response, you should enable
fulfillment for the intent. If an intent without fulfillment enabled
is matched, Dialogflow uses the static response you defined for the
intent. [2]
Perhaps you are using a custom integration [1]. So, unless you are using static response (those you see in the UI), the frontend code may be managed by your project API (not Dialogflow API), and perhaps the content modified before performing any further or eventually returning the response.
As I understand you should contact your colleagues for understanding about the integration solution they have created. Or otherwise if the Intent has been created through the API, look for its relative files where there may be They may have created the integration through the SDK, while picking up training data from a source out of the codebase. So perhaps you cannot see it directly in the code. Nonetheless, you should be able to access it through the UI once it has been created.
In case my answer was not of your help, please do not hesitate to further clarify your needs, perhaps providing some further information.
[0] https://cloud.google.com/dialogflow/docs/manage-intents#create_intent
[1] https://cloud.google.com/dialogflow/docs/integrations
[2] https://cloud.google.com/dialogflow/docs/fulfillment-overview

How to handle "Change Reason" in a PUT Request?

I work with a system that requires that each change be audited and a reason specified for each change. In trying to keep with good REST design, we want to use the HTTP verbs correctly.
With regards to this particular case, I am unsure about best way to handle this situation. Lets say we have a simple entity:
URL: /users/100
JSON: { username: 'usr1', firstName: 'John', lastName: 'Smith' }
Now if I want to update the username from 'usr1' to 'user1', our system requires that I specify the reason for the change.
Without the change reason requirement, we could easily just PUT the JSON to the URL.
My question is what is the best way to send the change reason to the server. Here are the options I have come up with so far:
Add a changeReason attribute to the entity.
Add changeReason as a query parameter.
Add a changeReason header.
None of these options seem right to me. Has anyone dealt with this before?
First, some thoughts to your proposed solutions:
Add changeReason to the JSON object: Is bad, because (if using the same mime-type) the GET would not need that field.
As query parameter: Is wrong, because it is not a parameter for the resource (but for the request)
As header: Is wrong, because this is an out-of-band information.
So, certainly this information should be part of the request representation, and the update happens on the user, so the URL should not change (it is the same resource). I propose the PATCH method, which was created seemingly for the exact purpose you describe.
With the PATCH method, you have to describe a "change" representation, that specifically describes a change you want applied to a resource. This document format will be different from any resource formats, and could then contain a "changeReason". Looks something like this:
PATCH /users/100
Content-Type: application/vnd.company.auditedchange+json
{
"replaceFields": {
"username": "user1"
}
"changeReason": "Typo in username."
}
Introduce a new resource userchange. POST to it with the information about the change.
POST /userchange
Content-Type: application/json
{
"id": 100,
"changes": {
"username": "user1"
},
"reason": "fixed name"
}
The server would execute the change on the user and log the reason.

AWS API Gateway: How do I make querystring parameters optional in mapping template?

I can't seem to figure out how to create optional query string parameters using a mapping template within my resource's Integration Request.
My template looks like this:
{ "limit": "$input.params('limit')", "post_date":"$input.params('post_date')" }
I'd like 'limit' & 'post_date' to be optional. This template creates a querystring that looks like this when these parameters are not provided:
/myresource?limit=undefined&
When I'm expecting:
/myresource
The Docs don't seem to cover this. I have found some example templates in the documentation that use a bash-like syntax to provide conditional functionality. I've tried testing the following but it will NOT validate in the AWS console:
#set($limit = $input.path('limit'))
{
#if($limit)"limit": "$input.params('limit')",#end
}
Am I on the right track?
Thanks!
Yes, you absolutely can do this in Api Gateway; although it does not seem to be well-documented!
In your question, you mentioned that this is a parameter; but you used input.path, which would normally refer to an element in the body of the POST request. The following should work:
#set($limit = $input.params('limit'))
{
#if($limit && $limit.length() != 0)
"limit": "$input.params('limit')"
#end
}
In terms of documentation, I found that the following page from AWS is actually pretty useful. It's tucked away in a section about mock endpoints, though:
http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-mock-integration.html
Wanted to contribute, as Tom Kerswill's answer did not work for me.
I kept running into the issue where if I defined an optional parameter and didn't use it, I would get a JSON parse exception when calling the API.
How I got mine to work:
#set($hasOptional = $input.params("optionalParam") != "")
{
"requiredParam1": $input.params("requiredParam1"),
"requiredParam2": $input.params("requiredParam2"),
#if(!$hasOptional )
"optionalParam": null
#end
#if($hasOptional )
"optionalParam": $input.params("optionalParam")
#end
}
Running with ONLY the second 'if' statement did NOT work, I had to explicitly set the optional parameter to null if it didn't exist.
Also note the use of quotation marks, putting quotes around the entire $input... never worked for me. E.g.
"$input.params('limit')"
I had to write them out exactly like this:
"requiredParam1": $input.params("requiredParam1")
Hope this helps another frustrated AWS API Gateway user.

REST API design: different granularity for receiving and updating resources

I'm in the process of creating a REST API. Among others, there is a resource-type called company which has quite a lot of attributes/fields.
The two common use cases when dealing with company resources are:
Load a whole company and all its attributes with one single request
Update a (relatively small) set of attributes of a company, but never all attributes at the same time
I came up with two different approaches regarding the design of the API and need to pick one of them (maybe there are even better approaches, so feel free to comment):
1. Using subresources for fine-grained updates
Since the attributes of a company can be grouped into categories (e.g. street, city and state represent an address... phone, mail and fax represent contact information and so on...), one approach could be to use the following routes:
/company/id: can be used to fetch a whole company using GET
/company/id/address: can be used to update address information (street, city...) using PUT
/company/id/contact: can be used to update contact information (phone, mail...) using PUT
And so on.
But: Using GET on subresources like /company/id/address would never happen. Likewise, updating /company/id itself would also never happen (see use cases above). I'm not sure if this approach follows the idea of REST since I'm loading and manipulating the same data using different URLs.
2. Using HTTP PATCH for fine-grained updates
In this approach, there are no extra routes for partial updates. Instead, there is only one endpoint:
/company/id: can be used to fetch a whole company using GET and, at the same time, to update a subset of the resource (address, contact info etc.) using PATCH.
From a technical point of view, I'm quite sure that both approaches would work fine. However, I don't want to use REST in a way that it isn't supposed to be used. Which approach do you prefer?
Do you really nead each and every field contained in the GET response all the time? If not, than its more than just fine to create own resources for addresses and contacts. Maybe you will later find a further use-case where you might reuse these resources.
Moreover, you can embed other resources as well in resources. JSON HAL (hal-json) f.e. explicitely provides an _embedded property where you can embed the current state of f.e. sub-resources. A simplified HAL-like JSON representation of an imaginary company resource with embedded resources could look like this:
{
"name":"Test Company",
"businessType":"PLC",
"foundingYear": 2010,
"founders": [
{
"name": "Tim Test",
"_links": {
"self": {
"href": "http://example.org/persons/1234"
}
}
}
],
...
"_embedded": {
"address": {
"street": "Main Street 1",
"city": "Big Town",
"zipCode": "12345",
"country": "Neverland"
"_links": {
"self": {
"href": "http://example.org/companies/someCompanyId/address/1"
},
"googleMaps": {
"href": "http://maps.google.com/?ll=39.774769,-74.86084"
}
}
},
"contacts": {
"CEO": {
"name": "Maria Sample",
...
"_links": {
"self": {
"href": "http://example.org/persons/1235"
}
}
},
...
}
}
}
Updating embedded resources therefore is straigtforward by sending a PUT request to the enclosed URI of the particluar resource. As GET requests my be cached, you might need to provide finer grained caching settings (f.e. with conditional GET requests a.k.a If-Modified-Since or ETAG header fields) to retrieve the actual state after an update. These headers should consider the whole resource (incl. embedded once) in order to return the updated state.
Concerning PUT vs. PATCH for "partial updates":
While the semantics of PUT are rather clear, PATCH is often confused with a partial update by just sending the new state for some properties to the service. This article however describes what PATCH really should do.
In short, for a PATCH request a client is responsible for comparing the current state of a resource and calculating the necessary steps to transform the current resource to the desired state. After calculating the steps, the request will have to contain instructions the server has to understand to execute these instructions and therefore produces the updated version. A PATCH request is furthermore atomic - either all instructions succeed or none. This adds some transaction requirements to this request.
In this particular case I'd use PATCH instead of subresources approach. First of all this isn't a real subresources. It's just a fake abstraction introduced to eliminate the problem of updating the whole big entity (resource). Whereas PATCH is a REST compatible, well established and common approach.
And (IMO ultima ratio), imagine that you need to extend company somehow (by adding magazine, venue, CTO, whatever). Will you be adding a new endpoint to enable client to update this newly-added part of a resource? How it finishes? With multiple endpoint that no one understands. With PATCH your API is ready for new elements of a company.

REST Array manipulation best practice

I have full access to foo resource via REST:
{
"name": "foo",
"tags": [
"tag01",
"tag02",
"tag03"
]
}
I would like to delete tag01 in tags array.
Usually I would GET \foo and PUT \foo it back without tag01.
In this case this object is small, so this is ok.
But let's assume it's much bigger. For this case I don't like to download and upload this data. After some google research I found out http PATCH. I looks like exactly what I need.
My request in PATCH way is now
PATCH /foo/tags?op={add|delete}
To delete I would use:
PATCH /foo/tags?op=delete
With this data:
{
"value": "tag01"
}
There are now two thinks that I don't like:
query field op - are there some deafult names described in rfc or smth. like this
member value in request data - this is also freely chosen name
It doesn't look correct to me.
Is there some other way to manipulate arrays via REST?
Are there some name conventions to do it in PATCH way?
The payload of a PATCH should contain "instructions describing how a resource currently residing on the origin server should be modified to produce a new version". All information should be passed in the payload and not in query-params.
For instance you could send:
PATCH /foo
[
{
"op": "remove",
"path": "/tags/0"
}
]
Path /tags/0 points to the first element of the array. The remaining elements should be shifted to the left.
See the JSON Patch draft for more details.
Is there some other way to manipulate arrays via REST?
Yes, because it is not correct. By REST you map your URLs to resources (not operations) and you manipulate resources using HTTP methods and sending representations. Having an op:remove in an URL or in a representation is wrong.
Are there some name conventions to do it in PATCH way?
No there are no REST naming conventions. The URI structure does not matter by REST clients, because they follow hyperlinks with semantic annotations.
If you need an op:remove or similar somewhere, then it indicates that your URI - resource mapping is not good. Probably you have to define a new resource or rethink the resource structure.
I would describe what you want as a bulk create and bulk delete. You can model this cases with something like:
POST /collection [{},{},...] -> 201
DELETE /collection?filter="..." -> 204
In order to delete something from a collection you need a resource identifier URI. In this case this can contain the tag name or the index in the array (if it is ordered).
/foo/tags/tag01
/foo/tags/0
It is up to you, but I would use the tag name.
After that it is pretty simple:
POST /foo/tags ["a","b","c"]
DELETE /foor/tags?name="a,b,c"
So PATCH is not the method you are looking for, because you are creating and removing resources and not replacing them.