AWS appsync w/ Lambd function backend, GraphQL gives unexpected repsonce to Query - aws-lambda

What follows is all in the AWS console. My lambda function defines deletePost
case "deletePost":
var id = event.arguments.id;
callback(null, {id:-1,author:"me",title:"title"}); //note, regardless of what your args are right now it is returning id:-1
break;
My schema is
type Mutation {
...
deletePost(id: ID!): Post!
}
type Post {
id: ID!
author: String!
title: String
}
and my graphiQL query is
mutation DeletePost{
deletePost(id: 3){
id
}
}
For some reason it just parrots id=3 back to me when i've hard coded id as -1? If I ask for author or title to be return in my query I don't get them back at all.
Update full lambs fxn. Just a slightly modified version of the template that is provided with the aws appSync docs.
exports.handler = (event, context, callback) => {
console.log("Received event {}", JSON.stringify(event, 3));
var posts = { //in memory array store (simulates DB)
"1": {"id": "1", "title": "First book", "author": "Author1"},
"2": {"id": "2", "title": "Second book", "author": "Author2"},
"3": {"id": "3", "title": "Third book", "author": "Author3"},
"4": {"id": "4", "title": "Fourth book", "author": "Author4"},
"5": {"id": "5", "title": "Fifth book", "author": "Author5"} };
console.log("Got an Invoke Request: "+event.field);
switch(event.field) {
case "getPost":
var id = event.arguments.id;
callback(null, posts[id]);
break;
case "updatePost":
posts[event.arguments.id]=event.arguments;
console.log(posts);
callback(null, event.arguments);
break;
case "deletePost":
var id = event.arguments.id;
//delete posts[event.arguments.id];
callback(null, {id:-1,author:"me",title:"tits"});
break;
case "allPosts":
var values = [];
for(var d in posts){
values.push(posts[d]);
}
callback(null, values);
break;
case "addPost":
var id = event.arguments.id;
posts[id]=event.arguments;
console.log(posts);
callback(null, event.arguments);
break;
case "addPostErrorWithData":
var id = event.arguments.id;
var result = posts[id];
// attached additional error information to the post
result.errorMessage = 'Error with the mutation, data has changed';
result.errorType = 'MUTATION_ERROR';
callback(null, result);
break;
default:
callback("Unknown field, unable to resolve" + event.field, null);
break;
}
};
Resolvers. For the most part just pass the data straight through.
#request mapping
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": {
"field": "addPost",
"arguments": $util.toJson($context.arguments)
}
}
#responce mapping
$util.toJson($context.result)

It's hard to know without seeing all of the Lambda function and also your resolver templates. From the above code I'm going to guess you used this tutorial as a starting point: https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html
Assuming you're passing back the result in the response template, like $util.toJson($context.result), then the issue is most likely with the structure of the callback in your Lambda function
let result = {"id" :-1, "author":"me", "title":"title"}
callback(null, result);
Essentially you need to pass back a result object for it to be seen in the $context object and converted as a GraphQL JSON response to the caller.

Related

GraphQL Mutation with JSON Patch

Are there any data types in GraphQL that can be used to describe a JSON Patch operation?
The structure of a JSON Patch operation is as follows.
{ "op": "add|replace|remove", "path": "/hello", "value": ["world"] }
Where value can be any valid JSON literal or object, such as.
"value": { "name": "michael" }
"value": "hello, world"
"value": 42
"value": ["a", "b", "c"]
op and path are always simple strings, value can be anything.
If you need to return JSON type then graphql have scalar JSON
which return any JSON type where you want to return it.
Here is schema
`
scalar JSON
type Response {
status: Boolean
message: String
data: JSON
}
type Test {
value: JSON
}
type Query {
getTest: Test
}
type Mutation {
//If you want to mutation then pass data as `JSON.stringify` or json formate
updateTest(value: JSON): Response
}
`
In resolver you can return anything in json format with key name "value"
//Query resolver
getTest: async (_, {}, { context }) => {
// return { "value": "hello, world" }
// return { "value": 42 }
// return { "value": ["a", "b", "c"] }
// return anything in json or string
return { "value": { "name": "michael" } }
},
// Mutation resolver
async updateTest(_, { value }, { }) {
// Pass data in JSON.stringify
// value : "\"hello, world\""
// value : "132456"
// value : "[\"a\", \"b\", \"c\"]"
// value : "{ \"name\": \"michael\" }"
console.log( JSON.parse(value) )
//JSON.parse return formated required data
return { status: true,
message: 'Test updated successfully!',
data: JSON.parse(value)
}
},
the only thing you need to specifically return "value" key to identify to get in query and mutation
Query
{
getTest {
value
}
}
// Which return
{
"data": {
"getTest": {
"value": {
"name": "michael"
}
}
}
}
Mutation
mutation {
updateTest(value: "{ \"name\": \"michael\" }") {
data
status
message
}
}
// Which return
{
"data": {
"updateTest": {
"data": null,
"status": true,
"message": "success"
}
}
}

loopback REST API filter by nested data

I would like to filter from REST API by nested data. For example this object:
[
{
"name": "Handmade Soft Fish",
"tags": "Rubber, Rubber, Salad",
"categories": [
{
"name": "women",
"id": 2,
"parent_id": 0,
"permalink": "/women"
},
{
"name": "kids",
"id": 3,
"parent_id": 0,
"permalink": "/kids"
}
]
},
{
"name": "Tasty Rubber Soap",
"tags": "Granite, Granite, Chair",
"categories": [
{
"name": "kids",
"id": 3,
"parent_id": 0,
"permalink": "/kids"
}
]
}
]
is comming by GET /api/products?filter[include]=categories
and i would like to get only products which has category name "women". How do this?
LoopBack does not support filters based on related models.
This is a limitation that we have never had bandwidth to solve, unfortunately :(
For more details, see the discussion and linked issues here:
Filter on level 2 properties: https://github.com/strongloop/loopback/issues/517
Filter by properties of related models (use SQL JOIN in queries): https://github.com/strongloop/loopback/issues/683
Maybe you want to get this data by the Category REST API. For example:
GET /api/categories?filter[include]=products&filter[where][name]=woman
The result will be a category object with all products related. To this, will be necessary declare this relation on the models.
Try like this.It has worked for me.
const filter = {
where: {
'categories.name': {
inq: ['women']**strong text**
}
}
};
Pass this filter to request as path parameters and the request would be like bellow
GET /api/categoriesfilter=%7B%22where%22:%7B%categories.name%22:%7B%22inq%22:%5B%women%22%5D%7D%7D%7D
Can you share how it looks like without filter[include]=categorie, please ?
[edit]
after a few questions in comment, I'd build a remote method : in common/models/myModel.js (inside the function) :
function getItems(filter, categorieIds = []) {
return new Promise((resolve, reject) => {
let newInclude;
if (filter.hasOwnProperty(include)){
if (Array.isArray(filter.include)) {
newInclude = [].concat(filter.include, "categories")
}else{
if (filter.include.length > 0) {
newInclude = [].concat(filter.include, "categories");
}else{
newInclude = "categories";
}
}
}else{
newInclude = "categories";
}
myModel.find(Object.assign({}, filter, {include: newInclude}))
.then(data => {
if (data.length <= 0) return resolve(data);
if (categoriesIds.length <= 0) return resolve(data);
// there goes your specific filter on categories
const tmp = data.filter(
item => item.categories.findIndex(
categorie => categorieIds.indexOf(categorie.id) > -1
) > -1
);
return resolve(tmp);
})
}
}
myModel.remoteMethod('getItems', {
accepts: [{
arg: "filter",
type: "object",
required: true
}, {
arg: "categorieIds",
type: "array",
required: true
}],
returns: {arg: 'getItems', type: 'array'}
});
I hope it answers your question...

Nested Observables from nested http requests in Angular

Here's what I intend to do.
Request https://reqres.in/api/users/2
Which sends a response as follows.
{
"data": {
"id": 2,
"first_name": "Janet",
"last_name": "Weaver",
"avatar":
"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
}
}
Now, I wanna grab the avatar url and make another request, which gives me the image binary.
At the end of this, what I want as output is an Observable that gives me this data.
{
"data": {
"id": 2,
"first_name": "Janet",
"last_name": "Weaver",
"avatar":
"BINARY_GIBBERISH HERE"
}
}
Here's how I approached but can't finish it.
this.http
.get('https://reqres.in/api/users/2')
.switchMap(a => {
const image$ = this.http.get(a.json().data.avatar);
const data = Observable.of(a.json().data);
// Do something here to combine.
})
.subscribe(a => {
// get data here.
});
}
Basically, is there any way to have a structure like this,
{
"data": {
"id": 2,
"first_name": "Janet",
"last_name": "Weaver",
"avatar": [Observable from this.http.get]
}
}
Which then gets resolved to my final data?
You don't need to use switchMap for this and use concatMap or mergeMap instead:
this.http
.get('https://reqres.in/api/users/2')
.concatMap(a => {
const data = a.json().data;
const image$ = this.http.get(data.avatar);
return image$.map(imageData => {
data.avatar = imageData;
return data;
});
})
.subscribe(a => {
// get data here.
});
}

How to assign a single object to a list?

I'm working with an API rest and it returns me two types of json, object and array.
Object(When there is only one record in the database):
{ "complain": { "id": "1" , "description": "hello", "date": "2017-01-24 11:46:22", "Lat": "20.5204446", "Long": "-100.8249097" } }
Array(When there is more than one record in the database):
{ "complain": [ { "id": "1" , "description": "hello", "date": "2017-01-24 11:46:22", "Lat": "20.587446", "Long": "-100.8246490" }, { "id": "2" , "description": "hello 2", "date": "2017-01-24 11:50:12", "Lat": "20.529876", "Long": "-100.8249097" } ] }
The code I use to consume the json is as follows:
content = await response.Content.ReadAsStringAsync();
var token = JToken.Parse(content);
if (token["complain"] is JArray)
{
var jsonArray = JsonConvert.DeserializeObject<RootArray>(content);
}
else if (token["complain"] is JObject)
{
var jsonObject = JsonConvert.DeserializeObject<RootObject>(content);
}
When it comes to a json array if I can add it to a listview:
myList.ItemsSource = jsonArray.listArray;
But if it is an object I can not and I get the following error:
Cannot implicitly convert type Object to IEnumerable.
Finally I was able to solve my error, it was just a matter of creating a list and adding the deserialized json object.
var jsonObject = JsonConvert.DeserializeObject<RootObject>(content);
List<Complain> simpleList = new List<Complain>();
simpleList.Add(jsonObject.ComplainObject);
myList.ItemsSource = simpleList;
The class to deserialize the Json object:
public class RootObject
{
[JsonProperty("complain")]
public Complain ComplainObject { get; set; }
}

How to create *static* property in can.Model

I need somehow to store metadata in the can.Model
I use findAll method and receive such JSON:
{
"metadata": {
"color": "red"
},
"data": [
{ "id": 1, "description": "Do the dishes." },
{ "id": 2, "description": "Mow the lawn." },
{ "id": 3, "description": "Finish the laundry." }
]
}
I can work with data like can.Model.List, but I need metadata like a static property or something.
You can use can.Model.parseModels to adjust your response JSON before it's turned into a can.Model.List.
parseModels: function(response, xhr) {
var data = response.data;
var metadata = response.metadata;
var properties;
if(data && data.length && metadata) {
properties = Object.getOwnPropertyNames(metadata);
can.each(data, function(datum) {
can.each(properties, function(property) {
datum[property] = metadata[property];
});
});
}
return response;
}
Here's a functional example in JS Bin: http://jsbin.com/qoxuju/1/edit?js,console

Resources