How to convert string Boolean to actual Boolean using kendo-UI data source? - kendo-ui

I am trying to convert string boolean to actual boolean, It's not converting properly.
Here is try:
var viewModel = kendo.observable({
Aggregate:true,
qreport:{
aggregateSource: [
{ Name: "viewModel.i18n.QReportCountType", Value: true },
{ Name: "viewModel.i18n.QReportMinMaxType", Value: false }
],
schema: {
model: {
fields: {
Name: { type: "string" },
Value: { type: "boolean" }
}
}
}
}
});
kendo.bind($("#demo"), viewModel);
Hwere is link:
https://dojo.telerik.com/eNakOgIj/13
and in console, it always a convert to string 'true' or 'falsre';
How can I get the true or false value here?

By setting up the value databinding, under the hood it is calling the ObservableObject.Set method (documentation). Unfortunately the only acceptable argument types for the value are Number, String, Date, and Object. Since you are trying to set the value using a type that is not supported, it is implicitly being converted to a String.
This means that you will need to convert the value back to a Boolean when you go to reference it. E.g.
var aggregate = ('' + viewModel.get('Aggregate')).toLowerCase() === 'true';
console.log(aggregate);
Example: https://dojo.telerik.com/AqAtekaF

Related

Validate each Map<string, number> value using class-validator

I'm trying to perform simple validation on a JSON input, modelled by one of my DTOs.
One of the object properties is of type Map<string, number>. an example input:
{
"type": "CUSTOM",
"is_active": true,
"current_plan_day": 1,
"custom_warmup_plan": {
"1": 123,
"2": 456
}
On my controller I'm using a DTO to specify the body type. the class, together with class-validator decorators is this:
export class CreateWarmupPlanRequestDto {
#IsEnum(WarmupPlanType)
type: string;
#IsOptional()
#IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 })
#IsPositive()
hard_cap: number | null;
#IsBoolean()
is_active: boolean;
#IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 })
#IsPositive()
current_plan_day: number;
#IsOptional()
#IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 })
#IsPositive()
previous_plan_day: number | null;
#IsOptional()
#IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 }, { each: true })
#IsPositive({ each: true })
custom_warmup_plan: Map<string, number>; // PROBLEM HERE
}
I'm looking to validate each value of custom_warmup_plan to be an existing positive integer.
Validation of the other properties of the object works just fine and as expected, but for my example input I keep getting errors (2 error messages, joined):
{
"message": "each value in custom_warmup_plan must be a positive number. |#| each value in custom_warmup_plan must be a number conforming to the specified constraints",
"statusCode": 400,
"timestamp": "2021-07-29T13:18:29.331Z",
"path": "/api/warmup-plan/bc4c3f0e-8e77-46de-a46a-a908edbdded5"
}
Documentation for this seems to be pretty straight forward, but I just cant get it to work.
I've also played around with a simple Map<string, string> and the #IsString(each: true) validator, but that does not seem to work either.
any ideas?
versions:
"#nestjs/common": "^8.0.0",
"#nestjs/core": "^8.0.0",
"#nestjs/mapped-types": "^1.0.0",
"#nestjs/platform-express": "^8.0.0",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
It is necessary to convert plain object to map. Use Transform decorator from class-transformer
#IsOptional()
#IsNumber(undefined, { each: true })
#Transform(({ value }) => new Map(Object.entries(value)))
prop?: Map<string, number>;
From the docs
If your field is an array and you want to perform validation of each item in the array you must specify a special each: true decorator option
If you want to be able to validate maps you could write a custom decorator and pass in a list of class-validator functions to validate the keys and values. For example the below decorator takes as input a list of validation functions for both the keys and values (e.g. passing in isString, isObject, etc..., class-validator has a corresponding function you can call for all the validation decorators they provide)
export function IsMap(
key_validators: ((value: unknown) => boolean)[],
value_validators: ((value: unknown) => boolean)[],
validationOptions?: ValidationOptions
) {
return function (object: unknown, propertyName: string) {
registerDecorator({
name: 'isMap',
target: (object as any).constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: unknown, args: ValidationArguments) {
if (!isObject(value)) return false;
const keys = Object.keys(value);
const is_invalid = keys.some((key) => {
const is_key_invalid = key_validators.some((validator) => !validator(key));
if (is_key_invalid) return false;
const is_value_invalid = value_validators.some((validator) => !validator(value[key]));
return is_value_invalid;
});
return !is_invalid;
},
},
});
};
}
And you can use this decorator in your example like this
import { isInt } from 'class-validator'
export class CreateWarmupPlanRequestDto {
#IsOptional()
#IsMap([], [isInt])
custom_warmup_plan: Map<string, number>;
}
Using the same approach with #Daniel, I modified the code little bit so that the focus is on 'isValid' rather than 'IsInvalid'. So that we could avoid double negation.
Additionally, the coming object is transformed to map in the DTO.
#Transform(({ value }) => new Map(Object.entries(value)))
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
} from 'class-validator';
import * as $$ from 'lodash';
export function IsMap(
keyValidators: ((value: unknown) => boolean)[],
valueValidators: ((value: unknown) => boolean)[],
validationOptions?: ValidationOptions,
) {
return function (object: unknown, propertyName: string) {
/**
* ** value is expected to be a MAP already, we are just checking types of keys and values...
*/
registerDecorator({
name: 'isMap',
target: (object as any).constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: Map<any, any>, args: ValidationArguments) {
if (!$$.isMap(value)) {
return false;
}
const keys = Array.from(value.keys());
return $$.every(keys, (key) => {
// checking if keys are valid...
const isKeyInvalid = keyValidators.some(
(validator) => !validator(key),
);
if (isKeyInvalid) {
return false;
}
// checking if values are valid...
const isValueInvalid = valueValidators.some(
(validator) => !validator(value.get(key)),
);
if (isValueInvalid) {
return false;
} else {
return true;
}
});
},
},
});
};
}

How to do a nested mutation resolver with nexus-prisma

I have the following datamodel:
type Job {
// ...
example: String
selections: [Selection!]
// ...
}
type Selection {
...
question: String
...
}
I define my object type so:
export const Job = prismaObjectType({
name: 'Job',
definition(t) {
t.prismaFields([
// ...
'example',
{
name: 'selections',
},
// ...
])
},
})
I do my resolver this way:
t.field('createJob', {
type: 'Job',
args: {
// ...
example: stringArg(),
selections: stringArg(),
// ...
},
resolve: (parent, {
example,
selections
}, ctx) => {
// The resolver where I do a ctx.prisma.createJob and connect/create with example
},
})
So now in the resolver I can receive the selections as json string and then parse it and connect/create with the job.
The mutation would look like this:
mutation {
createJob(
example: "bla"
selections: "ESCAPED JSON HERE"
){
id
}
}
I was wondering if there's anything more elegant where I could do something like:
mutation {
createJob(
example: "bla"
selections: {
question: "bla"
}
){
id
}
}
or
mutation {
createJob(
example: "bla"
selections(data: {
// ...
})
){
id
}
}
I've noticed that with nexus-prisma you can do stringArg({list: true}) but you can't really do objects.
My main question is what is the most elegant way to do either nested mutation or connect all in one.
You can use an inputObjectType as shown in the docs:
export const SomeFieldInput = inputObjectType({
name: "SomeFieldInput",
definition(t) {
t.string("name", { required: true });
t.int("priority");
},
});
Make sure to include the type as part of the types you pass to makeSchema. You can then use it to define an argument, like
args: {
input: arg({
type: "SomeFieldInput", // name should match the name you provided
}),
}
Now, the argument value will be available to your resolver as a regular JavaScript object, not a String. If you need a list of input objects, or want to make the argument required, you do so using the same options you would provide with when using a scalar -- list, nullable, description, etc.
Here's a complete example:
const Query = queryType({
definition(t) {
t.field('someField', {
type: 'String',
nullable: true,
args: {
input: arg({
type: "SomeFieldInput", // name should match the name you provided
}),
},
resolve: (parent, { input }) => {
return `You entered: ${input && input.name}`
},
})
},
})
const SomeFieldInput = inputObjectType({
name: "SomeFieldInput",
definition(t) {
t.string("name", { required: true });
},
});
const schema = makeSchema({
types: {Query, SomeFieldInput},
outputs: {
...
},
});
Then query it like:
query {
someField(
input: {
name: "Foo"
}
)
}
Or using variables:
query($input: SomeFieldInput) {
someField(input: $input)
}

GraphQL Relay defaultValue for array of object in fragment argument definitions

I am trying to define my createRefetchContainer with fragment. However, I am getting an error about defaultValue of array of object type. I checked the documentation yet I couldn't figure out how to handle default value attribute for array of object type parameter.
I tried to put empty array "[]" or "[{ key: "status", value: "active" }]" but none of them worked.
export default createRefetchContainer(
translate('admin')(UserList),
graphql`
fragment UserList_usersWithPage on Query
#argumentDefinitions(
filters: { type: [UserFilterInput], defaultValue: [] }
pageNumber: { type: Int, defaultValue: 0 }
pageSize: { type: Int, defaultValue: 25 }
) {
page: findUsersWithPage(filters: $filters, pageNumber: $pageNumber, pageSize: $pageSize) {
totalPages
totalElements
users {
...MiniProfile_user #relay(mask: false)
}
}
}
fragment UserList_filterFields on Query {
accounts: findDistinctUserAccounts
departments: findDistinctUserDepartments
expertises: findDistinctUserExpertises
ranks: findAllUserRankCodes
}
`,
graphql`
query UserListRefetchQuery($userFilterInput: [UserFilterInput], $pageNumber: Int, $pageSize: Int) {
...UserList_usersWithPage
#arguments(userFilterInput: $userFilterInput, pageNumber: $pageNumber, pageSize: $pageSize)
}
`,
);
ERROR:
GraphQLParser: Expected definition for variable `filters` to be an object
with the following shape: `{type: string, defaultValue?: mixed, nonNull?:
boolean, list?: boolean}`, got `[object Object]`. Source: document
`UserList_usersWithPage` file: `scenes/admin/pages/UserList.js`.
I found a solution that appears to work by wrapping the full argument type in a string and flagging my array member type as required within the array using "!":
...
#argumentDefinitions(
filters: { type: "[Filter!]", defaultValue: [] }
)
...
So in OP's example, that'd mean:
export default createRefetchContainer(
translate('admin')(UserList),
graphql`
fragment UserList_usersWithPage on Query
#argumentDefinitions(
filters: { type: "[UserFilterInput!]", defaultValue: [] }
pageNumber: { type: Int, defaultValue: 0 }
pageSize: { type: Int, defaultValue: 25 }
) {
...
Note that an empty array is still a valid argument type, as [Type!] allows for empty arrays, but requires that any members be non-null of type Type.
I'm still pretty new to graphql and relay myself, so cannot go into more detail on why this is working at this time.

Date and Json in type definition for graphql

Is it possible to have a define a field as Date or JSON in my graphql schema ?
type Individual {
id: Int
name: String
birthDate: Date
token: JSON
}
actually the server is returning me an error saying :
Type "Date" not found in document.
at ASTDefinitionBuilder._resolveType (****node_modules\graphql\utilities\buildASTSchema.js:134:11)
And same error for JSON...
Any idea ?
Have a look at custom scalars: https://www.apollographql.com/docs/graphql-tools/scalars.html
create a new scalar in your schema:
scalar Date
type MyType {
created: Date
}
and create a new resolver:
import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';
const resolverMap = {
Date: new GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
parseValue(value) {
return new Date(value); // value from the client
},
serialize(value) {
return value.getTime(); // value sent to the client
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return parseInt(ast.value, 10); // ast value is always in string format
}
return null;
},
})
};
Primitive scalar types in GraphQL are Int, Float, String, Boolean and ID. For JSON and Date you need to define your own custom scalar types, the documentation is pretty clear on how to do this.
In your schema you have to add:
scalar Date
type MyType {
created: Date
}
Then, in your code you have to add the type implementation:
import { GraphQLScalarType } from 'graphql';
const dateScalar = new GraphQLScalarType({
name: 'Date',
parseValue(value) {
return new Date(value);
},
serialize(value) {
return value.toISOString();
},
})
Finally, you have to include this custom scalar type in your resolvers:
const server = new ApolloServer({
typeDefs,
resolvers: {
Date: dateScalar,
// Remaining resolvers..
},
});
This Date implementation will parse any string accepted by the Date constructor, and will return the date as a string in ISO format.
For JSON you might use graphql-type-json and import it as shown here.

Graphql Typecheck on redux like events

I'm implementing a graphql server over some existing REST api using apollo-server.
There is and endpoint returning a list of redux-like events, where Hi have a type and a payload.
type is a string and payload is an object. e.g.
[
{
type:"joined"
from:"member1"
payload:{
received_events:"1518377870416"
invited_by:"member2"
}
},
{
type:"text"
from:"member1"
payload:{
test_string:"hello"
}
}
]
What I need to check is the following:
1) type is an enum joined|text
2) from is a String
3) if type == joined then the payload should contain received_events and invited_by, if type == text then payload should contain test_string
What is the best way to do it? I'm looking at the scalar , and Union, but I'm not sure what to do.
One way to solve this is to inject the type from your event type into the payload object. It can then be used inside the __resolveType resolver for your union. A simple example:
const typeDefs = `
type Query {
events: [Event]
}
type Event {
type: String
payload: Payload
}
union Payload = Text | Joined
type Text {
test_string: String
}
type Joined {
received_events: String
invited_by: String
}
`;
// Since your type names and the specified types are different, we
// need to map them (resolveType needs to return the exact name)
const typesMap = {
text: 'Text',
joined: 'Joined'
}
const resolvers = {
Query: {
events: (root, args, context) => {
return [
{ type: 'text', payload: { test_string: 'Foo' } }
];
},
},
// We can use the resolver for the payload field to inject the type
// from the parent object into the payload object
Event: {
payload: (obj) => Object.assign({ type: obj.type }, obj.payload)
},
// The type can then be referenced inside resolveType
Payload: {
__resolveType: (obj) => typesMap[obj.type]
}
};

Resources