How to use String properties in fastest-validator - node.js

Link: https://www.npmjs.com/package/fastest-validator
I'm using fastest-validation in my NodeJS application. I've been having great success with it. Unfortunately, I'm running into an issue that I can't seem to figure out.
If you take a look under String (located here: https://www.npmjs.com/package/fastest-validator#user-content-string)
I'm attempting to utilize the property numeric as I have a string that is a number that I would like to validate. I'm not able to find any examples, so I was left with the assumption I must set the property to true. This doesn't appear to work as I've tried to validate this theory by also setting another field that is a numeric string and set the property of alpha to true. I fully expected my 'label' to pass and my 'value' field to fail. But both passed.
How are you supposed to used these properties?
See below for my code:
buildSchema.catalogPages = {
type: "array", items: {
type: "object", props: {
label: { type: "string", empty: false, numeric: true },
value: { type: "string", empty: false, alpha: true }
}
}
}
const v = new Validator()
const check = v.compile(buildSchema)
check(valuesToCheck)
Here is my data:
const valuesToCheck = [
{
label: "9"
value: "9"
},
{
label: "12"
value: "12"
},
EDIT
I just figured out my issue. I have a handler function that checks my schemas and valuesToCheck. What I was doing was returning the check(valuesToCheck) with the assumption it simply returns true or false. But in fact, if the check fails, it returns an array of what failed (which is awesome). I'm going to accept tam.teixeira's answer as it helped me realized I need to update my handler to check if it's a Boolean true or an array (aka it failed).

I think that should not be something related to the schema itself, in the following example:
const Validator = require('fastest-validator');
const schema = {
myItems: {
type: 'array',
items: {
type: 'object',
props: {
label: { type: 'string', empty: false, numeric: true },
value: { type: 'string', empty: false, alpha: true },
},
},
},
};
const valuesToCheck = {
myItems: [
{
label: '9',
value: '9',
},
{
label: '12',
value: '12',
},
],
};
const v = new Validator();
const check = v.compile(schema);
console.log(JSON.stringify(check(valuesToCheck), null, 4));
Indeed it fails validation with errors:
$> node fastestValidatorTest.js
[
{
"type": "stringAlpha",
"message": "The 'myItems[0].value' field must be an alphabetic string.",
"field": "myItems[0].value",
"actual": "9"
},
{
"type": "stringAlpha",
"message": "The 'myItems[1].value' field must be an alphabetic string.",
"field": "myItems[1].value",
"actual": "12"
}
]
So i got your expected behaviour: "'label' to pass and my 'value' field to fail"
So my conclusion is that maybe something is not right in the buildSchema.catalogPages part, to me it feels a bit suspiscious and i also got the expected behaviour with your example, but i've changed the schema object to be simpler.
PS: Thanks for the question i didn't knew the library, so learned something new, which is cool

Related

Sequelize included Model result keys are strings

Forgive my limited knowledge im about a week into using Sequelize,
Models.PlannerModel.Builds.findAll({
raw: true,
where: {
ProposedDelivery: { [Op.gt]: moment().format("YYYY-MM-DD") },
description: { [Op.ne]: null },
description: { [Op.ne]: " " },
description: { [Op.not]: null },
},
include: [
{
model: Models.PlannerModel.Unit,
required: true
},
],
the result from the above is as you would expect except all the keys for the fields in the includes are as strings so referencing them in my Pug template/class has to be done with brackets
overall not the end of the world just wondering if im doing something wrong ?
Cheers!
Turn off raw to get nested model objects and also to get plain objects use get({ plain: true}) for each returned model instance:
const builds = await Models.PlannerModel.Builds.findAll({
where: {
ProposedDelivery: { [Op.gt]: moment().format("YYYY-MM-DD") },
[Op.and]: [{
description: { [Op.ne]: null },
}, {
description: { [Op.ne]: " " },
}, {
description: { [Op.not]: null },
}
]
},
include: [
{
model: Models.PlannerModel.Unit,
required: true
},
]
})
const plainBuilds = builds.map(x => x.get({ plain: true }))
Please pay attention that I changed conditions with description. In your version of conditions only the last one will work because JS saves only the last key if there are several same keys in the same object.

How can I cast a string to number in an response using axios?

I am using Axios to execute a GET request to a public API. This API return a numeric value as string. I need to cast this value a numeric value. My application runs using tsc, so I expect the result of my object to be a numeric value, but it's not.
Axios Response
[
{
"name": "foo1",
"value": "8123.3000"
},
{
"name": "foo2",
"value": "5132.2003"
},
{
"name": "foo3",
"value": "622.0000"
}
]
Expected Output
[
{
"name": "foo1",
"value": 8123.3
},
{
"name": "foo2",
"value": 5132.2003
},
{
"name": "foo3",
"value": 622
}
]
My code is very simple,
interface MyObj {
myString: string;
myNumber: number;
}
(async () => {
let { data }: AxiosResponse<MyObj> = await axios.get<MyObj>("/public/data");
console.log(data);
})();
I try to use interface, class, the interface Number. Nothing worked.
I leave an example of code to try it.
How can I get the expected output without manually converting each value one by one?
Axios does not change the type of properties in the response. Please verify that the server does not send you the wrong types.
Edit
From your comment, it seems that the server sends you the vslue as string instead of as number. In this case I would suggest working with Ajv (https://github.com/ajv-validator/ajv) so you can create a schema that describes how the response looks like. Ajv cn also transform the value from string to number for you:
const Ajv = require('ajv')
const ajv = new Ajv({
// allow chaning the type of some values from type X to type Y. depends on the source and target type:
// https://ajv.js.org/guide/modifying-data.html#coercing-data-types
// X => Y rules: https://ajv.js.org/coercion.html
coerceTypes: true,
})
const schema = {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'number' },
},
required: ['name', 'value'],
additionalProperties: false,
},
}
const data = { name: 1, value: '1.1' }
console.log(typeof data.value === 'number') // false!
const valid = ajv.validate(schema, data)
if (!valid) {
console.log(ajv.errors)
process.exit(1)
}
console.log(typeof data.value === 'number') // true!
When declaring that your axios request returns a certain type; this will be checked at compile time and syntax checking. It does not however do this at runtime. If you know that the axios request is returning something different than your interface, you need to convert to that format first. You can do this using the second argument of the JSON.parse function like so:
interface Item {
name: string;
value: number;
}
let responseString = `
[
{
"name": "foo1",
"value": "8123.3000"
},
{
"name": "foo2",
"value": "5132.2003"
},
{
"name": "foo3",
"value": "622.0000"
}
]`
const items: Item[] = JSON.parse(responseString, (key, value) => {
const propertiesToCast = ["value"] // Which properties should be converted from string to number
if (propertiesToCast.includes(key)) {
return parseFloat(value)
}
return value
});
console.log(items)

AJV JsonSchema validator reports seemingly incorrect error

I'm using AJV to validate a HTTP request payload against a schema. However, I see an error reported that I was not expecting. This is a code example to demonstrate the issue:
const schema = {
type: 'array',
minItems: 1,
items: {
anyOf: [
{
type: 'object',
properties: {
op: {
enum: ['replace']
},
path: {
type: 'string',
pattern: '/data/to/foo/bar',
},
value: {
type: 'string',
},
},
},{
type: 'object',
properties: {
op: {
enum: ['replace']
},
path: {
type: 'string',
pattern: '/data/to/baz',
},
value: {
type: 'object',
required: ['foo', 'bar'],
properties: {
foo: {
type: 'string',
},
bar: {
type: 'string',
},
}
}
}
}
],
},
}
const validator = new ajv()
const compiledValidator = validator.compile(schema)
const data = [
{ // this object should pass
op: 'replace',
path: '/data/to/foo/bar',
value: 'foo',
},
{ // this object should fail in the `value` mismatch (missing required attribute)
op: 'replace',
path: '/data/to/baz',
value: {
foo: 'bar',
},
},
]
compiledValidator(data)
console.log(compiledValidator.errors)
The schema defines a number of objects to which an incoming list of data objects should match. The first data item matches the schema (first item schema), however the second data item misses a required attribute (bar) in the value object.
When I run the above code I get the following output:
[
{
instancePath: '/1/path',
schemaPath: '#/items/anyOf/0/properties/path/pattern',
keyword: 'pattern',
params: { pattern: '/data/to/foo/bar' },
message: 'must match pattern "/data/to/foo/bar"'
},
{
instancePath: '/1/value',
schemaPath: '#/items/anyOf/1/properties/value/required',
keyword: 'required',
params: { missingProperty: 'bar' },
message: "must have required property 'bar'"
},
{
instancePath: '/1',
schemaPath: '#/items/anyOf',
keyword: 'anyOf',
params: {},
message: 'must match a schema in anyOf'
}
]
I understand the 2nd and the 3rd (last) errors. However, The first error seems to indicate that the path doesn't match path requirements of the first item schema. It is true that the 2nd data item doesn't match the 1st schema item but I don't seem to understand how it is relevant. I would assume that the error would be focused around the value, not the path since it matches on the path schemas.
Is there a way to get the error reporting more focused around the errors that matter?
There is no way for the evaluator to know whether you intended the first "anyOf" subschema to match or the second, so the most useful thing to do is to show you all the errors.
It can be confusing because you don't need to resolve all the errors, just some of them, which is why some implementations also offer a heirarchical error format to make it more easy to see relationships like this. Maybe if you request that ajv implement more of these error formats, it will happen :)
You can see that all of the errors pertain to the second item in the data by looking at the instancePath for each error, which all start with /1. This is the location within the data that generated the error.
So let's look at the second item and compare it against the schema.
{ // this object should fail in the `value` mismatch (missing required attribute)
op: 'replace',
path: '/data/to/baz',
value: {
foo: 'bar',
},
}
The schema says that an item should either (from the anyOf) have
path: '/data/to/foo/bar' and value: { type: 'string' }, or
path: '/data/to/baz' and value: { required: [ 'foo', 'bar' ] }
The reported errors are:
The first case fails because path is wrong.
The second case fails because /value/bar is not present.
The last error is just the anyOf reporting that none of the options passed.

Boosting results based on a boolean field

I have tons of articles in various stores. Some of these articles are own brand articles and should be ranked higher than other articles in my elasticsearch search results (both ownbrand and non ownbrand should be shown however.)
I already tried different approached with field_value_factor but that doesn't seem to go well with a boolean field.
I also tried the approached solution in Boosting an elasticsearch result based on a boolean field value but that didn't worked well for me. The results with the ownBrand approach were still way lower ranked then a lot of non ownBrand articles.
Index:
schema: {
articleId: { type: 'text' },
brandId: { type: 'text' },
brandName: { type: 'text' },
countryId: { type: 'text' },
description: { type: 'text' },
isOwnBrand: { type: 'boolean' },
stores: { type: 'keyword' },
},
};
Query:
query: {
function_score: {
query: {
bool: {
must: {
multi_match: {
query: searchterm,
fields: ['name^5', 'name.ngram'],
fuzziness: 'auto',
prefix_length: 2,
},
},
filter: [{ term: { stores: storeId } }],
},
},
},
},
};
The result should prioritize fields with isOwnBrand = true at the top while still showing relevant articles with isOwnBrand = false below.
I am a bit lost on how to handle this.
You can use Field Value factor. Below should work fine even on a boolean field as well. try it
{
"query": {
"function_score": {
"query" {...}, #your normal query as in question
#add below after query
"field_value_factor": {
"field": "isOwnBrand",
"factor": 1.2,
"modifier": "sqrt",
"missing": 1
}
}
}
}
One caveat i can think of but haven't tested - since false is 0, above script will score down all documents with false to 0 score, which messes up scoring. You could either make the isOwnBrand a number field and set priority starting 1
OR you could also use script_score

Use a GQLObject as arg for a mutation?

I have following mutation on serverside (nodeJS) (RequiredDataType is imported):
mutationA: {
type: MutationResponseType,
args: {
id: {
type: new GraphQLNonNull(GraphQLString)
},
name: {
type: new GraphQLNonNull(GraphQLString)
},
requiredData: {
type: new GraphQLNonNull(new GraphQLList(RequiredDataType))
}
},
async resolve(parentValue, {
id,
name,
requiredData
}, req) {
// Some Magic Code
}
},
The RequiredDataType is coded as follow (All GraphQL things are imported :)):
const RequiredDataType = new GraphQLObjectType({
name: 'RequiredDataType',
fields: {
name: {
type: GraphQLString
},
value: {
type: GraphQLString
},
required: {
type: GraphQLBoolean
}
}
});
module.exports = RequiredDataType;
When I use this code I get the following error: "module initialization error: Error"
If I change the RequiredDataType in the mutation to GraphQLString it works without any error but I can't use the object which I need :)
At the end I will send and process following data structure:
{
"name": "Hallo"
"id": "a54de3d0-a0a6-11e7-bf70-7b64ae72d2b6",
"requiredData": [
{
"name": "givenName",
"value": null,
"required": true
},
{
"name": "familyName",
"value": null,
"required": false
}
]
}
On the client (reactJS with apollo-client) I use the following gql-tag code:
export default gql`
mutation MutationA($id: String!, $name: String!, $requiredData: [RequiredDataType]!){
mutationA(id: $id, name: $name, requiredData: $requiredData) {
id,
somethingElse
}
}
`;
But in the first place it crashes on the mutation declaration on the server. So is it not possible to use and GQLObject as an argument at an mutation or where is my error in the code?
Thank you for your help!
Best,
Fabian
Unfortunately, a type cannot be used in place of an input, and an input cannot be used in place of a type. This is by design. From the official specification:
Fields can define arguments that the client passes up with the query,
to configure their behavior. These inputs can be Strings or Enums, but
they sometimes need to be more complex than this.
The Object type defined above is inappropriate for re‐use here,
because Objects can contain fields that express circular references or
references to interfaces and unions, neither of which is appropriate
for use as an input argument. For this reason, input objects have a
separate type in the system.
You can check this answer for more details as to the why
You'll need to define RequiredDataType as a GraphQLInputObjectType, not a GraphQLObjectType, to get your mutation working. If you need it as a GraphQLObjectType too, you'll need to declare them as two separate types -- something like RequiredDataType and RequiredDataInput.

Resources