When I manually write the mutation query (in graphql plugin), it's working:
mutation {
createExam(input: {
data: {
name: "myName"
desription: "ggg"
questions: [{gf: "hello"}]
time: 2
subjects: ["5c468e2d61670b25b46ccdfe"]
}
}) {
exam {
name
desription
time
}
}
}
But if I code it and pass the exact same array I get an array of the exact same object I get [null, null]
let parsedQuestion = [{gf: "hello"}];
const response = await strapi.request('POST', '/graphql', {
data: {
query: `mutation {
createExam(input: {
data: {
name: "` + examInfo.newExamName + `"
desription: "` + examInfo.newExamDescription + `"
time: ` + Number(examInfo.newExamTime) + `,
questions: `+ parsedQuestion + `,
subjects: ["` + this.state.modalSubject._id + `"]
}
}) {
exam {
name
desription
time
questions
}
}
}`
}
How can it be? Could it be a bug? I also tried with JSON.stringify but then got an error and the mutation didn't even come through
Thanks a lot in advance
Constructing a query string this way is error-prone and dangerous; it opens you up to a slew of bugs and well-known security vulnerabilities. (What if newExamName is My "super-duper" exam!!!?)
GraphQL provides variables as a better approach to pass data in. In your case since you have a complex somewhat structured object, it's probably easiest to pass the whole input in as one object (other syntaxes are possible). I would expect this to look something like:
const response = await strap.request('POST', '/graphql', {
data: {
query: `mutation CreateExam($input: CreateExamInput!) {
createExam(input: $input) {
exam { name, desription, time, questions }
}
}`,
variables: {
input: {
name: examInfo.newExamName,
desription: examInfo.newExamDescription,
time: Number(examInfo.newExamTime),
questions: [{gf: "hello"}],
subjects: [this.state.modalSubject._id]
}
}
}
});
Now the HTTP client library can take responsibility for producing well-formed JSON from your input, and you're not performing tricky string manipulation.
Related
I am attempting to query my database called battery2221 but keep getting no results. I've read all the API documentation but can't seem to find the problem. Any feedback is greatly appreciated. Here is my page code:
import wixData from 'wix-data';
$w.onReady(function () {
//TO DO: Write Your Page Related Code Here:
})
export function button1_click(event, $w) {
//Add your code for this event here:
getVINInfo($w("#vininput").value)
.then(VINInfo => {
console.log(VINInfo)
let year = VINInfo.Results[9].Value;
let make = VINInfo.Results[6].Value;
let model = VINInfo.Results[8].Value;
let engine = VINInfo.Results[71].Value + " " + "Liters"
let car = VINInfo.Results[9].Value + " " + VINInfo.Results[6].Value + " " + VINInfo.Results[8].Value;
wixData.query("battery2221")
.include("title")
.eq("year", year)
.eq("make", make)
.eq("model", model)
.find()
.then((results) => {
console.log(results);
$w("#results").text = "Vehicle Specific Battery for Your" + " " + car;
Here is what the developers console shows when I run the code:
_query:
{...}
jsonTableCopy JSON
orderBy:
"undefined"
invalidArguments:
[...]
filterTree:
{...}
jsonTableCopy JSON
$and:
Array(3)
jsonTableCopy JSON
0:
{...}
jsonTableCopy JSON
year:
"2019"
1:
{...}
jsonTableCopy JSON
make:
"HONDA"
2:
{...}
jsonTableCopy JSON
model:
"Civic"
{
"$and": [
{
"year": "2019"
},
{
"make": "HONDA"
},
{
"model": "Civic"
}
]
}
This code has worked when the Wix language was Corvid but since switching to Velo the code no longer functions.
What I need is to query the battery2221 database for the year, make and model from the VINInfo results. The result should be the battery reference field (field name is “title” in the database) from the database.
Any and all feedback is appreciated 😊
I am using GraphQL within the GatsbyJs framework.
I have multiple files with JSON data. The structure of data is similar to this:
{
...,
"sections" / "menuSections"
}
That last field can be either 'sections' or 'menuSections' in each file. My current graphQL query looks like this:
{
allDataJson {
nodes {
menuSections
}
}
}
This query returns the correct 'menuSections', however, data-files which do not have 'menuSections' are returned as null. How can I get GraphQL to only return data from files which contain 'menuSections', i.e. how to return data within which 'menuSections' exist. I am looking for an operator like $exists.
if sections & menuSections are string or arrays of string, maybe you can filter for null:
{
"menuSections": "..."
}
// query
{
allDataJson(filter: {
menuSections: {
ne: null
}
}) {
nodes {
menuSections
}
}
}
If they are object, you can still filter for null, but it has to be applied to one of the field inside that object. If your objects don't have a common field, this won't work:
{
"menuSections": {
"menuSectionField": "..."
}
}
// query
{
allDataJson(filter: {
menuSections: {
menuSectionField: {
ne: null
}
}
}) {
nodes {
menuSections
}
}
}
If they are array of objects, you can do the same thing but with elemMatch:
{
"menuSections": [
{ "menuSectionField": "..." },
{ "other": "..." }
]
}
// query
allDataJson(filter: {
menuSections: {
elemMatch: {
menuSectionField: {
ne: null
}
}
}
}) { ... }
Worst case worst, I think you might be able to define some sort of custom types that ensure existence of menuSections so you can query allDataWithMenuSections etc., but if filter works it's much simpler.
It looks like there isn't an $exists operator in GraphQL. Instead what you can do is add some logic in the resolver to check if a field is not null. I found two older questions related to yours:
GraphQL query and check the returned data
GraphQL query: only include field if not null
This piece of node.js code is run against a Spark History Server API.
What its supposed to do is find any jobs where the name matches the value passed in by uuid and return the id for only that job.
What the below code actually does is if the uuid is found in any job name, the id for every job is returned.
I think this has something to do with the way I'm parsing the JSON but I'm not entirely sure.
How do I change this so it works as I would like it to?
var arrFound = Object.keys(json).filter(function(key) {
console.log("gel json[key].name" + json[key].name);
return json[key].name;
}).reduce(function(obj, key){
if (json[key].name.indexOf(uuid)) {
obj = json[key].id;
return obj;
}
reduce is the wrong method for that. Use find or filter. You can even do that in the filter callback that you already have. And then you can chain a map to that to get the id property values for each matched key:
var arrFound = Object.keys(json).filter(function(key) {
console.log("gel json[key].name " + json[key].name);
return json[key].name && json[key].name.includes(uuid);
}).map(function(key) {
return json[key].id;
});
console.log (arrFound); // array of matched id values
Note also that your use of indexOf is wrong. You need to compare that value with -1 (not found). But nowadays you can use includes which returns a boolean.
Note that with Object.values you list the objects instead of the keys, which is more interesting in your case:
var arrFound = Object.values(json).filter(function(obj) {
console.log("gel obj.name " + obj.name);
return obj.name && obj.name.includes(uuid);
}).map(function(obj) {
return obj.id;
});
console.log (arrFound); // array of matched id values
While the accepted answer provides working code, I feel it's worth pointing out that reduce is a good way to solve this problem, and to me makes more sense than chaining filter and map:
const jobs = {
1: {
id: 1,
name: 'job: 2a2912c5-9ec8-4ead-9a8f-724ab44fc9c7'
},
2: {
id: 2,
name: 'job: 30ea8ab2-ae3f-4427-8e44-5090d064d58d'
},
3: {
id: 3,
name: 'job: 5f8abe54-8417-4b3c-90f1-a7f4aad67cfb'
},
4: {
id: 4,
name: 'job: 30ea8ab2-ae3f-4427-8e44-5090d064d58d'
}
}
const matchUUID = uuid =>
(acc, job) => job.name.includes(uuid) ? [ ...acc, job.id ] : acc
const target = '30ea8ab2-ae3f-4427-8e44-5090d064d58d'
const matchTarget = matchUUID(target)
// [ 2, 4 ]
console.log(Object.values(jobs).reduce(matchTarget, []))
reduce is appropriate for these kinds of problems: taking a larger, more complex or complete value, and reducing it to the data you require. On large datasets, it could also be more efficient since you only need to traverse the collection once.
If you're Node version-constrained or don't want to use array spread, here's a slightly more 'traditional' version:
var result = Object.keys(jobs).reduce(
function (acc, key) {
if (jobs[key].name.includes(uuid)) {
acc.push(jobs[key].id)
}
return acc
},
[]
)
Note use of Object.keys, since Object.values is ES2017 and may not always be available. String.prototype.includes is ES2015, but you could always use indexOf if necessary.
Overview (simplified):
In my NodeJS server I've implemented the following GraphQL schema:
type Item {
name: String,
value: Float
}
type Query {
items(names: [String]!): [Item]
}
The client query then passes an array of names, as an argument:
{
items(names: ["total","active"] ) {
name
value
}
}
The backend API queries a mysql DB, for the "total" and "active" fields (columns on my DB table) and reduces the response like so:
[{"name":"total" , value:100} , {"name":"active" , value:50}]
I would like my graphQL API to support "ratio" Item, I.E: I would like to send the following query:
{
items(names: ["ratio"] ) {
name
value
}
}
or
{
items(names: ["total","active","ratio"] ) {
name
value
}
}
And return active / total as the calculated result of that new field ([{"name":"ratio" , value:0.5}]). What would be a generic way to handle the "ratio" field differently?
Should it be a new type in my schema or should I implement the logic in the reducer?
Joe's answer (append {"name":"ratio" , value:data.active/data.total} to the result once the result is fetched from database) would do it without making any schema changes.
As an alternative method or as a more elegant way to do it in GraphQL, the field names can be specified in the type itself instead of passing them as arguments. And compute ratio by writing a resolver.
So, the GraphQL schema would be:
Item {
total: Int,
active: Int,
ratio: Float
}
type Query {
items: [Item]
}
The client specifies the fields:
{
items {
total
active
ratio
}
}
And ratio can be calculated inside the resolver.
Here is the code:
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { graphql } = require('graphql');
const { makeExecutableSchema } = require('graphql-tools');
const getFieldNames = require('graphql-list-fields');
const typeDefs = `
type Item {
total: Int,
active: Int,
ratio: Float
}
type Query {
items: [Item]
}
`;
const resolvers = {
Query: {
items(obj, args, context, info) {
const fields = getFieldNames(info) // get the array of field names specified by the client
return context.db.getItems(fields)
}
},
Item: {
ratio: (obj) => obj.active / obj.total // resolver for finding ratio
}
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const db = {
getItems: (fields) => // table.select(fields)
[{total: 10, active: 5},{total: 5, active: 5},{total: 15, active: 5}] // dummy data
}
graphql(
schema,
`query{
items{
total,
active,
ratio
}
}`,
{}, // rootValue
{ db } // context
).then(data => console.log(JSON.stringify(data)))
You could set your resolver function up so it uses the second parameter - the arguments - to see if the name "ratio" is in your names array:
resolve: (root, { names }, context, fieldASTs) => {
let arrayOfItems;
// Contact DB, populate arrayOfItems with your total / active items
// if 'ratio' is within your name array argument, calculate it:
if (names.indexOf("ratio") > -1){
// Calculate ratio
arrayOfItems.push({ name: "ratio", value: calculatedRatio });
}
return(arrayOfItems);
}
I hope I understood your question correctly
I have a couple of Json objects and I need to delete one of them if this Json contains specific information. For an example I need to delete if state of the Json object is RUNNING.
INPUT
projects {
key: "ads_evenflow.opt"
value {
name: "ads_evenflow.opt"
state: COMPLETE
result: PASSED
}
}
projects {
key: "alexandria.opt"
value {
name: "alexandria.opt"
state: RUNNING
result: PASSED
}
}
projects {
key: "android.opt"
value {
name: "android.opt"
state: COMPLETE
result: PASSED
}
}
OUTPUT
projects {
key: "ads_evenflow.opt"
value {
name: "ads_evenflow.opt"
state: COMPLETE
result: PASSED
}
}
projects {
key: "android.opt"
value {
name: "androids.opt"
state: COMPLETE
result: PASSED
}
}
Your structure isn't an valid JSON. For such structures you need some more relaxed parser. Fortunately, the JSONY perl module could parse it. From the doc:
JSONY is a data language that is simlar to JSON, just more chill. All
valid JSON is also valid JSONY (and represents the same thing when
loaded), but JSONY lets you omit a lot of the syntax that makes JSON a
pain to write.
The following perl code does what you want.
#!/usr/bin/env perl
use 5.014;
use warnings;
use JSONY;
my $string = slurp_file();
my $data = JSONY->new->load( $string );
for my $proj (#{$data}) {
next unless ref($proj);
next if $proj->{value}->{state} eq 'RUNNING';
pretty_print_proj($proj);
}
sub pretty_print_proj {
my $p = shift;
say "project {";
say qq{\tkey: "$p->{key}"};
say "\tvalue {";
say "\t\t$_: ", $p->{value}->{$_} for (qw(name state result));
say "\t}";
say "}";
}
sub slurp_file {
#change this for your real case...
return do { local $/; <DATA>};
}
__DATA__
projects {
key: "ads_evenflow.opt"
value {
name: "ads_evenflow.opt"
state: COMPLETE
result: PASSED
}
}
projects {
key: "alexandria.opt"
value {
name: "alexandria.opt"
state: RUNNING
result: PASSED
}
}
projects {
key: "android.opt"
value {
name: "android.opt"
state: COMPLETE
result: PASSED
}
}
prints:
project {
key: "ads_evenflow.opt"
value {
name: ads_evenflow.opt
state: COMPLETE
result: PASSED
}
}
project {
key: "android.opt"
value {
name: android.opt
state: COMPLETE
result: PASSED
}
}