Meteor SimpleSchema says random stuff is valid - validation

I'm using aldeed:collection2 and aldeed:simple-schema packages. I want to validate a doc against the schema. My schema contains e.g. a string field with allowedValues array and an array of nested object, described with sub-schema. Like this:
...
type: {
type: String,
allowedValues: [ 'A', 'B', 'C' ],
defaultValue: 'A',
index: 1,
},
nestedStuff: {
type: [ new SimpleSchema(nestedStuffSchema.schema(Meteor, SimpleSchema)) ],
defaultValue: [],
},
...
I have a 'bad' doc which has e.g. "D" in type field and invalid nested array items.
At client I'm trying to:
Contacts.simpleSchema().namedContext().validate(badDoc);
and it returns true. SimpleSchema says the doc is valid even though its fields do not abide to schema.
Validating 'bad' type field individually also returns true.
What am I doing wrong? Why could SimpleSchema assume random stuff to be valid?

if you want to validate an array of strings you need to keep String inside [ ].See the below code it may help
type: {
type: [String],
allowedValues: [ 'A', 'B', 'C' ],
defaultValue: ['A'],
index: 1,
},
nestedStuff: {
type: [ new SimpleSchema(nestedStuffSchema.schema(Meteor,SimpleSchema)) ],
defaultValue: [],
},
Thanks

Related

Joi validate object with unknown keys

How can I validate an object with unknown keys with Joi? Here is an example of my data
{
'5520': {
name: 'string',
order: 0,
},
'8123': {
name: 'string',
order: 5,
},
'8219': {
name: 'string',
order: 1,
},
'10113': {
name: 'string',
order: 2,
},
'14538': {
name: 'string',
order: 6,
},
'15277': {
name: 'string',
order: 4,
},
'16723': {
name: 'string',
order: 3,
}
I would like to validate each one of those unknown keys to make sure they all contain name, order and a few other properties, they all must have the same properties.
I read their documentation, just can't figure out how to deal with those unknown keys.
You can use object.pattern:
Joi.object().pattern(
/\d/,
Joi.object().keys({
name: Joi.string().valid('string'),
order: Joi.number().integer(),
})
)
This means that your keys must be numbers, and their value must be the object defined with name and order, which means, any of your example data will pass.

How can you implement "temporary placeholders" in data structures?

I am trying to boil down a pretty complicated problem into its essence so I can get some help on how to model or architect it. Here it goes.
Say we are compiling functions in this order:
function test() {
sum(mul(2, 3), mul(3, 4))
}
function sum(a, b) {
return a + b
}
function mul(a, b) {
return a * b
}
We end up with an AST something like this:
{
type: 'Program',
blocks: [
{
type: 'Function',
name: 'test',
args: [],
body: [
{
type: 'Call',
function: 'sum',
args: [
{
type: 'Call',
function: 'mul',
...
},
...
]
}
]
},
{
type: 'Function',
name: 'mul',
args: ...,
body: ...
},
{
type: 'Function',
name: 'sum',
args: ...,
body: ...
}
]
}
Now we start compiling this AST into more easily manipulated objects, with direct pointers to functions and such. The final result might look like this:
{
type: 'Program',
blocks: [
{
type: 'Function',
name: 'test',
args: [],
body: [
{
type: 'Call',
pointer: 2,
args: [
{
type: 'Call',
pointer: 1,
...
},
...
]
}
]
},
{
type: 'Function',
name: 'mul',
args: ...,
body: ...
},
{
type: 'Function',
name: 'sum',
args: ...,
body: ...
}
]
}
The main difference is that the "final" version has a pointer to the index where the function is defined. This is a very rough sketch. The reality would be there could be multiple passes required to resolve some context sensitivity, and so you end up with multiple partial/intermediate data structures in the transition from the AST to the final compiled object.
How do you make types to deal with this situation? The ideal is that there is an "initial" and a "final" type. The reality is that on our first pass, we have a "placeholder type" for the function calls, which we can't resolve until we have completed our first pass. So on the first pass, we have:
function: String
On the second pass we change it to:
pointer: Int
How do you reconcile this? How do you architect the algorithm so as to allow for these "placeholder" types for the final data structure?
I have tried searching the web for these sorts of topics but haven't found anything:
partial types
intermediate types
placeholder types
virtual types
temporary types
transitional types
how to have temporary placeholders in data structures
etc.
Create a hashmap.
In a first pass write name/index pairs to the hashmap without modifying the AST itself. For the example that would result in this hashmap (represented in JSON format):
{
"mul": 1,
"sum": 2
}
In a second pass you can use the hashmap to replace references to the keys of this hashmap with a pointer property that gets the corresponding value.
I would suggest not trying to understand how to store intermediate data types, but understanding how to store "references" or "holes". Go look up how a typical serialization/deserialization algorithm works (especially one that can deal with something like repeated substructure or circular references): http://www.dietmar-kuehl.de/mirror/c++-faq/serialization.html
It may give you helpful ideas.

Validate delimiter separated values using JOI

I have a use case where I need to validate a set of values separated by | .
I followed https://github.com/hapijs/joi/issues/570 but it throws a number of errors.
Is there any way I can do this?
Example- AA|BB|CC|DD
Now, I need to validate that all of the values (AA, BB, CC, DD) are strings.
I believe I can't use regex as it would validate just the first value.
Also, there are a number of other validations in my code, so I don't wish to loop the validation process.
Please let me know if I'm unclear. Thanks!
TL;DR:
const Joi = require('joi').extend(joi => ({
base: joi.array(),
coerce: (value, helpers) => ({
value: value.split ? value.split('|') : value,
}),
type: 'versionArray',
}))
The .extend function signature has changed since that comment was written; the name property has been removed and the CoerceFunction should return an object with value as the property.
> Joi.versionArray().validate('AA|BB|CC|DD')
{ value: [ 'AA', 'BB', 'CC', 'DD' ] }
> Joi.versionArray().validate('AA|BB,CC|DD')
{ value: [ 'AA', 'BB,CC', 'DD' ] }
From here on, you can use the .items(...) function to validate each of the strings in the returned array:
> const regex = new RegExp('^[a-zA-Z]+$') // every character must be a-z or A-Z
undefined
> Joi.versionArray().items(Joi.string().regex(regex)).validate('AA|BB|CC|DD')
{ value: [ 'AA', 'BB', 'CC', 'DD' ] }
> Joi.versionArray().items(Joi.string().regex(regex)).validate('AA|BB|CC|00')
{ value: [ 'AA', 'BB', 'CC', '00' ],
error:
{ ValidationError: "[3]" with value "00" fails to match the required pattern: /^[a-zA-Z]+$/ _original: 'AA|BB|CC|00', details: [ [Object] ] } }
> Joi.versionArray().items(Joi.string().regex(regex)).validate('AA|BB,CC|DD')
{ value: [ 'AA', 'BB,CC', 'DD' ],
error:
{ ValidationError: "[1]" with value "BB,CC" fails to match the required pattern: /^[a-zA-Z]+$/ _original: 'AA|BB,CC|DD', details: [ [Object] ] } }

NetSuite invoice's entity doesn't have external_id or type set

I'm using the netsuite ruby gem and having issues with the association between an invoice and a customer, and wondering if I'm missing something obvious.
We have an invoice that's associated with a customer. We set the external_id on the customer (synced via celigo from salesforce), and can see it on the customer when querying it directly:
customer = NetSuite::Records::Customer.get("14829")
customer.internal_id
# "14829"
customer.external_id
# "xxxxxxxxxxxxxx1"
However, when we query the invoice, the external_id and type on the entity are both nil:
NetSuite::Records::Invoice.get("79502").entity
# #<NetSuite::Records::RecordRef:0x00007fb30aa63cf8 #internal_id="14829", #external_id=nil, #type=nil, #attributes={:name=>"customer name"}>
This also prevents us from searching with external_id, for example:
search_params = {
criteria: {
basic: [{
field: 'internalId',
operator: 'anyOf',
value: [ NetSuite::Records::Invoice.new(internal_id: '79502') ]
}],
customerJoin: [{
field: 'internalId',
operator: 'anyOf',
# value: [ NetSuite::Records::Customer.new(internal_id: '14829') ] # this works
value: [ NetSuite::Records::Customer.new(external_id: 'xxxxxxxxxxxxxx1') ] # this returns a `INVALID_KEY_OR_REF` error
}]
}
}
NetSuite::Records::Invoice.search(search_params)
I'm wondering if I miss something to get the associated entity from an invoice. Any insights would be very appreciated! Thank you!
If you are using external id, you need to change this
customerJoin: [{
field: 'internalId',
operator: 'anyOf',
value: [ NetSuite::Records::Customer.new(external_id: 'xxxxxxxxxxxxxx1') ]
}]
to this
customerJoin: [{
field: 'externalId',
operator: 'anyOf',
value: [ NetSuite::Records::Customer.new(external_id: 'xxxxxxxxxxxxxx1') ]
}]
The external id you set is in a different field on the customer record.

Joi object validation: How to validate values with unknown key names?

I have an object with key names I cannot possibly know - they are created by user. However I do know what values they (keys) are going to store, and they (values) are going to be ISO strings. How do I validate those values? And, optionally, how do I validate uknown object's keys, i.e.:
key: Joi.string().min(2).max(25)
What I have already tried was based on Joi API docs :
Another benefits of using Joi.object([schema]) instead of a plain JS object is >that you can set any options on the object like allowing unknown keys, e.g:
const schema = Joi.object({
arg: Joi.string().valid('firstname', 'lastname', 'title', 'company', 'jobtitle'),
value: Joi.string(),
}).pattern(/firstname|lastname/, Joi.string().min(2));
What I understood from the example is that arg key represents Joi.object()'s key, and value represents it's value.
My example:
campaign: Joi.object({
arg: Joi.string().valid( 'unknown' ),
value: Joi.date().iso(),
}).pattern( /unknown/, Joi.string().min(2).max(25) )
My input;
campaign: { g_ad_adwords: "2017-01-19T11:33:26.205Z" }
My error:
"campaign" fails because ["g_ad_adwords" is not allowed]
Try this. It'll basically accept any key within an object campaign and the value must validate against Joi.date().iso()
campaign: Joi.object().pattern(/^/, Joi.date().iso())
This however will match any key. You can restrict this by padding out the regex a little. e.g. only word characters between 2 and 25 chars
campaign: Joi.object().pattern(/\w{2,25}/, Joi.date().iso())
UPDATE
Regarding the example in the Joi docs, I haven't tested it but here's my interpretation. I can understand that it's not the most straightforward example they could have given...
const schema = Joi.object({
arg: Joi.string().valid('firstname', 'lastname', 'title', 'company', 'jobtitle'),
value: Joi.string(),
}).pattern(/firstname|lastname/, Joi.string().min(2));
The objects to validate must contain the two attributes arg and valuewhere arg's value can be one of 'firstname', 'lastname', 'title', 'company', 'jobtitle' and value is just a string.
{
arg: 'firstname',
value: 'john'
}
{
arg: 'lastname',
value: 'smith'
}
{
arg: 'jobtitle',
value: 'brewer'
}
However it will also allow the object to have the attributes firstname and lastname where both of their values is a string with more than two characters. So the above examples could be condensed into a single valid object.
{
firstname: 'john',
lastname: 'smith',
arg: 'jobtitle',
value: 'brewer'
}

Resources