How to cast a string into an array using Yup - javascript

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.

Related

Is there a better way that to Cast mongoose.Types.ObjectID references to String in order to compare them with == in some cases? [duplicate]

I have a node.js application that pulls some data and sticks it into an object, like this:
var results = new Object();
User.findOne(query, function(err, u) {
results.userId = u._id;
}
When I do an if/then based on that stored ID, the comparison is never true:
if (results.userId == AnotherMongoDocument._id) {
console.log('This is never true');
}
When I do a console.log of the two id's, they match exactly:
User id: 4fc67871349bb7bf6a000002 AnotherMongoDocument id: 4fc67871349bb7bf6a000002
I am assuming this is some kind of datatype problem, but I'm not sure how to convert results.userId to a datatype that will result in the above comparison being true and my outsourced brain (aka Google) has been unable to help.
Mongoose uses the mongodb-native driver, which uses the custom ObjectID type. You can compare ObjectIDs with the .equals() method. With your example, results.userId.equals(AnotherMongoDocument._id). The ObjectID type also has a toString() method, if you wish to store a stringified version of the ObjectID in JSON format, or a cookie.
If you use ObjectID = require("mongodb").ObjectID (requires the mongodb-native library) you can check if results.userId is a valid identifier with results.userId instanceof ObjectID.
Etc.
ObjectIDs are objects so if you just compare them with == you're comparing their references. If you want to compare their values you need to use the ObjectID.equals method:
if (results.userId.equals(AnotherMongoDocument._id)) {
...
}
converting object id to string(using toString() method) will do the job.
The three possible solutions suggested here have different use cases.
Use .equals when comparing ObjectId on two mongoDocuments like this
results.userId.equals(AnotherMongoDocument._id)
Use .toString() when comparing a string representation of ObjectId to an ObjectId of a mongoDocument. like this
results.userId === AnotherMongoDocument._id.toString()
According to the above,i found three ways to solve the problem.
AnotherMongoDocument._id.toString()
JSON.stringify(AnotherMongoDocument._id)
results.userId.equals(AnotherMongoDocument._id)
The accepted answers really limit what you can do with your code. For example, you would not be able to search an array of Object Ids by using the equals method. Instead, it would make more sense to always cast to string and compare the keys.
Here's an example answer in case if you need to use indexOf() to check within an array of references for a specific id. assume query is a query you are executing, assume someModel is a mongo model for the id you are looking for, and finally assume results.idList is the field you are looking for your object id in.
query.exec(function(err,results){
var array = results.idList.map(function(v){ return v.toString(); });
var exists = array.indexOf(someModel._id.toString()) >= 0;
console.log(exists);
});
I faced exactly the same problem and i simply resolved the issue with the help of JSON.stringify() as follow:-
if (JSON.stringify(results.userId) === JSON.stringify(AnotherMongoDocument._id)) {
console.log('This is never true');
}
Mongoose from 5 to 6 migration guide:
"Mongoose now adds a valueOf() function to ObjectIds. This means you can now use == to compare an ObjectId against a string."
https://mongoosejs.com/docs/migrating_to_6.html#objectid-valueof
Here is an example that explains the issue and why it confusing for many. Only the first console log shows the object in its true form, and any other debuging/loging will be confusing because they look the same.
// Constructor for an object that has 'val' and some other stuffs
// related to to librery...
function ID(_val) {
this.val = _val;
this.otherStuff = "other stuffs goes here";
}
// function to help user get usefull infos from the Object
ID.prototype.toString = function toString() {
return `${this.val}`;
};
// Create new Object of type ID
const id = new ID('1234567');
console.log("my ID: ", id); // my ID: Object {
// val: "1234567",
// otherStuff: "other stuffs goes here"
// }
console.log("my ID: " + id); // my ID: 1234567
console.log(id === '1234567'); // false
console.log(id == '1234567'); // true
console.log(id.toString() === '1234567'); //true
console.log(`${id}` === '1234567'); // true
console.log(new ID('1234567') === id); // false

GraphQL field with space

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")
}

Override value in the form using yup when condition applies

I am trying to build a yup validation schema, but I couldn't figure out how to transform value to a default every time when it doesn't match the schema.
I was trying to check is value higher than 0 and not undefined, so then this form value would be required, otherwise I would like to set it to the empty string.
This is my code:
reason: string()
.when('value', {
is: val => Boolean(val),
then: string().required('This field is required'),
otherwise: string().transform((value) => value ? value : '')
})
Thanks a lot!

Firestore select where is not null

I'm using firebase to manage my project and I cannot get to create a query with a where clause where some value is not null.
Example: I have a collection of employees. Each have a list of equipments as an object where the key is the equipment id and the value a color.
user = {
firstName: 'blabla',
lastName: 'bloblo',
equipments: {
123: 'blue',
124: 'red'
}
}
I would like to get all the employees who has a certain equipment in the equipments. Lets say 123.
It comes to Select * from Employees where equipments.123 is not null. I've tried:
firestore.collection('employees').where(`equipments.${equipmentId}`, '!=', null)
but it's not working.
I can't seems to make it work. Can you help me.
Update Sep 2020: v7.21.0 introduces support for not equals (!=) queries!
That means you can now use the code bellow:
firestore.collection('employees').where(`equipments.${equipm‌​entId}`, '!=', null)
Previous answer:
Firestore has no "not equal" operator. But looking at the logic, what you're trying to do is query for values which are String, and not null. Firestore can do that if you pass a String to the where() function.
So what you can do is query for values lower than \uf8ff. That's a very high code point in the Unicode range. Since it is after most regular characters in Unicode, this query will return everything that is of type String:
firestore.collection('employees').where(`equipments.${equipm‌​entId}`, '<', '\uf8ff')
Or you can simply query for values higher than "" (empty String):
firestore.collection('employees').where(`equipments.${equipm‌​entId}`, '>', '')
FWIW since Firestore indexes are sparse [1] you can do something like:
firestore.collection('employees').orderBy(`equipments.${equipm‌​entId}`)
And you'll only get documents where that field is set. If you're explicitly setting the fields to null in your database, however, you'll get null values first, so if you want to avoid explicit null values you can do:
firestore.collection('employees').orderBy('equipments.${equipm‌​entId}').startAfter(null);
[1]: Sparse in the sense that if the field is not present in the document, Cloud Firestore does not create an index entry in the index for that document/field.

How to set a default value

Here is how I extend the Immutable.Record
import Immutable from 'immutable';
const DATA_DEFAULTS = {
id: String,
data: String,
ref_id: String,
};
export default class Data
extends Immutable.Record(DATA_DEFAULTS) {}
If the json representation does not contain ref_id, then when I try to print the value of the field in the console of Chrome devtool, I will get:
> data.ref_id
String() { [native code] }
I think it is the reason why I cannot filter out entities with empty ref_id in a list
a = [] // a list of data
b = a.filter(x => x.ref_id != '') // b is the same as a
I suppose it can be fixed by either:
set a default value to a field in the Immutable.Record instance when the field is missing in the json data
use another way to detect if the field is not set in the instance
For the second approach, I have tried, for example, data.has('ref_id') and it always report true regardless if the field has any valid value.
My questions:
1) Can I set a default value in a Immutable.Record?
2) How to filter on field that may or may not exist in the Immutable.Record
3) What is the right way to test the existence of valid value in a field in an instance of Immutable.Record?
You are explicitly setting the default values of each of the fields to the function String. String is not a data type here, if that's what you thought. What you seem to want is
const DATA_DEFAULTS = {
id: '',
data: '',
ref_id: '',
};
1) Can I set a default value in a Immutable.Record?
Exactly how you already did, you just need to set the correct default value.
2) How to filter on field that may or may not exist in the Immutable.Record
Fields on a record always exist. From the docs:
Records always have a value for the keys they define. removeing a key from a record simply resets it to the default value for that key.
3) What is the right way to test the existence of valid value in a field in an instance of Immutable.Record?
You can use a default value that you know would not be explicitly assigned to the field otherwise. That could be null or a symbol.

Categories

Resources