How to store number set in nestjs + typeorm - set

I am making a project using nestjs and typeorm.
But I don't know how to apply the set type.
I'm writing a question because there are too many meanings of set and it's hard to find.
#IsNumber()
#Column({ nullable: true })
#Min(0)
#Max(300)
data: number;
Previously, only one value was entered as above.
But now multiple values ​​must be input.
#IsNumber()
#Column({ type:'set', nullable: true })
#Min(1)
#Max(15)
data: number[];
If I write the code as above and sync the typeorm,
an error occurs and it does not run.
Please tell me how to set the set type by setting min and max values ​​for each.

I suggest you to do this following code:
#IsNumber({ each: true })
#Column("simple-json", { nullable: true })
#Min(1, {each: true})
#Max(15, {each: true})
data: number[];

Related

Why are arguments handled by type-graphql always coming through as undefined

I am seeing some really unusual behaviour in a type-graphql project.
I've followed the documentation here https://typegraphql.com/docs/resolvers.html to link some GQL into a JS function, but the object is simply empty when it enters the function. Really not sure why it is doing this.
Given this type class:
#ArgsType()
export class GetFoosArgs implements IGetFoosArgs {
#Field(() => Boolean, { nullable: true, defaultValue: false })
bar?: boolean;
#Field(() => Int, { nullable: true, defaultValue: 0 })
baz?: number;
#Field(() => Int, { nullable: true, defaultValue: 100 })
buzz?: number;
}
And this query function:
#Query(_returns => [Foo], { name: 'foo' })
async getFoos(#Args() {
bar,
baz,
buzz
}: GetFoosArgs): Promise<Foo[]> {
console.log({
foo,
baz,
buzz,
});
}
And this GQL query:
{
foos(
bar:true,
baz:1,
buzz:2,
) {
_key
}
}
The console dump just shows:
{
bar: undefined,
baz: undefined,
buzz: undefined
}
:-S
look good to me, I tested it and it worked just fine, maybe it's something related to your TypeGraphql typescipt setup, it might be helpful to check TypeGraphql ts config docs.
An issue I noticed and might be related is that when I use babel to compile TypeGraphql project, object destructuring in #Args and #Arg will not work properly
so maybe trying this instead might work in your case
#Query((returns) => Boolean, { name: 'foo' })
async getFoos(#Args() args: GetFoosArgs): Promise<boolean> {
console.log(args);
return true;
}
The strange thing is that graphql is strict and the idea that your resolver compiled successfully then you could run the query without errors, yet you get this output is a bit confusing so I'm not sure if SO will be helpful in your case, at least not without more info.
also note that your posted code contains some errors like query using foos when it should be foo so please read how to include MVCE in your question which will be more helpful for others to debug the issue.
P.S. I'm using TypeGraphql v0.17.6
Make sure you've installed the babel-plugin-transform-typescript-metadata package via npm (enter link description here.
You can read more about Babel + TypeScript compiler options here.
In my case, the args never showed up in my emitted schema in the first place (I checked via emitSchemaFile option for buildSchema). Installing the mentioned plugin and adding it to the plugins section of my .babelrc fixed it.
Ok, not completely sure of the nature of the problem, but I've changed things up a bit and the magic is working now.
The key thing to note is that before, I was using the Arg decorator from type-graphql, and now I'm using the Args decorator from #nestjs/graphql.
Old code:
import { InputType, Field, Int, Arg, ID } from 'type-graphql';
#Query(_returns => [Intent], { name: 'intents' })
async getIntents(
#Arg('getIntentsArgs', () => GetIntentsArgs, {
nullable: true,
defaultValue: {},
})
getIntentsArgs: GetIntentsArgs,
): Promise<Intent[]> {
}
#InputType()
export class GetIntentsArgs implements IGetIntentsArgs {
#Field(() => Boolean, { nullable: true, defaultValue: false })
displayTest?: boolean;
#Field(() => Int, { nullable: true, defaultValue: 0 })
skip?: number;
#Field(() => Int, { nullable: true, defaultValue: 100 })
take?: number;
#Field(() => String, { nullable: true, defaultValue: 'intent._key' })
sortColumn?: string;
#Field(() => String, { nullable: true, defaultValue: 'asc' })
sortDirection?: string;
#Field(() => [Filter], { nullable: true, defaultValue: [] })
filters?: IFilter[];
}
New code:
import {
Resolver,
Query,
Parent,
ResolveProperty,
Mutation,
Args,
Context,
} from '#nestjs/graphql';
#Query(_returns => [Intent], { name: 'intents' })
async getIntents(
#Args({ name: 'getIntentsArgs', type: () => GetIntentsArgs })
getIntentsArgs: GetIntentsArgs,
): Promise<Intent[]> {
return this.intentsService.getIntents(getIntentsArgs);
}
#InputType()
export class GetIntentsArgs implements IGetIntentsArgs {
#Field()
#IsBoolean()
#IsOptional()
displayTest?: boolean = false;
#Field()
#IsNumberString()
#IsOptional()
skip?: number = 0;
#Field()
#IsNumberString()
#IsOptional()
take?: number = 100;
#Field()
#IsString()
#IsOptional()
sortColumn?: string = 'intent._key';
#Field()
#IsString()
#IsOptional()
sortDirection?: string = 'asc';
#Field(_ => [Filter])
#Type(_ => Filter)
#IsOptional()
filters?: Filter[] = [];
Filter[] = [];
}

Passing multiple variable to Redux Action creator. What format?

I am pretty new to Redux but I am getting a good hang of it so far.
I am setting up an actions file and I would need to pass two values to payload.
Please let me know if the question has already been asked elsewhere and I missed it.
Thanks!
kramnic
p.s. below is what I have drafted
export const transferValues = (fromId, toId) => {
return {
type: VALUES_TRANSFER,
payload: { fromId, toId }, //should it be array? not sure about syntax here
};
};
you can also have an action of type { type: string, fromId: number, toId: number } instead of a single "payload". that's a matther of preference I think. you just need to adapt you reducers accordingly. I personally like the above attempt more because it represents single attributes like I have them in my state as well. so I can do like a one-to-one mapping. for example:
// state
export type SessionState = {
+id: number,
+username: string,
+sessionId: string,
}
// reducer here
[...]
case SET_USERNAME:
const setUsernameAction = ((action: any): SetUsernameAction)
return {
...state,
username: setUsernameAction.username,
sessionId: setUsernameAction.sessionId,
}
[...]
// example action
export const setUsername = (username: string, sessionId: string): SetUsernameAction => {
return { type: SET_USERNAME, username, sessionId }
}
but you will certainly never use an array for multiple attributes. you'll lose the named access to properties as well as any chance to type your action. stick with objects, no matther what solution you'll go for.

Buefy datepicker with Laravel: Expected Date, got String

I have a form that is populated by data received from an API request to my backend.
I am using v-model to bind the data to the fields (for example):
<input type="text" v-model="fields.name">
Everything works just fine. But when it comes to Buefy datepicker I get the following warning:
Invalid prop: type check failed for prop "value". Expected Date, got String.
This is correct since this is the value I get back from Laravel is "2019-02-01 00:00:00". I am trying to parse this String to a Date using the Buefy property date-parser but with no luck:
<b-datepicker
:date-parser="(date) => new Date(Date.parse(date))"
v-model="fields.budget_date"
:first-day-of-week="1"
placeholder="DD/MM/YYYY"
name="order_date"
editable>
</b-datepicker>
Update:
This is the data object:
data() {
return {
csrf: document.querySelector('meta[name="csrf-token"]').content,
fields: {},
errors: {},
success: false,
loaded: true,
loading: false,
}
Then I use Axios.get to fetch the data from the server and assign them to the fields object like so:
this.fields = response.data;
This is how is see the fields.budget_date in Vue DevTools:
Any idea how to overcome this? Thank you in advance.
I just had this problem in a wrapper component around Buefy's b-datepicker component.
The solution can be derived from christostsang's answer which is to pass in the initial value wrapped in new Date().
My wrapper component prop type looks like this:
initialValue: {
type: Date,
required: false,
default: () => undefined,
},
and it is used like this:
<my-datepicker
:initial-value="new Date(something.renews_on)"
></my-datepicker>
I'm using mostly the default props from b-datepicker, but my wrapper component is using this:
<b-datepicker
v-model="value"
:initial-value="initialValue"
:placeholder="placeholder"
:name="name"
:date-formatter="dateFormatter"
:date-parser="dateParser"
:date-creator="dateCreator"
... etc
...
dateFormatter: {
type: Function,
required: false,
default: date => date.toLocaleDateString(),
},
dateParser: {
type: Function,
required: false,
default: date => new Date(Date.parse(date)),
},
dateCreator: {
type: Function,
required: false,
default: () => new Date(),
},
You can investigate the default props here: https://buefy.org/documentation/datepicker
Finally figured it out.
The warning is pretty clear: Don't use a string here, use a Date object.
So after getting the response from the server, I parsed the string value into Date object and then bind it to v-model:
this.fields.budget_date = new Date(this.fields.budget_date)
So now I get this into Vue DevTools:
As you can see the budget_date is of the correct Date format, unlike created_at which is a string.
Parser function (:date-parser) gives you the correct Date object during user date selection.
What I wanted was to set the v-model value based on the data stored in my database. And for the b-datepicker to work that needs to be Date object, not String.

JSON schema validation with perfect messages

I have divided the data entry in a REST call in 4 parts. Data can be sent to REST call via:-
headers
query params
path params
request body
So in order to validate the presence of any key in any of the above 4 parts I have created a schema in this format. So if in case I have to validate anything in query params I will add the key 'query' and then add the fields inside that, that needs to be validated
const schema = {
id: 'Users_login_post',
type: 'object',
additionalProperties: false,
properties: {
headers: {
type: 'object',
additionalProperties: false,
properties: {
Authorization: {
type: 'string',
minLength: 10,
description: 'Bearer token of the user.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length',
required: 'should have Authorization'
}
}
},
required: ['Authorization']
},
path: {
type: 'object',
additionalProperties: false,
properties: {
orgId: {
type: 'string',
minLength: 23,
maxLength: 36,
description: 'OrgId Id of the Organization.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length', // ---> B
maxLength: 'should not be more than 36 length',
required: 'should have OrgId'
}
}
},
required: ['orgId']
}
}
};
Now, in my express code, I created a request object so that I can test the validity of the JSON in this format.
router.get("/org/:orgId/abc", function(req, res){
var request = { //---> A
path: {
orgId : req.params.orgId
},
headers: {
Authorization : req.headers.Authorization
}
}
const Ajv = require('ajv');
const ajv = new Ajv({
allErrors: true,
});
let result = ajv.validate(schema, request);
console.log(ajv.errorsText());
});
And I validate the above request object (at A) against my schema using AjV.
The output what I get looks something like this:
data/headers should have required property 'Authorization', data/params/orgId should NOT be shorter than 23 characters
Now I have a list of concerns:
why the message is showing data word in the data/headers and data/params/orgId even when my variable name is request(at A)
Also why not my errormessages are used, like in case of orgId I mentioned: should be atleast of 23 length (at B) as a message, even then the message came should NOT be shorter than 23 characters.
How can I show request/headers instead of data/headers.
Also, the way I used to validate my path params, query params, header params, body param, is this the correct way, if it is not, then what can be the better way of doing the same?
Please shed some light.
Thanks in advance.
Use ajv-keywords
import Ajv from 'ajv';
import AjvKeywords from 'ajv-keywords';
// ajv-errors needed for errorMessage
import AjvErrors from 'ajv-errors';
const ajv = new Ajv.default({ allErrors: true });
AjvKeywords(ajv, "regexp");
AjvErrors(ajv);
// modification of regex by requiring Z https://www.regextester.com/97766
const ISO8601UTCRegex = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?Z$/;
const typeISO8601UTC = {
"type": "string",
"regexp": ISO8601UTCRegex.toString(),
"errorMessage": "must be string of format 1970-01-01T00:00:00Z. Got ${0}",
};
const schema = {
type: "object",
properties: {
foo: { type: "number", minimum: 0 },
timestamp: typeISO8601UTC,
},
required: ["foo", "timestamp"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { foo: 1, timestamp: "2020-01-11T20:28:00" }
if (validate(data)) {
console.log(JSON.stringify(data, null, 2));
} else {
console.log(JSON.stringify(validate.errors, null, 2));
}
https://github.com/rofrol/ajv-regexp-errormessage-example
AJV cannot know the name of the variable you passed to the validate function.
However you should be able to work out from the errors array which paths have failed (and why) and construct your messages from there.
See https://ajv.js.org/#validation-errors
To use custom error messages in your schema, you need an AJV plugin: ajv-errors.
See https://github.com/epoberezkin/ajv-errors

Joi validation return only one error message

I have a three field form made of a name field, email field and a textarea. I'm using Joi 4.7.0 version along with hapijs. I use the object below validate the input. I receive the data object from an ajax call. When I fill all the three fields with wrong informations I get only the message relative to the first wrong field. Like that:
"{"statusCode":400,"error":"Bad Request","message":"name is not allowed to be empty","validation": {"source":"payload","keys":["data.name"]}}"
validate: {
payload: {
data: {
name: Joi.string().min(3).max(20).required(),
email: Joi.string().email().required(),
message: Joi.string().min(3).max(1000).required()
}
}
}
For explanation let suppose to not fill the three field. I get only one message error and not the message error of the others fields. Why?
It happens because Joi aborts early by default.
abortEarly - when true, stops validation on the first error, otherwise returns all the errors found. Defaults to true.
*EDIT: Configuration has changed in hapi 8.0. You need to add abortEarly: false to the routes config:
var server = new Hapi.Server();
server.connection({
host: 'localhost',
port: 8000,
routes: {
validate: {
options: {
abortEarly: false
}
}
}
});
*See the Joi API documentation for more details.
*Also, see validation under Hapi Route options.
So Joi stops the validation on the first error:
var Hapi = require('hapi');
var Joi = require('joi');
var server = new Hapi.Server('localhost', 8000);
server.route({
method: 'GET',
path: '/{first}/{second}',
config: {
validate: {
params: {
first: Joi.string().max(5),
second: Joi.string().max(5)
}
}
},
handler: function (request, reply) {
reply('example');
}
});
server.start();
server.inject('/invalid/invalid', function (res) {
console.log(res.result);
});
Outputs:
{ statusCode: 400,
error: 'Bad Request',
message: 'first length must be less than or equal to 5 characters long',
validation: { source: 'params', keys: [ 'first' ] } }
You can however configure Hapi to return all errors. For this, you need to set abortEarly to false. You can do this in server configuration:
var server = new Hapi.Server('localhost', 8000, { validation: { abortEarly: false } });
If you run the script now, you get:
{ statusCode: 400,
error: 'Bad Request',
message: 'first length must be less than or equal to 5 characters long. second length must be less than or equal to 5 characters long',
validation: { source: 'params', keys: [ 'first', 'second' ] } }
I'm not integrating with hapi.js, but I noticed that there is a ValidationOptions object which can be passed along. Inside that object is an abortEarly option, so this should work:
Joi.validate(request, schema, { abortEarly: false }
This can also be configured as follows:
Joi.object().options({ abortEarly: false }).keys({...});
Check out these type definitions for more ValidationOptions:
https://github.com/DefinitelyTyped/tsd/blob/master/typings/joi/joi.d.ts
The validation key no longer works with the Hapi.Server constructor in Hapi 8.0:
[1] validation is not allowed
I found the solution in a GitHub issue for hapi:
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({
host: HOST,
port: PORT,
routes: {
validate: {
options: {
abortEarly: false
}
}
}
});
// Route using Joi goes here.
server.route({});
server.start(function () {
console.log('Listening on %s', server.info.uri);
});
After some research, I found out it can be solved 2 ways:
[Segments.BODY]: Joi.object().keys({
value: Joi.string().required().error(new Error('Value is required and has to be a text!')),
})
or
[Segments.BODY]: Joi.object().keys({
password: Joi.string().required().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).min(8).label('Password').messages({
'string.pattern.base': 'Your {#label} does not matche the suggested pattern',
'string.base': `Your {#label} should match the suggested pattern`,
'string.empty': `Your {#label} can not be empty`,
'string.min': `Your {#label} has to be at least {#limit} chars`,
'any.required': `Your {#label} is required`,
}),
})

Resources