GraphQL field with space - javascript

I'm trying to use a new GraphQL server on a very old legacy code, where the column names have spaces, e.g: "Poke ball"
I've been trying to run this query:
query{{userItems{Poke ball}}}
and got this:
extensions: {code: "GRAPHQL_VALIDATION_FAILED",…}
locations: [{line: 1, column: 12}]
message: "Cannot query field "Poke" on type "UserItems"."
I've tried to use quotes with no luck, any idea if this is supported / workaround?
Thanks.

The GraphQL specification requires that names of things (fields, types, arguments, etc.) only contain letters, numbers and underscores. A field name cannot contain a space because spaces and other whitespace are used to separate individual tokens. In other words, one or more spaces or line returns are used to indicate that, for example, one field's name has terminated and another has begun.
If your underlying data layer is returning data with keys that contain spaces, you need to define a field with an allowed name (like pokeball) and then write a resolver for that field. For example:
const UserItems = new GraphQLObjectType({
name: "UserItems",
fields: () => ({
pokeball: {
type: Pokeball,
resolve: (parent) => {
// The parent value will be whatever the parent field resolved to.
// We look for a property named "Poke ball" on that value and return it.
return parent["Poke ball"];
},
},
...
}),
});

or in the schema, do this
directive #fetch(from : String!) on FIELD_DEFINITION
type Product {
Pokeball : String #fetch(from:"Poke ball")
}

Related

How to cast a string into an array using Yup

So whenever I receive a string I want to store it as an array. But I've got no luck so far, i tried to do with cast and with transform. I just need some clarity to get the things going.
Is transform and cast the same thing? How to cast a string into an array using Yup?
const schema = yup.object().shape({
types: yup
.array('type must be an array.')
.of(
yup
.string('the array must contains only strings.')
.transform(value =>
typeof value === 'string' || value instanceof 'string'
? [value]
: value,
)
.matches(/(writer|artist)/, null),
)
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
name: yup
.string('name must be a string.')
.min(3, 'too short'),
});
let obj = {
name: 'Kentarou Kishima',
types: 'artist',
}
//returns ValidationError
obj = schema.cast(obj, { stripUnknown: true });
//trying to just validate results in the same error
schema
.validate(obj)
.then(() => {
next();
})
.catch(function (e) {
console.log(e);
return something;
});
ValidationError: types must be a array type, but the final value was: null (cast from the value "artist")
Edit:
I fixed minor typo btw.
Well, I removed the matches line and it keeps returning the same Error. So now I am thinking since it's receiving a string and not an array, when it goes into the transform function it is going to search for the array items to cast, but there's none because it got a string. So it's well likely that the transform function should be side-by-side with array() and not inside it.
The code looks like this now, but I'm still getting the Error with or without matches():
.array('types must be an array.')
.of(
yup
.string('the array must contains only strings.')
.matches(/(^writer$|^artist$)/) //I improved the regex pattern
)
.transform(value =>
typeof value === 'string' || value instanceof String
? [value]
: value,
)
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
To make things clearer, these are the type of input I am expecting:
let obj = {
name: 'Kentarou Kishima',
types: 'artist', //should cast
types: ['artist'], //should pass
types: ['artist', 'writer'], //should pass
types: '', //should reject
types: ['something'], //should reject
types: ['artist', 'something', 'writer'], //should reject
types: ['artist', 'artist'], // should reject, but i will put a test() later on.
}
The order of operations on the types property is out of order. It follows:
Ensure array element is string.
If the value is a string, convert it to an array containing itself.
If the array of the single string matches the regular expression, continue. If it does not, report null.
If you truly want an array of single-element arrays, then you can keep the transform, but you'll want to move the matches() above the transform() as the arrays that result from the transform() will never match the regular expression, and so matches() always returns your null.
const schema = yup.object().shape({
types: yup
.array('type must be an array.')
.of(
yup
.string('the array must contains only strings.')
.matches(/(writer|artist)/, null)
.transform(value =>
typeof value === 'string' || myVar instanceof 'string'
? [value]
: value,
),
)
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
name: yup
.string('name must be a string.')
.min(3, 'too short'),
});
After messing around with the documentation I found the answer. In this case is enough to use the ensure() function, basically it will take anything that is not an array and put it into an array. After that, the matches() function will reject anything that does not follow the regex pattern.
yup
.array('types must be an array.')
.of(
yup
.string('the array must contains only strings.')
.matches(/(^writer$|^artist$)/)
)
.ensure()
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
Edit:
Just a disclaimer, the way it is with min(1) enabled, this property always will be required even though there's no required(), even specifying notRequired() in the object doesn't do the trick.
This is a known issue in yup
https://github.com/jquense/yup/issues/1267
In my case, I need this validator for my POST (all required) and PUT (at least one required) requests, in POST requests I use it the way it is, in PUT requests I dynamically add the rules checking for those present in the req.body.

Problem in handling many field through function in mongoose

I want to generate field in mongoose with function.
Because there are many fields, but they are much the same, I wanna use function to create them to keep code short.
I wrote a function, but there exists lints.
import { Schema } from 'mongoose'
function fieldGen(name, type="string", isRequired=true) {
var field = {}
field[name] = {
type: type,
required: isRequired
}
return {...field}
}
const testSchema = new Schema({
fieldGen("firstname")
fieldGen("lastname")
fieldGen("location")
})
In VS Code, Problem shows as below
Identifier expected. ts(1003) [20, 12]
I expect first argument "firstname" matches name in function, and return object.
You're adding values to your testSchema object without giving them names.
Also, you're spreading the properties of the field object into a new object literal. That doesn't accomplish anything. Just returning the field object would yield the same result.
I see what you are trying to do. If you debug this in smaller steps and take a closer look at the data you are handling, I think you'll figure it out on your own.

Mongodb Node.js driver embedded arrays query [duplicate]

I have a problem when querying mongoDB with nested objects notation:
db.messages.find( { headers : { From: "reservations#marriott.com" } } ).count()
0
db.messages.find( { 'headers.From': "reservations#marriott.com" } ).count()
5
I can't see what I am doing wrong. I am expecting nested object notation to return the same result as the dot notation query. Where am I wrong?
db.messages.find( { headers : { From: "reservations#marriott.com" } } )
This queries for documents where headers equals { From: ... }, i.e. contains no other fields.
db.messages.find( { 'headers.From': "reservations#marriott.com" } )
This only looks at the headers.From field, not affected by other fields contained in, or missing from, headers.
Dot-notation docs
Since there is a lot of confusion about queries MongoDB collection with sub-documents, I thought its worth to explain the above answers with examples:
First I have inserted only two objects in the collection namely: message as:
> db.messages.find().pretty()
{
"_id" : ObjectId("5cce8e417d2e7b3fe9c93c32"),
"headers" : {
"From" : "reservations#marriott.com"
}
}
{
"_id" : ObjectId("5cce8eb97d2e7b3fe9c93c33"),
"headers" : {
"From" : "reservations#marriott.com",
"To" : "kprasad.iitd#gmail.com"
}
}
>
So what is the result of query: db.messages.find({headers: {From: "reservations#marriott.com"} }).count()
It should be one because these queries for documents where headers equal to the object {From: "reservations#marriott.com"}, only i.e. contains no other fields or we should specify the entire sub-document as the value of a field.
So as per the answer from #Edmondo1984
Equality matches within sub-documents select documents if the subdocument matches exactly the specified sub-document, including the field order.
From the above statements, what is the below query result should be?
> db.messages.find({headers: {To: "kprasad.iitd#gmail.com", From: "reservations#marriott.com"} }).count()
0
And what if we will change the order of From and To i.e same as sub-documents of second documents?
> db.messages.find({headers: {From: "reservations#marriott.com", To: "kprasad.iitd#gmail.com"} }).count()
1
so, it matches exactly the specified sub-document, including the field order.
For using dot operator, I think it is very clear for every one. Let's see the result of below query:
> db.messages.find( { 'headers.From': "reservations#marriott.com" } ).count()
2
I hope these explanations with the above example will make someone more clarity on find query with sub-documents.
The two query mechanism work in different ways, as suggested in the docs at the section Subdocuments:
When the field holds an embedded document (i.e, subdocument), you can either specify the entire subdocument as the value of a field, or “reach into” the subdocument using dot notation, to specify values for individual fields in the subdocument:
Equality matches within subdocuments select documents if the subdocument matches exactly the specified subdocument, including the field order.
In the following example, the query matches all documents where the value of the field producer is a subdocument that contains only the field company with the value 'ABC123' and the field address with the value '123 Street', in the exact order:
db.inventory.find( {
producer: {
company: 'ABC123',
address: '123 Street'
}
});

Joi 'when/is' syntax - how to check for type?

I'm using the Joi library to validate an object. I want to make a certain property required when another optional property (at the same level of the same object) is of a certain type, e.g. string. The Joi docs show this example:
const schema = {
a: Joi.when('b', { is: true, then: Joi.required() }),
b: Joi.boolean()
};
However, rather than checking that b (for instance) is true, I'd like to check whether it is a string. I've tried this:
const schema = {
a: Joi.when('b', { is: Joi.string(), then: Joi.required() }),
};
But it doesn't seem to work. If I remove b completely from the object Joi still seems to expect a to be required. If b isn't in the object I don't want any validation placed on a.
I can't find any other examples of people doing this - can anyone help?
We managed to solve this using object.with. If one key is present (e.g. a), then its peers must be present too (e.g. b).
However, it's not ideal because while we were able to specify that a should be a Joi.string(), object.with is looking for its mere presence rather than its type. So if a non-string a is present a 'should be a string' error will be thrown for a. It should be perfectly fine for a not to be a string - all that should mean is that b is not mandatory. I hope that makes sense.

Where clause using included relation in `OR` statement

I'm moving a project from MySQL to Postgres using Sequelize, and there is one thing that has failed straight away. It looks to be due to the fact that 'User' isn't included anywhere.
Edit: the error message is 'missing FROM-clause entry for table users'.
The code for sequelize is:
List.model.findOne({
where: {
id: listId,
$or: [
{ open: true },
['Users.id = ?', [userId]], // <-- the line I think is breaking this.
],
},
include: [{
model: db.models.User
}],
});
I think this is due to User not being included in the FROM clause. But, I'm not sure how to make Sequelize do the right thing.
The query generated by Sequelize is:
SELECT "List"."id",
"List"."name",
"List"."image",
"List"."slug",
"List"."open",
"List"."password",
"List"."createdAt",
"List"."updatedAt",
"List"."OwnerId",
"Users"."id" AS "Users.id",
"Users"."firstName" AS "Users.firstName",
"Users"."lastName" AS "Users.lastName",
"Users"."email" AS "Users.email",
"Users"."password" AS "Users.password",
"Users"."image" AS "Users.image",
"Users"."facebookId" AS "Users.facebookId",
"Users"."googleId" AS "Users.googleId",
"Users"."createdAt" AS "Users.createdAt",
"Users"."updatedAt" AS "Users.updatedAt",
"Users.UserList"."createdAt" AS "Users.UserList.createdAt",
"Users.UserList"."updatedAt" AS "Users.UserList.updatedAt",
"Users.UserList"."ListId" AS "Users.UserList.ListId",
"Users.UserList"."UserId" AS "Users.UserList.UserId"
FROM "Lists" AS "List"
LEFT OUTER JOIN ("UserList" AS "Users.UserList"
INNER JOIN "Users" AS "Users" ON "Users"."id" = "Users.UserList"."UserId") ON "List"."id" = "Users.UserList"."ListId"
WHERE "List"."id" = 13
AND ("List"."open" = TRUE
OR (Users.id = 1234));
Try to wrap Users.id with double quotes like "Users"."id", because in PostgreSQL all unquoted identifiers (table names, fields) will be folded to lowercase and PostgreSQL is case-sensitive: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
Quoting an identifier also makes it case-sensitive, whereas unquoted names are always folded to lower case. For example, the identifiers FOO, foo, and "foo" are considered the same by PostgreSQL, but "Foo" and "FOO" are different from these three and each other. (The folding of unquoted names to lower case in PostgreSQL is incompatible with the SQL standard, which says that unquoted names should be folded to upper case. Thus, foo should be equivalent to "FOO" not "foo" according to the standard. If you want to write portable applications you are advised to always quote a particular name or never quote it.)

Categories

Resources