Yup validate one of several possible strings - yup

I have:
const schema = yup.object().shape({
title: yup.string().trim().required(),
taste: yup.string().defined(), // <<< this needs to be different
);
However, the title makes sense because a title can be any string, but for taste, it needs to be either "Sweet", "Salty", or "Spicy".
How do you do this with yup?

Related

Query child model with mongoose

I'm updating the mongoose version from 5 to 6 and found a change in the behavior.
This is a small example:
We have two models
const MessageSchema = new mongoose.Schema({ text: String });
const Message = mongoose.model('Message', MessageSchema);
const Notification = Message.discriminator(
'Notification',
new mongoose.Schema({important: Boolean})
);
And let's say, we create two documents:
const m = new Message({text: 'Some text 1'});
await m.save();
const n = new Notification({important: true, text: 'Some text'});
await n.save();
And we run this query:
await Message.find({important: true})
In mongoose v5 the query will use important in spite of it not being declared in the Message schema. And the result will be just one document.
However, in the new version, it will ignore important if you query Messages, and will return all the documents, because without important query is just {}.
My question is, how can I achieve the same behavior with the new version?
I know that I could use the Notification model instead of Messages, but image if there were several models like Notification (children of Message), that had important in them, and I wanted to search in all of them.

Customize the mock data defined against the types in graphql-tools

I am using graphql-tools to mock for my UI (create-react-app) functional tests. I have a question around the MockList
Currently, I am mocking by type and one of the properties is an array but I still need to be able to customise the elements in the list
e.g. I have a LineItem type which has a mock defined as
const LineItem = () =>
({
name: 'Item name'
} as LineItemType)
and the cart type has a list of LineItem
const Cart = () =>
({
id: 'cart-id',
lineItems: [...new Array(2)],
} as ActiveCartType)
Is there a way for the item name to be different for the 2 items in the cart?
I tried to map over the Array like so
const Cart = () =>
({
id: 'cart-id',
lineItems: [...new Array(2)].map(i => ({...i, id: '123', name: 'new item name'})),
} as ActiveCartType)
But the name doesn't change in the mock result. It is still set to Item name. Only id changes to 123. Am I missing something here?
From the documentation for graphql-tools/mocking
we are using casual, a fake data generator for JavaScript, so that we can get a different result every time the field is called. You might want to use a collection of fake objects, or a generator that always uses a consistent seed, if you are planning to use the data for testing.
From your code, you could assign name to a generator function that adds unique names to that field.
const LineItem = () =>
({
name: functionGoesHere()
} as LineItemType)
Two libraries that produce generated data for mocking are,
Casual library
Faker.js Library

How to explicitly allow additional fields when using stripUnknown in Yup?

I have generic framework code that validates incoming requests using Yup with stripUnknown: true so that excess fields are removed. However, I have one place where I explicitly want to allow any JSON object as input.
How can I explicitly allow one object within a schema to have any fields while otherwise using stripUnknown: true?
Things I've considered but haven't figured out how to implement:
Use yup.object().test(...) or similar to explicitly allow the object
Use yup.addMethod to add a method to yup.object() which would short-circuit the stripping
Use yup.lazy to generate a schema which allows anything (but the type should allow nested JSON, not only top-level fields)
Add a new top-level type yup.anyObject() which would allow any object
Sandbox example
Allowing (and keeping) any value is actually as simple as:
const schema = yup.object().shape({
json: yup.mixed()
});
This allows any value, not just an object. If you want to validate that json is an object containing anything, you can use yup.lazy to map it into a schema having yup.mixed() for every key existing in the object:
const schema = yup.object().shape({
json: yup.lazy((value) =>
yup
.object()
.shape(
Object.keys(value).reduce(
(map, key) => ({ ...map, [key]: yup.mixed() }),
{}
)
)
)
});

How can I transform an object schema's property schemas to derive a new object schema?

Is there a way to introspect and then transform a Joi schema?
I see there is any.describe() which "returns an object that represents the internal configuration of the schema" but then I have to map that internal configuration back to schema builder calls and re-create the schema myself.
e.g. given
const PersonSchema = Joi.object().keys({
firstName: Joi.string().required(),
lastName: Joi.string().required(),
age: Joi.number().required(),
});
I want to define a generic function to take any object schema and make its properties support arrays of the original type equivalent to the following:
const PersonFilterSchema = Joi.object().keys({
firstName: Joi.array().items(Joi.string().required()).optional(),
lastName: Joi.array().items(Joi.string().required()).optional(),
age: Joi.array().items(Joi.number().required()).optional(),
});
i.e. I want to be able to write something like this:
function createFilterSchema(schema) {
return Joi.object().keys(
Object.fromEntries(
schema
.getEntries()
.map(([key, value]) => [key, Joi.array().items(value).optional()])
)
);
}
and then elsewhere use it with whatever raw schemas I have:
const PersonFilterSchema = createFilterSchema(PersonSchema);

Is there a Deconstruct Mongo Response to DTO short cut?

If I have a table in a mongoDB with five properties and I only want to return four of them and none of the mongo added info such as v1 I can map the reposne to a dto like so,
const product = await this.productModel.findById(productId).exec()
return { id: product.id, title: product.title }
Is there a deconstruct shortcut for the return, to extract every field from an interface (Product) from the product response, to save typing each property out ? If for example im retunring 127 properties from a table of entires with 140.
interface Product {
id: string
title: string
...
}
Unfortunately no, typescript interfaces do not really exist when your program compiles
Interface is a structure that defines the contract in your application. It defines the syntax for classes to follow. Classes that are derived from an interface must follow the structure provided by their interface.
The TypeScript compiler does not convert interface to JavaScript. It
uses interface for type checking. This is also known as "duck typing"
or "structural subtyping".
So, you can't really read interface fields and then write some logic (you can maybe achieve this through reflection but it's a bad practice)
An alternative is to explicitly define what fields are to include/or exclude from your object
Suppose that I have an object with this interface:
interface Foo {
field1: string;
field2: string;
field3: string;
.....
field140: string;
}
What you can do here is to define what properties you want to exclude (you take the exclude approach here since you are returning 127 fields of 140)
// This isn't an implementation with mongoose (if you are using it),
// it's just to give you the idea
const FIELDS_TO_EXCLUDE = ["field128", "field129", "field130", ..., "field140"];
productModel.toDTO(){
const documentData = this;
FIELDS_TO_EXCLUDE.forEach(x => delete documentData[x]);
return documentData;
}
In this way, when you will execute the toDTO function your manipulate itself excluding (or including) the fields you want