NestJS sequelize bulk insert - postgresql

I am new to nestJS and I am trying to insert bulk data. But I get the following error and I can't seem to find what I am doing wrong here. Some guidance would be highly appreciated.
stockStyle.entity.ts file:
import {
Column,
ForeignKey,
Model,
Table,
HasMany,
} from 'sequelize-typescript';
import { Stock } from '../stocks/entities/stock.entity';
import { Style } from '../styles/entities/style.entity';
#Table({ tableName: 'stock_style' })
export class StockStyle extends Model<StockStyle> {
#ForeignKey(() => Stock)
#Column
stockId: string;
#ForeignKey(() => Style)
#Column
styleId: number;
}
stockStyle.service.ts file:
import { Injectable } from '#nestjs/common';
import { StockStyle } from './v1/stockStyles/stockStyle.entity';
#Injectable()
export class StockStylesService {
async bulkInsert() {
const stockStyleArray = [
{ stockId: 'Diamond', styleId: 2 },
{ stockId: 'Gold', styleId: 2 },
{ stockId: 'Ruby', styleId: 2 },
];
StockStyle.bulkCreate(stockStyleArray)
}
}

StockStyle.bulkCreate([ stockStyleArray])
I`m not sure, but you should try
StockStyle.bulkCreate(stockStyleArray)

I fixed it using the following way:
async bulkInsert() {
let holder: Array<StockStyle> = [];
const stockStyleArray = [
{ stockId: '1', styleId: 1 },
{ stockId: '2', styleId: 1 },
{ stockId: '3', styleId: 1 },
];
stockStyleArray.forEach((element) => {
let stocks = new StockStyle();
stocks.stockId = element.stockId;
stocks.styleId = element.styleId;
holder.push(stocks.get());
});
StockStyle.bulkCreate(holder);}
I don't know if this is a vaiable solution or not. I just used a workaround here. If anyone has a solution or better alternative to this, then please do free to share the knowledge.

Related

PrismaClientValidationError: Missing required argument in connectOrCreate

Problem:When I try and send/store data in my database I get this error. Specifically, I am trying to create/save a classroom with student names.
Tech Used:
Prisma/Postgres connected to AWS RDS and Next.js, deployed on Vercel, etc.
Error Message
PrismaClientValidationError: Argument data.classrooms.upsert.0.create.students.connectOrCreate.0.create.school.connect of type schoolWhereUniqueInput needs at least one argument.
Argument data.classrooms.upsert.0.update.students.upsert.0.create.school.connect of type schoolWhereUniqueInput needs at least one argument.
at Document.validate (/var/task/node_modules/#prisma/client/runtime/index.js:29501:20)
at serializationFn (/var/task/node_modules/#prisma/client/runtime/index.js:33060:19)
at runInChildSpan (/var/task/node_modules/#prisma/client/runtime/index.js:22550:12)
at PrismaClient._executeRequest (/var/task/node_modules/#prisma/client/runtime/index.js:33067:31)
at async PrismaClient._request (/var/task/node_modules/#prisma/client/runtime/index.js:32994:16)
at async profile (/var/task/.next/server/pages/api/user/profile.js:175:27)
at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils/node.js:366:9)
at async NextNodeServer.runApi (/var/task/node_modules/next/dist/server/next-server.js:481:9)
at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:735:37)
at async Router.execute (/var/task/node_modules/next/dist/server/router.js:247:36) {
clientVersion: '4.9.0'
}
DB Models with relationships: school (1 to many w/students); students (many to many with classrooms); teachers (one to many with students, many to many with classrooms)
Code/Prisma Query
export default async (req, res) => {
...
classroom.students.forEach((student) => {
const totalStudentPoints = student.rewardsRecieved.reduce(
(totalPoints, reward) => {
return totalPoints + reward.pointValue;
},
0
);
groups[student.group.name] += totalStudentPoints;
});
return { ...classroom, groupsTotalPoints: groups };
});
user.classrooms = newClassrooms;
res.json(user);
} else {
console.log("Could Not Find User");
res.status(401).json({
error: "Not authorized",
});
}
}
if (req.method === "PUT") {
const connectStudents = (shouldUpsert) => {
const students = req.body.students;
return students.map((student) => {
const UNSAFEHASH = md5(student.id);
const studentQuery: any = {
where: {
id: student.id,
},
create: {
id: student.id,
firstName: student.firstName,
lastName: student.lastName,
profilePicture: student.profilePicture,
userKey: UNSAFEHASH,
school: {
connect: {
id: req.body.schoolId,
},
},
group: {
connect: {
id: student.group.id,
},
},
},
};
if (shouldUpsert) {
studentQuery.update = {
firstName: student.firstName,
lastName: student.lastName,
profilePicture: student.profilePicture,
userKey: UNSAFEHASH,
group: {
connect: {
id: student.group.id,
},
},
};
}
return studentQuery;
});
};
try {
const user = await prisma.staff.update({
where: {
id: session.id,
},
data: {
firstName: req.body.firstName,
lastName: req.body.lastName,
classrooms: {
upsert: [
{
where: {
id: req.body.classId || "-1",
},
create: {
// id: req.body.classId,
name: req.body.className,
subject: req.body.classSubject,
students: {
connectOrCreate: connectStudents(false),
},
},
update: {
name: req.body.className,
subject: req.body.classSubject,
students: {
upsert: connectStudents(true),
},
},
},
],
},
},
});
Take a look at the PUT request and the prima.staff.update method more specifically. I was looking at the UPSERT I have there, but I can't figure out what's wrong.

Can't access Pinia changed state in Prisma API call in a Nuxt 3 app

Somehow I when I make an API call using Prisma I can access only the default state:
import dbClient from '~/server/utils';
import { useTextStore } from '~/pinia/text'
export default defineEventHandler(async (event) => {
const { id } = event.context.params
const text = useTextStore()
const updatedText = await dbClient.opus.update({
where: {
id: parseInt(id),
},
data: {
content: text.content
},
});
return {
statusCode: 200,
body: text.content
}
})
Here's the Pinia store code:
import { defineStore } from 'pinia'
export const useTextStore = defineStore({
id: 'text',
state: () => {
return {
editor:'Введите текст',
}
},
actions: {
updateText(newContent) {
this.editor = newContent
}
},
getters: {
content: state => state.editor,
},
})
The state changes are shared across components and pages but can't get through to the eventHandler. Is it Nuxt 3 or some other mistake I should look into?

How to accept multiple objects into an array NestJS

I have a feedbackQuestion schema which takes (title: string, subtitle: string, types: enum, values: enum)
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose'
import { Document } from 'mongoose'
import { Types, Value } from 'src/common/enum/types.enum'
export type FeedbackQuestionDocument = FeedbackQuestion & Document
#Schema({ timestamps: true, id: true })
export class FeedbackQuestion {
#Prop()
title: string
#Prop()
subtitle: string
#Prop()
types: Types
#Prop()
value: Value
}
export const FeedbackQuestionSchema =
SchemaFactory.createForClass(FeedbackQuestion)
The feedbackQuestion schema serves as a subdocument in my feedback schema for the key question
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose'
import mongoose, { Document, ObjectId } from 'mongoose'
import { User } from './user.schema'
import { Transform, Type } from 'class-transformer'
import { FeedbackQuestion } from './feedback-question.schema'
import { distributionChannels } from 'src/common/enum/distributionChannels.enum'
export type FeedbackDocument = Feedback & Document
#Schema({ timestamps: true, id: true })
export class Feedback {
#Transform(({ value }) => value.toString())
_id: ObjectId
#Prop()
label: string
#Prop({ default: false })
status: boolean
#Prop()
question: [FeedbackQuestion]
#Prop()
comment: string
#Prop()
thankYouMessage: string
#Prop()
distributionChannels: distributionChannels
#Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
#Type(() => User)
user: User
}
export const FeedbackSchema = SchemaFactory.createForClass(Feedback)
when creating my create-feedbackDto, I assigned question to be an array of type feedbackQuestion
import { Type } from 'class-transformer'
import { FeedbackQuestion } from '../../schemas/feedback-question.schema'
import { IsArray, IsEnum, IsNotEmpty, ValidateNested } from 'class-validator'
import { Types, Value } from '../enum/types.enum'
import { distributionChannels } from '../enum/distributionChannels.enum'
export class CreateFeedbackDto {
#IsNotEmpty()
label: string
status: boolean
#IsArray()
#ValidateNested({ each: true })
#Type(() => FeedbackQuestion)
question: FeedbackQuestion[]
#IsNotEmpty()
comment: string
#IsNotEmpty()
thankYouMessage: string
#IsNotEmpty()
title: string
#IsNotEmpty()
subtitle: string
#IsEnum(Types)
#IsNotEmpty()
types: Types
#IsEnum(Value)
#IsNotEmpty()
value: Value
#IsEnum(distributionChannels)
#IsNotEmpty()
distributionChannels: distributionChannels
}
In my feedback services, I want to work on questions such that I can pass in multiple objects of feedbackQuestion into the question array when creating a feedback. Please How can I do that?
The current code only takes one FeedbackQuestion object in the array
import { Injectable } from '#nestjs/common'
import { InjectModel } from '#nestjs/mongoose'
import { Model } from 'mongoose'
import { Feedback, FeedbackDocument } from '../../schemas/feedback.schema'
import {
FeedbackQuestion,
FeedbackQuestionDocument,
} from '../../schemas/feedback-question.schema'
import { IServiceResponse } from '../../common/interfaces/service.interface'
import { CreateFeedbackDto } from 'src/common/dto/create-feedback.dto'
#Inject#5406able()
export class FeedbackService {
constructor(
#Inject#5406Model(Feedback.name)
private feedbackDocumentModel: Model<FeedbackDocument>,
#Inject#5406Model(FeedbackQuestion.name)
private feedbackQuestionDocumentModel: Model<FeedbackQuestionDocument>,
) {}
async createFeedback(payload: CreateFeedbackDto): Promise<IServiceResponse> {
const feedbackQuestion = await this.feedbackQuestionDocumentModel.create({
title: payload.title,
subtitle: payload.subtitle,
type: payload.types,
value: payload.value,
})
const feedback = await this.feedbackDocumentModel.create({
label: payload.label,
status: payload.status,
question: [feedbackQuestion],
comment: payload.comment,
thankYouMesage: payload.thankYouMessage,
distributionChannels: payload.distributionChannels,
})
// feedback.question.push(feedbackQuestion)
await feedback.save()
return {
data: {
user: feedback,
},
}
}
}
This is the current response I get
"label": "Yearly Feedback",
"status": false,
"question": [
{
"title": "Yearly Feedback",
"subtitle": "Rating the Yearly performance of the organization",
"value": 1,
"_id": "627fa9b915d31bbbc0fe6908",
"createdAt": "2022-05-14T13:08:09.180Z",
"updatedAt": "2022-05-14T13:08:09.180Z",
"__v": 0
}
],
Thanks

How to write Nestjs unit tests for #Injectable() mongodb service

Can someone please guide me. I'm learning Nestjs and doing a small project, and I'm not able to get the unit test working for a controller and service which has dependency on the database.module. How do I go about mocking the database.module in the product.service.ts? Any help will be highly appreciated.
database.module.ts
try {
const client = await MongoClient.connect(process.env.MONGODB, { useNewUrlParser: true, useUnifiedTopology: true });
return client.db('pokemonq')
} catch (e) {
console.log(e);
throw e;
}
};
#Module({
imports: [],
providers: [
{
provide: 'DATABASE_CONNECTION',
useFactory: setupDbConnection
},
],
exports: ['DATABASE_CONNECTION'],
})
export class DatabaseModule {}
product.service.ts
#Injectable()
export class ProductService {
protected readonly appConfigObj: EnvConfig;
constructor(
private readonly appConfigService: AppConfigService,
#Inject('DATABASE_CONNECTION') => **How to mock this injection?**
private db: Db,
) {
this.appConfigObj = this.appConfigService.appConfigObject;
}
async searchBy (){}
async findBy (){}
}
product.service.spec.ts
describe('ProductService', () => {
let service: ProductService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
ConfigService,
DatabaseModule,
AppConfigService,
ProductService,
{
provide: DATABASE_CONNECTION,
useFactory: () => {}
}
],
}).compile();
service = module.get< ProductService >(ProductService);
});
afterAll(() => jest.restoreAllMocks());
}
product.controller.spec.ts
describe('ProductController', () => {
let app: TestingModule;
let ProductController: ProductController;
let ProductService: ProductService;
const response = {
send: (body?: any) => {},
status: (code: number) => response,
json: (body?: any) => response
}
beforeEach(async () => {
app = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [appConfig],
isGlobal: true,
expandVariables: true
}),
ProductModule,
],
providers: [
AppConfigService,
ProductService,
],
controllers: [ProductController]
}).compile();
productController = app.get< ProductController >(ProductController);
productService = app.get< ProductService >(ProductService);
});
afterAll(() => jest.restoreAllMocks());
}
Anything that is not being tested directly in a unit test should theoretically be mocked. In this case, you have two dependencies, AppConfigService adn DATABASE_CONNECTION. You're unit test should provide mock objects that look like the injected dependencies, but have defined and easy to modify behavior. In this case, something like this may be what you're looking for
beforeEach(async () => {
const modRef = await Test.createTestingModule({
providers: [
ProductService,
{
provide: AppConfigService,
useValue: {
appConfigObject: mockConfigObject
}
},
{
provide: 'DATABASE_CONNECTION',
useValue: {
<databaseMethod>: jest.fn()
}
]
}).compile();
// assuming these are defined in the top level describe
prodService = modRef.get(ProductionService);
conn = modRef.get('DATABASE_CONNECTION');
config = modRef.get(AppConfigService);
});
In your controller test, you shouldn't worry about mocking anything other than the ProdctService.
If you need more help there's a large repository of examples here
Edit 9/04/2020
Mocking chained methods is a major pain point when working with things like Mongo. There's a few ways you can go about it, but the easiest is probably to create a mock object like
const mockModel = {
find: jest.fn().mockReturnThis(),
update: jest.fn().mockReturnThis(),
collation: jest.fn().mockReturnThis(),
...etc
}
And on the last call in the chain, make it return the expected outcome so your service can keep running the rest of the code. This would mean if you have a call like
const value = model.find().collation().skip().limit().exec()
you would need to set the exec() method to return the value you expect it to, probably using something like
jest.spyOn(mockModel, 'exec').mockResolvedValueOnce(queryReturn);
I am also exploring using native Mongodb with NestJS. Below is my working test for cron job service updating value in db.
src/cron/cron.service.ts
import { Inject, Injectable } from '#nestjs/common';
import { Cron, CronExpression } from '#nestjs/schedule';
import { Db } from 'mongodb';
import { Order } from 'src/interfaces/order.interface';
#Injectable()
export class CronService {
constructor(
#Inject('DATABASE_CONNECTION')
private db: Db,
) {}
#Cron(CronExpression.EVERY_30_SECONDS)
async confirmOrderEveryMinute() {
console.log('Every 30 seconds');
await this.db
.collection<Order>('orders')
.updateMany(
{
status: 'confirmed',
updatedAt: {
$lte: new Date(new Date().getTime() - 30 * 1000),
},
},
{ $set: { status: 'delivered' } },
)
.then((res) => console.log('Orders delivered...', res.modifiedCount));
}
}
src/cron/cron.service.spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { Db } from 'mongodb';
import { CronService } from './cron.service';
describe('CronService', () => {
let service: CronService;
let connection: Db;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
CronService,
{
provide: 'DATABASE_CONNECTION',
useFactory: () => ({
db: Db,
collection: jest.fn().mockReturnThis(),
updateMany: jest.fn().mockResolvedValue({ modifiedCount: 1 }),
}),
},
],
}).compile();
service = module.get<CronService>(CronService);
connection = module.get('DATABASE_CONNECTION');
});
it('should be defined', async () => {
expect(service).toBeDefined();
});
it('should confirmOrderEveryMinute', async () => {
await service.confirmOrderEveryMinute();
expect(connection.collection('orders').updateMany).toHaveBeenCalled();
});
});

set empty values in MongoDB

I'm trying to set some values for the devices that don't have that parameters but for dataPersist and each of the timestamps is not working. I don't know if it'a a problem of the if structure I'm using, but for dataContainer and dataImageList is working.
public getDeviceById = (deviceId: string): Promise<IDevice> => {
return new Promise((resolve, reject) => {
Device.findOne({ _id: mongoose.Types.ObjectId(deviceId) }).then((doc: any) => {
if (doc) {
if (!doc.dataPersist) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataPersist: { persistSize: "", timestamp: new Date()} }})
}
if (!doc.dataContainer) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataContainer: { containerInfo:[], dockerInfo: [], timestamp: new Date()} }})
}
if(!doc.dataImageList) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataImageList: { imageList:[], timestamp: new Date()} }})
}
resolve(doc);
} else {
reject("fieldNotFoundError,device,getDeviceById");
}
}).catch((err: any) => {
console.error(err);
reject("databaseError,043");
});
});
}
Why is that?
This is my model:
import mongoose from "mongoose";
import { IDevice } from "./iDevice";
interface IDeviceModel extends IDevice, mongoose.Document {
}
const deviceSchema = new mongoose.Schema({
dataPersist: {
persistSize: String,
timestamp: Date
},
dataImageList: {
imageList: Array,
timestamp: Date
},
dataContainer: {
containerInfo: Array,
dockerInfo: Array,
timestamp: Date
}
});
const Device = mongoose.model<IDeviceModel>("device", deviceSchema);
export = Device;
Thank in advance for your help.