AWS AppSync RDS - Passthrough output resolver for json SQL result - amazon-rds

Is it possible to create some sort of passthrough response resolver for an RDS datasource. The result of the query is 1 row and 1 column of type json which I would like to be the result of the graphql query.
Eg. schema could be
type Query {
getJsonFromDb(p: String): Res
}
type Res {
prop: String
}
schema {
query: Query
}
The request resolver could be
{
"version": "2018-05-29",
"statements": [
"select json_build_object('prop',:P)"
],
"variableMap": {
":P": $util.toJson($ctx.arguments.p)
}
}
In the log result logging looks like this
"result": "{\"sqlStatementResults\":[{\"columnMetadata\":[{\"arrayBaseColumnType\":0,\"isAutoIncrement\":false,\"isCaseSensitive\":true,\"isCurrency\":false,\"isSigned\":false,\"label\":\"json_build_object\",\"name\":\"json_build_object\",\"nullable\":0,\"precision\":2147483647,\"scale\":0,\"schemaName\":\"\",\"tableName\":\"\",\"type\":1111,\"typeName\":\"json\",\"signed\":false,\"autoIncrement\":false,\"caseSensitive\":true,\"currency\":false}],\"numberOfRecordsUpdated\":0,\"records\":[[{\"stringValue\":\"{\\\"prop\\\" : \\\"test\\\"}\"}]]}]}"
So it's there allright, but I cannot seem to find the correct velocity expressions to get it out.
All tips certainly appreciated!
Peter

So I found one way of getting this done by doing like in the response mapping
$util.parseJson($ctx.result).sqlStatementResults[0].records[0][0].stringValue

Related

How to return both error and data in a graphql resolver?

I was thinking about ways of implementing graphql response that would contain both an error and data.
Is it possible to do so without creating a type that would contain error?
e.g.
Mutation addMembersToTeam(membersIds: [ID!]! teamId: ID!): [Member] adds members to some team. Suppose this mutation is called with the following membersIds: [1, 2, 3].
Members with ids 1 and 2 are already in the team, so an error must be thrown that these members cannot be added, but member with an id 3 should be added as he is not in the team.
I was thinking about using formatResponse but seems that I can't get an error there.
Is it possible to solve this problem without adding error field to the return type?
Is it possible to solve this problem without adding error field to the return type?
Unfortunately, no.
A resolver can either return data, or return null and throw an error. It cannot do both. To clarify, it is possible to get a partial response and some errors. A simple example:
const typeDefs = `
type Query {
foo: Foo
}
type Foo {
a: String
b: String
}
`
const resolvers = {
Query: {
foo: () => {},
}
Foo: {
a: () => 'A',
b: () => new Error('Oops!'),
}
}
In this example, querying both fields on foo will result in the following response:
{
"data": {
"foo": {
"a": "A",
"b": null
}
},
"errors": [
{
"message": "Oops",
"locations": [
{
"line": 6,
"column": 5
}
],
"path": [
"foo",
"b"
]
}
]
}
In this way, it's possible to send back both data and errors. But you cannot do so for the same field, like in your question. There's a couple of ways around this. As you point out, you could return the errors as part of the response, which is usually how this is done. You could then use formatResponse, walk the resulting data, extract any errors and combine them with them with any other GraphQL errors. Not optimal, but it may get you the behavior you're looking for.
Another alternative is to modify the mutation so it takes a single memberId. You can then request a separate mutation for each id you're adding:
add1: addMemberToTeam(memberId: $memberId1 teamId: $teamId): {
id
}
add2: addMemberToTeam(memberId: $memberId2 teamId: $teamId): {
id
}
add3: addMemberToTeam(memberId: $memberId3 teamId: $teamId): {
id
}
This can be trickier to handle client-side, and is of course less efficient, but again might get you the expected behavior.
If you think about combining the GraphQL error - there is a way to do it in Apollo.
You need to set errorPolicy to all. That will help you notify users about the error and at the same time have as much data as possible.
none: This is the default policy to match how Apollo Client 1.0
worked. Any GraphQL Errors are treated the same as network errors and
any data is ignored from the response.
ignore: Ignore allows you to
read any data that is returned alongside GraphQL Errors, but doesn’t
save the errors or report them to your UI.
all: Using the all policy
is the best way to notify your users of potential issues while still
showing as much data as possible from your server. It saves both data
and errors into the Apollo Cache so your UI can use them.
But according to best practices, you shouldn't manipulate it in this way.
This is a great article about handling errors in GraphQL.
So, preferable way is to add "errors" field as part of your response and handle it in JS code.
We can achieve this by using a union. I would recommend visiting the great article Handling GraphQL errors like a champ
Example:
Mutation part: We can return the union type for the response & capture the result according to types.
type MemberType {
id: ID!
name: String!
}
enum ErrorType {
BAD_REQUEST_ERROR
FORBIDDEN_ERROR
INTERNAL_SERVER_ERROR
NOT_FOUND_ERROR
UNAUTHORIZED_ERROR
}
type GraphqlError {
type: ErrorType!
code: String!
message: String!
helpLink: URL
}
union UserRegisterResult = MemberType | GraphqlError;
addMembersToTeam(membersIds: [ID!]! teamId: ID!): UserRegisterResult!
Response:
addMembersToTeam(membersIds: [ID!]! teamId: ID!): {
...on MemberType{
id,
name,
}
...on GraphqlError{
id,
message,
statusCode,
}
}

GraphQL Conditional Queries

I'm a newbie in GraphQL and I was wondering if there is a easy way to query with "dynamic conditions".
For exemple, on GraphiQL I can query for :
query {
users{
name
age
}
}
And It will bring me a list of all users
{
"data": {
"users": [
{
"name": "Luis Coimbra",
"age": 15
},
{
"name": "Sebastião Campagnucci",
"age": 50
},
{
"name": "Giovana Ribeiro",
"age": 30
}
]
}
}
But is there an easy way for me to bring only, for example, users who are above 18 or any other age ?
An expected solution would be:
query {
users{
name
age > 18
}
}
Haven't found anything like that on documentation...
This is possible-it would have to be different. Your query wouldn't be a valid GQL query. Something like this would:
{
users(where: {age: { $gt: 18 }}){ #inspired by mongoDB query api
name
age
}
}
or maybe simpler:
{
users(where: {age: ">18"}}){
name
age
}
}
of course either way the resolver on the backend needs to expect this where argument on the users field and construct the DB query accordingly when it is passed. You would not find this in GraphQL docs because GraphQL itself doesn't care about that. It only showcases how to use features of GraphQL.
If you tried example projects like for example star was api, those don't have any filtering built in.
You should send your age filter as a parameter.You might try the following one:
In your graphql file
type users {
name: String,
age: Int,
...
}
usersQuery(ageLimit: Int): [users]
also you can send '>' , '<' , '=' as a parameter. Also it seems like that
usersQuery(ageLimit: Int, ageOperator: String): [users]
and you should configure your resolver where statement with these operators. hope it helps you.

Dynamoose query by different attributes (Node.js)

I need to get record (-s) from DynamoDB via Dynamoose by non-key attribute. For example, I need to get all records from the table someModel where the field my_field is equal to 111. I do it by the next way:
const data = await someModel.query("my_field").eq("111").exec();
And this code stops executing! I mean that following code after that is not called.
If I change code to this:
const data = await someModel.query("my_field").eq("111");
my query is working, but data contains:
{
"options": {
"all": {
"delay": 0,
"max": 1
}
},
"query": {
"hashKey": {
"name": "my_field",
"value": "111"
}
},
"filters": {},
"buildState": false,
"validationError": null,
"notState": false,
"success": true
}
I understand that above code is prepared query parameters for query but how can I apply them and execute query to DynamoDB? Of course the query code is placing inside async function - that is why await is written there.
I use also serverless framework for describing DynamoDB schemes. But all models I write via dynamoose.
Where am I mistaking?
As mentioned in the documentation here, Dynamoose query returns the value in the callback and not as a promise. Therefore, your response is actually returned in a callback that should come inside the exec part of your query. async/await is valid for promises and not callbacks.
someModel.query("my_field").eq("111").exec((err, data) => {
// Do whatever you wish to with your data
});

Convert a string parameter into float in Cloudant query

I need to do a query in an Cloudant DataBase where you compare a number with decimals that is defined as a string with another number sent from the server. The problem is that a comparison of strings is made and I need it to be a numerical comparison. There is there any way to perform this search by converting the database parameter to float while doing the query? O there are another way to do this query?
This is the query in the server, value.precio is sent from the client as a string.
value.precio = value.precio.split("-");
var precio_init = value.precio[0];
var precio_final = value.precio[1];
value.precio = {
"$gte":precio_init,
"$lte":precio_final
};
And in my database this is the parameter I want to search is:
"precio": "13.39"
Thanks
I don't think you will be able to do this with Cloudant Query, but you could try Cloudant Search. Create a new search index similar to the following:
Design Doc: myDesignDoc
Index Name: byPrecio
Index:
function (doc) {
if (doc.precio) {
index("precio", parseFloat(doc.precio));
}
}
Then you can uses ranges to search. For example:
precio:[13 TO 14]
Full search on Cloudant would look like this:
https://xxx.cloudant.com/YOUR_DB/_design/myDesignDoc/_search/byPrecio?q=precio:[13%20TO%2014]&include_docs=true
Sample response:
{
"total_rows":1,
"bookmark":"g2wAAAAxxx",
"rows":[
{
"id":"74fa6ff1b6dbca8c10d677832f6a3de2",
"order":[
1.0,
0
],
"fields":{
},
"doc":{
"_id":"74fa6ff1b6dbca8c10d677832f6a3de2",
"_rev":"2-17c984e51102b719fe9f80fc5d5bc78e",
"precio":"13.39",
"otherField":"otherValue"
}
}
]
}
More info on Cloudant Search here

IBM Bluemix Discovery - query parameter

I have created a Discovery service on my bluemix account. I want to query my documents from a nodejs application.
I have built a query with some aggregation, tested it using the bluemix online tool and it's working well.
Now when I query the collection from my code, whatever my parameters are, I always receive all of my documents with the enriched text and so on. I think I am missing how to send the query attributes to the service (like filters and aggregations).
Here is my code:
var queryParams = {
query:'CHLOE RICHARDS',
return:'title',
count:1,
aggregations:'nested(enriched_text.entities).filter(enriched_text.entities.type:Person).term(enriched_text.entities.text, count:5)'
};
discovery.query({environment_id:that.environment_id, collection_id:that.collection_id, query_options:queryParams }, function(error, data) {
if(error){
console.error(error);
reject(error);
}
else{
console.log(JSON.stringify(data, null, 2));
resolve(data.matching_results);
}
});
And the result is always:
{
"matching_results": 28,
"results": [
{
"id": "fe5e2a38e6cccfbd97dbdd0c33c9c8fd",
"score": 1,
"extracted_metadata": {
"publicationdate": "2016-01-05",
"sha1": "28434b0a7e2a94dd62cabe9b5a82e98766584dd412",
"author": "Richardson, Heather S",
"filename": "whatever.docx",
"file_type": "word",
"title": "no title"
},
"text": "......
Independantly of the value of the query_optionparameter. Can you help me?
EDIT
Instead of the query_options:queryParams, I have used query:"text:CHLOE RICHARDS" and it's working well. Now my problem still remains to find the right parameter format to add the aggregations I want
EDIT 2
So I have looked at IBM's example on Github more carefully, and the parameters are now formatted like this:
const queryParams = {
count: 5,
return: 'title,enrichedTitle.text',
query: '"CHLOE RICHARDS"',
aggregations: [ 'nested(enriched_text.entities).filter(enriched_text.entities.type:Person).term(enriched_text.entities.text, count:5)' ],
environment_id: '1111111111',
collection_id: '11111111111'
};
It works well if I use only the query attribute. Now if I only use the aggregations one, all the documents are sent back as a result (which is understandable) but I have no aggregation part, so I can not access the list of proper name in my documents.
Your query does not look right. I you are going to use query then you will need to construct a query search like text:"CHLOE RICHARDS"
If you want to perform a natural language query then you should be setting the parameter natural_language_query.

Resources