Is it possible to build Yup schema incrementally? - yup

Example, we have a base shape like
const shape = {
password: yup.string().required()
}
Then we build the schema incrementally based on certain data, eg:
switch (data) {
case 'length':
shape["password"] = shape["password"].min(x);
case 'symbols':
shape["password"] = shape["password"].minSymbols(x);
}
Finally,
const schema = yup.object().shape(shape)

Related

Search by first character in a column Typeorm PostgreSQL

I have a company table where I want to search companies by their name of the first letter. In my front end I have a series of letters as checkboxes from A to z and I am passing selected letters as a,b,c in my request body.
If I execute this raw query then it is working, but how I can write this in typeorm querybuilder
select * FROM companies WHERE lower(substring(name from 1 for 1)) = ANY (ARRAY['m'])
I have tried with the query below -
public async getCompanies(
pageOptionsQuery: CompanyPageOptionsDto,
): Promise<Company[]> {
const queryBuilder = this._companyRepository
.createQueryBuilder()
.select('*');
if (!!pageOptionsQuery.filter_by) {
//pageOptionsQuery.filter_by is like series of selected letters
//a,b,c
const filters = pageOptionsQuery.filter_by.split(',');
queryBuilder.where('LOWER(name) IN (:...filters )', {
filters: filters,
});
}
return queryBuilder.getRawMany();
}
This is what I have come up with -
public async getCompanies(
pageOptionsQuery: CompanyPageOptionsDto,
): Promise<Company[]> {
const queryBuilder = this._companyRepository
.createQueryBuilder()
.select('*');
if (!!pageOptionsQuery.filter_by) {
//pageOptionsQuery.filter_by is like series of selected letters
//a,b,c
const filters = pageOptionsQuery.filter_by.split(',');
queryBuilder.where(
'LOWER(SUBSTRING(name, 1, 1)) IN (:...filters)',
{
filters,
},
);
}
return queryBuilder.getRawMany();
}

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);

How to update with returning values in TypeORM?

I wanted to update values and to return specified columns with PostgreSQL.
So far, what I found was update the value then use findOne, but then again it will always use two queries to achieve what I want.
Another is using RAW SQL, UPDATE ... SET ... WHERE ... RETURNING * and this seems a great solution so is there a way to achieve this with TypeORM using UpdateQueryBuilder?
You can use createQueryBuilder:
const firstUser = await connection
.getRepository(User)
.createQueryBuilder("user")
.update<User>(User, {firstName: 'new first name'})
.where("user.id = :id", { id: 1 })
.returning(['id', 'email'])
.updateEntity(true)
.execute();
Notice: there are many ways in type orm to use createQueryBuilder such as: BaseEntity, Repository, EntityManager.
If using repository, this is slightly modified answer and inspired from Noam's answer:
import {
EntityRepository,
Repository,
} from "typeorm";
import { User } from "../entities/user";
#EntityRepository(User)
export class UserRepository extends Repository<User> {
// ... other functions
updateUser = async (payload: User, id: string): Promise<User> => {
const updatedData = await this.createQueryBuilder("user")
.update<User>(User, { ...payload })
.where("user.id = :id", { id: id })
.returning("*") // returns all the column values
.updateEntity(true)
.execute();
return updatedData.raw[0];
};
}

Feather.js + Sequelize + postgres 11 : How to patch a jsonb column?

I would like to update several row of my db with the same object.
let say I have a column customText type jsonb which contains an array of object
here my sequelize model :
customText: {
type: DataTypes.JSONB,
allowNull: true,
field: "custom_text"
}
Now from client I send an object:
const obj = {}
const data = {
textid: "d9fec1d4-0f7a-2c00-9d36-0c5055d64d04",
textLabel: null,
textValue: null
};
obj.customText = data
api.service("activity").patch(null, obj).catch(err => console.log(err));
Like the documentation from feathers.js said if I want to replace multiple record, I send an id equal to null.
So now here come the problem, if I do that my column customText will contain the new object only but I want an array of object, so I want to push the new data in the array. How can I patch the data?
My guess is to use a hook in feathers.js and a raw query with sequelize. But I'm not sure how to do that.
I'm not really sure of my answer but this hook work :
module.exports = function() {
return async context => {
debugger;
const sequelize = context.app.get("sequelizeClient");
const customText = JSON.stringify(context.data.customText[0]);
console.log(customField);
let query =
"UPDATE activity SET custom_text = custom_text || '" +
customText +
"' ::jsonb";
console.log(query);
await sequelize
.query(query)
.then(results => {
console.log(results);
context.results = results;
})
.catch(err => console.log(err));
return context;
I still have a problem because after this hook in feathers, the patch continue so it will update my db again.. so i put a disallow() hook.
Also, with this hook i lost the abilities to listening to event
Also i have a concern with the query, i'm not sure if it's better to use :jsonb_insert over ||

Mongoose Error - Mongoose models with same model name

I am working on a NodeJs application and I am using mongoose node package.
Sample Code
I am using following method to create dynamic collections and these collections sometimes fail to persist the data in database -
const Mongoose = require("mongoose");
const Schema = new Mongoose.Schema({
// schema goes here
});
module.exports = function (suffix) {
if (!suffix || typeof suffix !== "string" || !suffix.trim()) {
throw Error("Invalid suffix provided!");
}
return Mongoose.model("Model", Schema, `collection_${suffix}`);
};
I am using this exported module to create dynamic collections based on unique ids passed as suffix parameter. Something like this (skipping unnecessary code) -
const saveData = require("./data-service");
const createModel = require("./db-schema");
// test 1
it("should save data1", function (done) {
const data1 = [];
const response1 = saveData(request1); // here response1.id is "cjmt8litu0000ktvamfipm9qn"
const dbModel1 = createModel(response1.id);
dbModel1.insertMany(data1)
.then(dbResponse1 => {
// assert for count
done();
});
});
// test 2
it("should save data2", function (done) {
const data2 = [];
const response2 = saveData(request2); // here response2.id is "cjmt8lm380006ktvafhesadmo"
const dbModel2 = createModel(response2.id);
dbModel2.insertMany(data2)
.then(dbResponse2 => {
// assert for count
done();
});
});
Problem
The issue is, test 2 fails! It the insertmany API results in 0 records failing the count assert.
If we swap the the order of the tests, test 1 will fail.
If I run the two tests separately, both will pass.
If there are n tests, only first test will pass and remaining will fail.
Findings
I suspected the mongoose model creation step to be faulty as it is using the same model name viz. Model while creating multiple model instances.
I changed it to following and the tests worked perfectly fine in all scenarios -
return Mongoose.model(`Model_${suffix}`, Schema, `collection_${suffix}`);
Questions
This leaves me with following questions -
Am I following correct coding conventions while creating dynamic collections?
Is suspected code the actual cause of this issue (should the model name also be unique)?
If yes, why is it failing? (I followed mongoose docs but it doesn't provide any information regarding uniqueness of the model name argument.
Thanks.
I you are calling insertMany method on dbModel1, where you variable is declared to dbModel2.
Change your test 2 from:
dbModel1.insertMany(data2)
.then(dbResponse1 => {
// assert for count
done()
});
To:
dbModel2.insertMany(data2)
.then(dbResponse1 => {
// assert for count
done()
});