How to debug Flutter Amplify Graphql requests? - dart

I'm trying to return a list of my model sorted by date. But I run into a vague error.
Here is my graphql.schema
type Vote #model(timestamps: { createdAt: "created_at", updatedAt: "updated_at" }) {
id: ID!
name: String!
date: AWSTimestamp #index(name: "date-index", sortKeyFields: ["name"], queryField: "getVotesByDate")
}
Here is the request:
Future<List<Vote?>> recentVotes() async {
const getRecentVotes = "getRecentVotes";
String document = """
query getRecentVotes {
listVotes(limit: 5) {
items {
date
id
name
}
nextToken
}
}
""";
try {
final request = GraphQLRequest(
document: document,
modelType: Vote.classType,
decodePath: getRecentVotes,
);
final response = await Amplify.API.query(request: request).response;
List<Vote?>? votes = response.data?.items;
if (votes == null) {
return [];
}
_voteLength = votes.length;
return votes;
} catch (err) {
debugPrint(err.toString());
}
return [];
}
The error I get back is pretty terrible:
flutter: ApiException(message: The HTTP response status code is [400]., recoverySuggestion: The metadata associated with the response is contained in the HTTPURLResponse.
flutter: For more information on HTTP status codes, take a look at
flutter: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes, underlyingException: null)
As best I can tell, it's just a standard bad request error but with no message of what makes it bad. The graphql syntax is valid and tested.
*Edit
I've managed to restart the app which provided more details about the error. With the same code, I now get
flutter: type 'Null' is not a subtype of type 'String'
My first thought after this is that there's some returned field that is null that cannot be. I've checked the name and the id of all the records in the database and they are not null so I'm not sure how that's possible.

Related

Neo4j GraphQL how to check if node exists before connecting to it

B"H
I've seen this question and I want to do the same thing with the GraphQL driver for Neo4j. I think the answer lies somewhere in combining AND and OR operators found here, of some sort, but not sure how.
To illustrate the question, say I have this type:
type Comment {
id: ID #id
timeAdded: DateTime! #timestamp(
operations: [CREATE]
)
writer: User! #relationship(
type: "WRITTEN_BY",
direction: IN
)
content: String
}
type User {
id: ID #id
name: String
commentsWritten: [Comment!]! #relationship(
type: "WRITTEN_BY",
direction: OUT
)
}
type Mutation {
addComment(authorID: String)
}
and this resolver to add a new comment (where "Comment" is a reference to the OGM model of the Comment type):
addComment: async (
src,
args,
ctx
) => {
var authorID = args/*edit*/.authorID //let's say this is passed
//from request headers / JWT token or something
var adCom = await Comment.create({
input: [
{
content: args.content,
writer: {
connect: {
where: {
node: {
users: {
id: authorID
}
}
}
}
}
}
]
})
return adCom;
}
So it attempts to connect to that user with that ID. but if there is no user with that ID, I don't want to create anything [not only do I not want to connect it].
I could run another query to find a User with that ID, and test if it went through, but was wondering if it's possible to do everything in one call

Passing array object in find Method (TypeORM)

I am migrating the API to TypeORM from mongoose. I have an array(conversationIds) containing list of id to be searched from the Database and it should return me the available id. Below are the query which i have tried.
const conversationMessagesWithDuplicates = await consversationRepository.createQueryBuilder("conversation").where("conversation.channel_message_ID = :channel_message_ID",{channel_message_ID : conversationIds}).getMany();
and also this one
const conversationMessagesWithDuplicates = await consversationRepository.find({channel_message_ID: conversationIds});
But i am getting below issue:
(node:11548) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): QueryFailedError: Error: Incorrect syntax near ','.
I have tried using entity Manger also but no success
const conversationMessagesWithDuplicates = await entityManager.query(`SELECT channel_message_ID FROM Conversation where channel_message_ID IN (conversationIds)`);
Below is the MoongoseAPI which is working fine:
Conversation.find( { channel_message_ID: { $in: conversationMessageIdArray } } ).exec(function(err, conversationMessagesDuplicatesArray) {}
This is the solution
const conversationMessagesWithDuplicates = await consversationRepository.createQueryBuilder("conversation").where("conversation.channel_message_ID IN (:conversationIds)", { conversationIds: conversationIds }).getMany();

How to update connection metadata in client-side store?

I'm attempting to learn Relay by implementing TodoMVC from scratch.
I can query my data like this which is working well:
query {
allTodos(first: 100) {
totalCount
completedCount
edges {
node {
id
text
completed
}
}
}
}
I got the idea to add the totalCount and completedCount metadata to the connection from here: http://graphql.org/learn/pagination/#end-of-list-counts-and-connections
It's similar in this example: https://github.com/graphql/swapi-graphql/blob/master/src/schema/index.js#L78
Now I am writing a mutation to change the completed field of a Todo given its id.
I gather I will need to return the new completedCount in the mutation payload, but I'm not sure how to implement getConfigs() to update this in the client-side store. I don't have an id for the connection, right? Is there is a flaw in my schema design? Thanks!
Assuming your mutation returns a viewer, you'll need to add the viewer to your fatQuery and getConfigs. I think this tutorial might be helpful. Here's the excerpt relevant to your task:
Adding a Todo is more complex. The reason for this is that we need to
update not only the state of a Todo object that we will create, but
also a connection where it is stored - the count of Todos will change,
as well as the listing of Todo nodes in edges.
import Relay from 'react-relay';
export default class AddTodoMutation extends Relay.Mutation {
static fragments = {
viewer: () => Relay.QL`fragment on ReindexViewer {
id
allTodos {
count,
}
}`
};
getMutation() {
return Relay.QL`mutation{ createTodo }`;
}
getVariables() {
return {
text: this.props.text,
complete: false,
};
}
getFatQuery() {
return Relay.QL`
fragment on _TodoPayload {
changedTodoEdge,
viewer {
id,
allTodos {
count
}
}
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentID: this.props.viewer.id,
connectionName: 'allTodos',
edgeName: 'changedTodoEdge',
rangeBehaviors: {
'': 'prepend',
},
}, {
type: 'FIELDS_CHANGE',
fieldIDs: {
viewer: this.props.viewer.id,
},
}];
}
getOptimisticResponse() {
return {
changedTodoEdge: {
node: {
text: this.props.text,
complete: false,
},
},
viewer: {
id: this.props.viewer.id,
allTodos: {
count: this.props.viewer.allTodos.count + 1,
},
},
};
}
}
In order to perform this mutation, we need some data that might not be
available to the component - the id of viewer object and count of
allTodos connection. Therefore we need to specify fragments for the
mutation same way as we specify them for containers.
Our configs are more complex this time too - we need to add our new
Todo to a connection, so we use RANGE_ADD mutation config. Relay
expects an edge to be passed in payload, not just a Todo, Reindex
provides changedTodoEdge for this. Lastly we need to fetch updated
connection count from the server and for this viewer field is
available for every payload.
In our optimistic update we increment the count of allTodos, so that
we change our “total” display without any delay.

In a GraphQL/Relay mutation that creates a model, is there a way to get back the model ID?

We're using Relay and GraphQL in a new project.
We've got a Relay mutation that creates a new model in the DB:
export default class AddCampaignMutation extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation { addCampaign }`;
}
getVariables() {
return {
type: this.props.campaignType
};
}
getFatQuery() {
return Relay.QL`
fragment on AddCampaignPayload {
campaignEdge
viewer
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'viewer',
parentID: this.props.viewer.id,
connectionName: 'campaigns',
edgeName: 'campaignEdge',
rangeBehaviors: {
'': 'append',
},
}];
}
static fragments = {
viewer: () => Relay.QL`
fragment on User {
id
}
`,
};
}
However, since none of the components are currently querying for the range specified in RANGE_ADD (viewer { campaigns }), Relay intelligently excludes that query from the AddCampaignPayload.
This results in a console warning:
Warning: writeRelayUpdatePayload(): Expected response payload to include the newly created edge `campaignEdge` and its `node` field. Did you forget to update the `RANGE_ADD` mutation config?
I really want to get back the ID of the newly created model, so that I can navigate the client to it. For example, getting back the new campaignEdge, I want to send the client to /campaigns/${campaignEdge.node.id}.
Is there any way to tell Relay to fetch that edge? Have I configured this mutation correctly?
You can use REQUIRED_CHILDREN in this context. For more details, see https://github.com/facebook/relay/issues/237 and https://github.com/facebook/relay/issues/236.
REQUIRED_CHILDREN lets you specify an extra data dependency for exactly this pattern.

500 Internal Server Error when querying index

I am trying to get started with Neo4j and the Neo4jClient; the first thing I'm trying to attempt is to insert a series of nodes with a publication_number property. Before inserting each node, I want to check to ensure another node with the same publication number does not exist. To this end I created an index for publication_number, which I then query.
This is the code I have so far. (Obviously all the logic above has not been implemented, but I can't even get this to work.)
class Program
{
static void Main(string[] args)
{
var client = new GraphClient(new Uri("http://192.168.12.31:7474/db/data"));
client.Connect();
// create index
client.CreateIndex("publication_number_idx", new IndexConfiguration
{
Provider = IndexProvider.lucene,
Type = IndexType.exact
},
IndexFor.Node);
// create record
Record record1 = new Record { publication_number = "1" };
Record record2 = new Record { publication_number = "2" };
// add record1 to graph and index
var record1Ref = client.Create(record1);
client.ReIndex(record1Ref, new[] { new IndexEntry ("publication_number_idx") { { "publication_number", record1.publication_number } } });
Console.WriteLine("Added record1 at {0}", record1Ref.Id);
// add record2 to graph and index
var record2Ref = client.Create( record2,
new[] { new Cites(record1Ref) { Direction = RelationshipDirection.Outgoing } },
new[] { new IndexEntry("publication_number_idx") { {"publication_number", record2.publication_number } } });
Console.WriteLine("Added record2 at {0}", record2Ref.Id);
// 500 error here
client.QueryIndex<Record>("publication_number_idx", IndexFor.Node, #"START n=node:publication_number_idx(publication_number = ""2"") RETURN n;");
}
}
public class Cites : Relationship, IRelationshipAllowingSourceNode<Record>, IRelationshipAllowingTargetNode<Record>
{
public Cites(NodeReference targetNode)
: base(targetNode)
{
}
public const string TypeKey = "CITES";
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
I appear to be successful in adding the notes and updating the index. I am able to query the index using Cypher in the Console; however, when I use the same Cypher query with the Neo4J Client I get a 500 Internal Server Error on the query.
Unhandled Exception: System.ApplicationException: Received an unexpected HTTP status when executing the request.
The response status was: 500 Internal Server Error
The response from Neo4j (which might include useful detail!) was: {
"exception" : "NullPointerException", "fullname" :
"java.lang.NullPointerException", "stacktrace" : [
"org.apache.lucene.util.SimpleStringInterner.intern(SimpleStringInterner.java:54)",
"org.apache.lucen e.util.StringHelper.intern(StringHelper.java:39)",
"org.apache.lucene.index.Term.(Term.java:38)", "org.apache.luce
ne.queryParser.QueryParser.getFieldQuery(QueryParser.java:643)",
"org.apache.lucene.queryParser.QueryParser.Term(QueryPa
rser.java:1436)",
"org.apache.lucene.queryParser.QueryParser.Clause(QueryParser.java:1319)",
"org.apache.lucene.queryPar
ser.QueryParser.Query(QueryParser.java:1245)",
"org.apache.lucene.queryParser.QueryParser.TopLevelQuery(QueryParser.java
:1234)",
"org.apache.lucene.queryParser.QueryParser.parse(QueryParser.java:206)",
"org.neo4j.index.impl.lucene.IndexType .query(IndexType.java:300)",
"org.neo4j.index.impl.lucene.LuceneIndex.query(LuceneIndex.java:227)",
"org.neo4j.server.re
st.web.DatabaseActions.getIndexedNodesByQuery(DatabaseActions.java:889)",
"org.neo4j.server.rest.web.DatabaseActions.get
IndexedNodesByQuery(DatabaseActions.java:872)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.getIndexedNodesByQuery(R
estfulGraphDatabase.java:707)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.security.Secu
rityFilter.doFilter(SecurityFilter.java:112)" ] } at
Neo4jClient.GraphClient.SendHttpRequest(HttpRequestMessage request,
String commandDescription, HttpStatusCode[] ex pectedStatusCodes) in
c:\TeamCity\buildAgent\work\f1c4cf3efbf1b05e\Neo4jClient\GraphClient.cs:line
137 at Neo4jClient.GraphClient.QueryIndex[TNode](String indexName,
IndexFor indexFor, String query) in c:\TeamCity\buildA
gent\work\f1c4cf3efbf1b05e\Neo4jClient\GraphClient.cs:line 1168 at
Antares.Program.Main(String[] args) in c:\Users\Yellick
Chris\Documents\Visual Studio 2012\Projects\Antares\Antare
s\Program.cs:line 41
I'm not sure what the 500 error is about, but the solution to getting your query to work is to remove the 'QueryIndex' call (which is obsolete) and replace it with the Cypher notation, so:
var query = client.Cypher
.Start(new {n = Node.ByIndexLookup("publication_number_idx", "publication_number", "2")})
.Return<Record>("n");
var results = query.Results;
The query used in 'QueryIndex' has a different format to yours, if you look at the Neo4jclient Index Documentation you'd need to replace things like the = with : and wrap with ' like so:
client.QueryIndex<Record>("publication_number_idx", IndexFor.Node, #"START n=node:publication_number_idx('publication_number: ""2""') RETURN n;");
Not that that fixes the 500 error.

Resources