In Sanity how to only use validation if boolean is set to true? - validation

In a current document group I'm trying to find a way to only allow validation when the boolean is set to true:
{
title: 'Has the movie been released?',
name: 'released',
type: 'boolean',
initialValue: true
}
fooBar.ts:
export default {
title: 'Foo',
name: 'foo',
group: 'general',
type: 'object',
fields: [
{
title: 'Alpha',
name: 'alphaBool',
type: 'boolean',
description: 'Random text',
initialValue: false
},
{
title: 'Alpha Location',
name: 'alphaLoc',
type: 'string',
hidden: ({ parent }) => parent?.alphaBool !== true,
validation: Rule => Rule.required()
}
]
};
but this current implemented approach throws a required error even though it might be set to false. I've tried to see if I could pass down either parent or document from validation so I could attempt to get the value of alphaBool but they both show undefined:
validation: (rule, parent, document) => console.log(rule, parent, document)
but I'm allowed to see parent and document objects in hidden.
Research
Conditional fields
Field Groups
Conditional validation of string
Boolean
Now you see them, now you don’t. Introducing Conditional Fields.
Validation
Optional validation for hidden fields
In Sanity 2.35.0 how can I run validation only if the boolean value is set to true?

If I correctly understand your question, you can use custom validations for this.
Example:
{
title: 'Alpha',
name: 'alphaBool',
type: 'boolean',
description: 'Random text',
initialValue: false
},
{
title: 'Alpha Location',
name: 'alphaLoc',
type: 'string',
hidden: ({ parent }) => parent?.alphaBool !== true,
validation: (Rule) => Rule.custom((value, { document: { alphaBool } }) => {
return alphaBool && !value ? "Field required" : true
})
}
With Rule.custom, you can access the value of the field and the context object of the document. In this example, if alphaBool is set to true and the current field's value is an empty string, then it will throw a Field required validation message. If alphaBool is false, it will not validate anything.

Related

How to disabled a button on lightning datatable based on field value?

I want to disabled a button on a lightning datatable based on a boolean field, I came to this solution:
const columns = [
{ label: 'Client', type:'button',
typeAttributes: {
label: 'Client',
disabled: {fieldName: 'Client__c' }
} }
];
The problem is, I need to make visible when is true, but it actually doing the opposite, i search to a enabled property or trying something like this:
Client__c == true ? false : true;
but it doens't work..
I also try this solution here
this.tableData = result.data.map((value) => ({ ...value, clientDisabled: ('Client__c' == true) ? false : true }));
And the column:
const columns = [
{ label: 'Client', type:'button',
typeAttributes: {
label: 'Client',
disabled: {fieldName: 'clientDisabled' }
} }
];
Also, not work, all buttons became disabled.
Also, I would like to put a - when is disabled (and the field = false), like this:
'Client__c' == true is always false; you're comparing two literal values that will never be equal. You'll want to use the data from the record instead:
this.tableData = result.data.map((record) => ({ ...record, clientDisabled: !record.Client__c }));

How to use custom GraphQLScalarType with default GraphQL types?

I created a GraphQLScalarType to prevent arguments with empty strings. The issue however is if I don't pass the argument at all when calling the mutation, the mutation succeeds and gives the field a value of null.
Usually, I'd wrap the type in a GraphQLNonNull type e.g
GraphQLNonNull(GraphQLString). But that doesn't work with my custom scalar type.
function validateEmptyStrings(value) {
if (typeof value !== 'string') {
throw new TypeError('Value must be a string')
}
if (value === "") {
throw new TypeError('Value cannot be empty')
}
return value
}
const NonEmptyString = new GraphQLScalarType({
name: 'NonEmptyString',
serialize: validateEmptyStrings,
parseValue: validateEmptyStrings,
})
My mutation below
addClient: {
type: ClientType,
args: {
name: {type: NonEmptyString, required: true},
},
resolve(parent, args) {
const client = new Client ({
name: args.name,
})
return client.save()
}
}
Wrapping the arg 'name' type like GraphQLNonNull(NonEmptyString) doesn't work, and neither does the required: true do anything
Apparently, Wrapping the arg 'name' type like GraphQLNonNull(NonEmptyString) does indeed work.
I'm not sure how I skipped that part. Thanks Matthew Herbst for making me take a second look

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

I have confusion on relay and graphql resolve method

Apologies if this is a stupid question. this is the code for relay/graphql pagination that's confusing me:
const GraphQLTodo = new GraphQLObjectType({
name: 'Todo',
fields: {
id: globalIdField('Todo'),
text: {
type: GraphQLString,
resolve: (obj) => obj.text,
},
complete: {
type: GraphQLBoolean,
resolve: (obj) => obj.complete,
},
},
interfaces: [nodeInterface],
});
/* When pagination is needed, make a connection */
const {
connectionType: TodosConnection,
edgeType: GraphQLTodoEdge,
} = connectionDefinitions({
name: 'Todo',
nodeType: GraphQLTodo,
});
const GraphQLUser = new GraphQLObjectType({
name: 'User',
fields: {
id: globalIdField('User'),
todos: {
type: TodosConnection,
args: {
status: {
type: GraphQLString,
defaultValue: 'any',
},
...connectionArgs,
},
resolve: (obj, {status, ...args}) =>
connectionFromArray(getTodos(status), args),
},
totalCount: {
type: GraphQLInt,
resolve: () => getTodos().length,
},
completedCount: {
type: GraphQLInt,
resolve: () => getTodos('completed').length,
},
},
interfaces: [nodeInterface],
});
const Root = new GraphQLObjectType({
name: 'Root',
fields: {
viewer: {
type: GraphQLUser,
resolve: () => getViewer(),
},
node: nodeField,
},
});
You can see that on the GraphQLTodo field, it has text and complete fields with resolve function passed an obj parameter, how is obj passed there? is it from GraphQLUser resolve? I've read on docs that source(in this case obj) - The object resolved from the field on the parent type. is it not from the root query? how is obj here created?
The Connection
Here is where (some of) the magic happens:
const {
connectionType: TodosConnection,
edgeType: GraphQLTodoEdge,
} = connectionDefinitions({
name: 'Todo',
nodeType: GraphQLTodo,
});
You have now told GraphQL that a TodosConnection is going to be made up of GraphQLTodo nodes. Now, let's take a look at where the objects are actually fetched for the connection in your GraphQLUser object, which is on the todos field:
todos: {
type: TodosConnection,
args: {
status: {
type: GraphQLString,
defaultValue: 'any',
},
...connectionArgs,
},
resolve: (obj, {status, ...args}) =>
connectionFromArray(getTodos(status), args),
},
So where does the object come from? The key part here is the getTodos function, which is responsible for actually getting an array of the objects from your data source. Since this field is a TodosConnection and we've already specified in the connection definitions that the nodes are GraphQLTodos, GraphQL knows that the text and complete fields are resolved by getting (in this case) identically named fields on the objects that have been returned. In other words, the returned object is passed to the resolve method on each field.
Querying the Root
You have two fields exposed on Root: viewer and node. Ignoring node for a moment, you have just one way to actually query todos. Since viewer is of type GraphQLUser, and GraphQLUser has that todos field, they can be fetched only as a subfield of viewer, like this:
{
viewer {
todos(first: 10) {
edges {
# each node is a Todo item
node {
text
complete
}
}
}
}
}
Mystery of the Node
But what about that node field? Relay wants to be able to fetch any object using a top-level query, i.e. on your Root field, when given a unique globalId, which is just a base64 encoding of the type name and the id, so Todo:1 is encoded to VG9kbzox. This is set up in the nodeDefinitions (which you haven't included here, but probably have). In those definitions, the globalId is parsed back into the type (Todo) and id (1), and once again you then tell it how to fetch the correct object from your data source. It might look something like:
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type === 'Todo') {
return getTodo(id)
} else if (type === 'User') {
return getUser(id)
}
...
Because you're implementing the nodeInterface in both your GraphQLTodo and GraphQLUser types, Relay will be able query for either of them from the Root's node field.

Sequelizejs - custom message for allowNull

If I have a model User:
var User = sequelize.define('User', {
name: {
type: Sequelize.STRING,
allowNull: false,
validate: {
notEmpty: {
msg: 'not empty'
}
}
},
nickname: {
type: Sequelize.STRING
}
});
How can I specify a message for when name is null or not provided?
This code:
User.create({}).complete(function (err, user) {
console.log(err);
console.log(user);
});
Produces:
{ [SequelizeValidationError: Validation error]
name: 'SequelizeValidationError',
message: 'Validation error',
errors:
[ { message: 'name cannot be null',
type: 'notNull Violation',
path: 'name',
value: null } ] }
The message 'name cannot be null' is generated and doesn't appear to be under my control.
Using User.create({name:''}) shows me my custom message 'not empty':
{ [SequelizeValidationError: Validation error]
name: 'SequelizeValidationError',
message: 'Validation error',
errors:
[ { message: 'not empty',
type: 'Validation error',
path: 'name',
value: 'not empty',
__raw: 'not empty' } ] }
Is there a way to supply the message for allowNull ?
Thanks
Unfortunately custom messages for Null validation errors are not currently implemented. According to the source code, notNull validation is deprecated in favor of a schema-based validation and the code within the schema validation does not allow for a custom message. There is a feature request for this at https://github.com/sequelize/sequelize/issues/1500. As a workaround you can catch the Sequelize.ValidationError and insert some custom code that includes your message.
e.g.
User.create({}).then(function () { /* ... */ }).catch(Sequelize.ValidationError, function (e) {
var i;
for (i = 0; i < e.errors.length; i++) {
if (e.errors[i].type === 'notNull Violation') {
// Depending on your structure replace with a reference
// to the msg within your Model definition
e.errors[i].message = 'not empty';
}
}
})

Resources