class-validator: validating sub-types: reject invalid discriminator values, avoid looking inside arrays - class-validator

How can I get class-validator to be stricter when validating sub-types - specifically, reject invalid discriminator values, and not automatically look inside arrays?
Consider the following code:
import 'reflect-metadata';
import { Equals, ValidateNested, validateOrReject } from 'class-validator';
import { plainToClass, Type } from 'class-transformer';
class Base {
type: string;
value: string;
}
class Derived1 extends Base {
#Equals('value1')
value: string;
}
class Derived2 extends Base {
#Equals('value2')
value: string;
}
class Data {
#ValidateNested()
#Type(() => Base, {
keepDiscriminatorProperty: true,
discriminator: {
property: 'type',
subTypes: [
{ value: Derived1, name: 'derived1' },
{ value: Derived2, name: 'derived2' },
],
},
})
stuff: Base;
}
const validate = async (data: unknown) => {
const instance = plainToClass(Data, data);
await validateOrReject(instance);
};
(async () => {
validate({ stuff: { type: 'derived1', value: 'value1' } }); // (1) passes as expected
validate({ stuff: { type: 'derived2', value: 'value2' } }); // (2) passes as expected
validate({ stuff: { type: 'derived3', value: 'value3' } }); // (3) how can I get this to throw?
validate({ stuff: [{ type: 'derived1', value: 'value1' }] }); // (4) how can I get this to throw?
validate({ stuff: [{ type: 'derived1', value: 'value2' }] }); // (5) this throws, expecting 'value' to equal 'value1'
})();
In (3), it seems that class-validator is perfectly happy with the invalid type field that was passed. Perhaps it is actually the class-transformer that should be expected to throw here?
In (4) I am actually surprised that this passes without any issue. Can I force class-validator / class-transformer to not automatically validate each array element as if it were a Base?

Related

How to validate child DTO conditionally with class-validator?

I am trying to conditionally validate nested DTO with class validator, but #ValidateIf seems to be not applying correctly and always validating the nested class.
I have the following DTO:
export class SuperAdminStoreDto extends StoreDto {
#IsOptional()
#IsBoolean()
readonly payments: boolean;
#ValidateIf(object => object.payments)
#ValidateNested({ each: true })
#Type(() => PaymentDetails)
readonly payment_details: PaymentDetails[];
}
class PaymentDetails {
#IsNumberString()
#IsNotEmpty()
readonly min: string;
#IsNumberString()
#IsNotEmpty()
readonly max: string;
#IsNumberString()
#IsNotEmpty()
readonly percentage: string;
}
Let's say the object being validated is:
{
"payments": false,
"payment_details": {
"min": "1"
}
}
I get no errors although I should because I have set the validator with following flags:
{
whitelist: true,
forbidNonWhitelisted: true,
forbidUnknownValues: true,
validationError: {
target: false,
}
}
I am expecting the validator to not allow the child object to be present and throw an error.
One option I've used is to create a custom decorator to validate the nested object since custom decorators work with #ValidateIf()
type Class<T> = { new (...args: any[]): T };
export function ValidateDtoArray(dto: Class<any>, validationOptions?: ValidationOptions) {
return function (object: unknown, propertyName: string) {
registerDecorator({
name: 'validateDto',
target: (object as any).constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
async validate(value: unknown, args: ValidationArguments) {
if (!isArray(value)) return false;
for (let i = 0; i < (value as any[]).length; i++) {
const array_value = (value as any[])[i];
if(typeof array_value !== 'object') return false;
const validation_errors = await validate(plainToClass(dto, array_value as Record<string, unknown>);
if (validation_errors.length > 0) return false;
}
return true;
},
defaultMessage(validationArguments?: ValidationArguments) {
return `Can only be of type ${dto.name}[]`;
},
},
});
};
}
Your code then becomes
export class SuperAdminStoreDto extends StoreDto {
#IsOptional()
#IsBoolean()
readonly payments: boolean;
#ValidateIf(object => object.payments)
#ValidateDtoArray(PaymentDetails)
#Type(() => PaymentDetails)
readonly payment_details: PaymentDetails[];
}

Transient transition that sets a value only on condition passing

Take the following code:
const isWarning = () => { ... }
const setWarning = () => { ... }
const machine = Machine({
initial: "foo",
context: {
warning: null
},
states: {
foo: {
on: {
"": [
target: "bar",
action: "setWarning",
cond: "isWarning",
]
}
},
bar: {
on: {
FOO: "foo,
}
}
}
}, {
actions: {
setWarning
}
guards: {
isWarning
}
});
Is this the best way to go to "bar" and set a warning based on some quantitative data in "foo"?
Given the posted code example, I am not sure what you mean by "quantitative data in foo". Data relevant for machine's behavior can be stored in machine's context or state's meta property.
For getting into bar state and set a warning you might need something like:
const sm = Machine({
initial: 'baz',
context: { wasWarned: false },
on: {
'WARNING': {
target: 'bar',
action: 'setWarning'
}
},
states: {
baz: {},
bar: {}
}
}, {
actions: {
setWarning: assign({ warning: true })
}
})
This means: When machine gets 'WARNING' event, go into bar state AND immediately, before anything else update the context.
Actions are not immediately triggered. Instead, the State object returned from machine.transition(...) will declaratively provide an array of .actions that an interpreter can then execute.
The transition will be enabled after the guards are passed.
Other code example that might prove useful depending on what you want to achieve:
const sm = Machine({
initial: 'pending',
context: { wasWarned: null },
states: {
pending: {
on: {
'': [
{target: 'bar', cond:'wasWarned'},
{target: 'baz', cond: 'otherCond'}
]
}
},
bar: {},
baz: {}
},
guards: {
wasWarned: (ctx) => ctx.wasWarned
}
})

Vue.js Change placeholder in template dynamically

I want change the placeholder in a template dynamically over the input of a textbox but it not work after change the value. Initial it work perfect.
Demo
https://jsfiddle.net/he4gx40g/
Update: Working example thanks #Roy J
https://jsfiddle.net/z3gbk0L2/
Example of the component (without the textbox logic)
<customValueComponent :item="config" :value="'ConfigValue1'" />
Code of the customValue component
customValueComponent: {
props: {
item: {
type: Object,
required: true
},
value: {
type: String,
required: true
}
},
watch: {
value: function (newVal, oldVal) { // watch it
console.log('Prop changed: ', newVal, ' | was: ', oldVal)
this.$options.template = '<div>{{ item.' + this.value + '}}</div>';
}
},
created: function () {
this.$options.template = '<div>{{ item.' + this.value + '}}</div>';
},
template: ''
}
Object
var config =
{
ConfigValue1: "Titanium",
ConfigValue2: "Gold",
ConfigValue3: "Silver",
ConfigValue4: "Bronze",
ConfigValue5: "Copper",
...
};
$options is read-only. This is not how you change values in a template. Vue updates values as they change. Your component definition should be
Vue.component('customvalue-component', {
props: {
item: {
type: Object,
required: true
},
value: {
type: String,
required: true,
}
},
template: '<div>{{value}}</div>'
});
And your binding on the component should be
<customvalue-component :item="config" :value="config[value1]" />

How to conditionally pass in a picture AND a child component's string

a Vue newbie here. I am constructing a navbar-brand element as part of my navbar.
<template>
<!--Navbar-->
<navbar position="top" className="red">
<!-- Navbar brand -->
<navbar-brand></navbar-brand>
...
I would like it to display its child - a string passed in between the tags, if present AND a picture, if the prop.src is not empty. How do I do that - how do I condition the render function? The code is here:
import classNames from 'classnames';
export const props = {
tag: {
type: String,
default: "a"
},
src: {
type: String,
required: true
},
alt: {
type: String,
default: 'brand logo'
},
href: {
type: String,
default: '#'
},
className: {
type: String
}
};
export default {
functional: true,
props,
render(h, { props, data, children }) {
const dataObj = {
class: classNames(
'navbar-brand',
props.className ? props.className : ''
),
attrs: {
href: 'props.href'
}
};
const img = [
h('img', {
class: [],
attrs: {
src: props.src,
alt: props.alt
}
})
];
const
return h(props.tag, dataObj, img);
}
};
PLS HALP
Yours, Paco

Loopback remote method not returning response body

So I created this remote method in loopback:
Message.findUserMessages = function(id,cb) {
Message.find({
where: {
from_user_id: id
},
include: {
"relation":"message_text"
}
});
};
Message.remoteMethod('findUserMessages', {
accepts: {
arg: 'id',
type: 'number'
},
returns: {
arg: 'response',
type: 'Object'
},
http: {
path: '/user/',
verb: 'get'
}
});
But when I view the response, it does not show the output in the response body. The only reason I know the correct results are being accessed is due to the fact that my DB is returning the result of the query. How do I get put the output of the query in the response body?
The correct code should be:
Message.findUserMessages = function(id, cb) {
Message.find({
where: {
from_user_id: id
},
include: {
"relation":"message_text"
}
}, function(err, response) {
if (err) throw err;
cb(null, response);
});
};
Message.remoteMethod('findUserMessages', {
accepts: {
arg: 'id',
type: 'number',
required: true,
http: { source: 'path' }
},
returns: {
arg: 'response',
type: 'Object',
root: true
},
http: {
path: '/user/:id/findUserMessages',
verb: 'get'
}
});
You forget to callback the response.
Note: I've also changed the http url path hoping you wanted it like so. And also source to the argument is set to path. You might also want to look at usage of root.