Skip to content

Commit

Permalink
Merge pull request #253 from samagra-comms/develop
Browse files Browse the repository at this point in the history
Develop Merge v2.4.5
  • Loading branch information
chinmoy12c authored Aug 13, 2024
2 parents 3cbb0eb + 14a067d commit f06da7e
Show file tree
Hide file tree
Showing 18 changed files with 469 additions and 75 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/CI-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: 16.18
node-version: 16

- name: Install Dependencies
run: yarn install --frozen-lockfile
run: yarn config set ignore-engines true && yarn install

- name: Generate Prisma Client and Test
run: npx prisma generate && yarn test 2>&1 | tee test-report.txt
Expand All @@ -33,4 +33,4 @@ jobs:
exit 1
else
echo "Tests have passed."
fi
fi
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
FROM node:16 AS install
WORKDIR /app
COPY package.json ./
RUN yarn config set ignore-engines true
RUN yarn install

FROM node:16 as build
Expand All @@ -20,4 +21,4 @@ COPY --from=build /app/package*.json ./
COPY --from=build /app/prisma ./prisma
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3002
CMD [ "npm", "run", "start:migrate:prod" ]
CMD [ "npm", "run", "start:migrate:prod" ]
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@nestjs/passport": "^8.2.1",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/platform-fastify": "^8.2.6",
"@nestjs/schedule": "^4.1.0",
"@nestjs/swagger": "^5.2.0",
"@nestjs/terminus": "^9.2.2",
"@prisma/client": "3",
Expand All @@ -50,6 +51,7 @@
"cache-manager-redis-store": "^2.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cron": "^3.1.7",
"expect-type": "^0.13.0",
"fastify-compress": "3.7.0",
"fastify-helmet": "^7.1.0",
Expand Down
14 changes: 14 additions & 0 deletions prisma/migrations/20240718081636_add_schedule_table/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- CreateTable
CREATE TABLE "Schedules" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"scheduledAt" TIMESTAMP(3) NOT NULL,
"authToken" TEXT NOT NULL,
"botId" TEXT NOT NULL,
"config" JSONB NOT NULL,

CONSTRAINT "Schedules_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Schedules_botId_key" ON "Schedules"("botId");
2 changes: 2 additions & 0 deletions prisma/migrations/20240718084125_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- DropIndex
DROP INDEX "Schedules_botId_key";
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Warnings:
- Changed the type of `botId` on the `Schedules` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
*/
-- AlterTable
ALTER TABLE "Schedules" DROP COLUMN "botId",
ADD COLUMN "botId" UUID NOT NULL;

-- AddForeignKey
ALTER TABLE "Schedules" ADD CONSTRAINT "Schedules_botId_fkey" FOREIGN KEY ("botId") REFERENCES "Bot"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Bot" ALTER COLUMN "startDate" SET DATA TYPE TIMESTAMP(3),
ALTER COLUMN "endDate" SET DATA TYPE TIMESTAMP(3);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Schedules" ADD COLUMN "name" TEXT NOT NULL DEFAULT E'';
16 changes: 14 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,13 @@ model Bot {
ownerOrgID String?
purpose String?
description String?
startDate DateTime? @db.Date
endDate DateTime? @db.Date
startDate DateTime?
endDate DateTime?
status BotStatus @default(DRAFT)
tags String[]
botImage String?
meta Json?
schedules Schedules[]
}

model UserSegment {
Expand Down Expand Up @@ -132,3 +133,14 @@ model ConversationLogic {
bots Bot[]
transformers TransformerConfig[]
}

model Schedules {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid()
name String @default("")
createdAt DateTime @default(now())
scheduledAt DateTime
authToken String
bot Bot @relation(fields: [botId], references: [id])
config Json
botId String @db.Uuid
}
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { HealthModule } from './health/health.module';
import { FusionAuthClientProvider } from './modules/user-segment/fusionauth/fusionauthClientProvider';
import { VaultClientProvider } from './modules/secrets/secrets.service.provider';
import { MonitoringModule } from './monitoring/monitoring.module';
import { ScheduleModule } from '@nestjs/schedule';

import * as redisStore from 'cache-manager-redis-store';

Expand Down Expand Up @@ -58,6 +59,7 @@ import * as redisStore from 'cache-manager-redis-store';
max: 1000
}),
MonitoringModule,
ScheduleModule.forRoot(),
],
controllers: [AppController, ServiceController],
providers: [
Expand Down
40 changes: 34 additions & 6 deletions src/modules/bot/bot.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { BotStatus, Prisma } from '../../../prisma/generated/prisma-client-js';
import { FusionAuthClientProvider } from '../user-segment/fusionauth/fusionauthClientProvider';
import { BadRequestException, CacheModule, ServiceUnavailableException } from '@nestjs/common';
import { VaultClientProvider } from '../secrets/secrets.service.provider';
import { SchedulerRegistry } from '@nestjs/schedule';

class MockPrismaService {
bot = {
Expand Down Expand Up @@ -94,6 +95,7 @@ const mockBotService = {
getBroadcastReport: jest.fn(),

start: jest.fn(),
scheduleNotification: jest.fn(),
}

const mockBotData: Prisma.BotGetPayload<{
Expand Down Expand Up @@ -254,29 +256,34 @@ describe('BotController', () => {
BotService, {
provide: BotService,
useValue: mockBotService,
}
},
SchedulerRegistry,
],
}).compile();

botController = module.get<BotController>(BotController);
configService = module.get<ConfigService>(ConfigService);
});

afterEach(() => {
jest.clearAllMocks();
});

it('bot start returns bad request on non existent bot', async () => {
expect(botController.startOne('testBotIdNotExisting', {})).rejects.toThrowError(new BadRequestException('Bot does not exist'));
expect(botController.startOne('testBotIdNotExisting', '1', {})).rejects.toThrowError(new BadRequestException('Bot does not exist'));
});

it('bot start returns bad request when bot does not have user data', async () => {
expect(botController.startOne('noUser', {})).rejects.toThrowError(new BadRequestException('Bot does not contain user segment data'));
expect(botController.startOne('noUser', '1', {})).rejects.toThrowError(new BadRequestException('Bot does not contain user segment data'));
});

it('disabled bot returns unavailable error',async () => {
await expect(() => botController.startOne('disabled', {})).rejects.toThrowError(ServiceUnavailableException);
await expect(() => botController.startOne('disabled', '1', {})).rejects.toThrowError(ServiceUnavailableException);
});

it('only disabled bot returns unavailable error',async () => {
expect(botController.startOne('pinned', {})).resolves;
expect(botController.startOne('enabled', {})).resolves;
expect(botController.startOne('pinned', '1', {})).resolves;
expect(botController.startOne('enabled', '1', {})).resolves;
});

it('update only passes relevant bot data to bot service', async () => {
Expand Down Expand Up @@ -367,4 +374,25 @@ describe('BotController', () => {
expect(resp).toBeTruthy();
updateParametersPassed = [];
});

it('bot start schedule for future time', async () => {
const futureTime = new Date(Date.now() + 100000).toUTCString();
await botController.startOne(
'enabled',
futureTime,
{ 'conversation-authorization': 'testToken' }
);
expect(mockBotService.scheduleNotification).toHaveBeenCalledTimes(1);
expect(mockBotService.start).toHaveBeenCalledTimes(0);
});

it('bot start triggers immediately when triggerTime is not passed', async () => {
await botController.startOne(
'enabled',
undefined,
{ 'conversation-authorization': 'testToken' }
);
expect(mockBotService.scheduleNotification).toHaveBeenCalledTimes(0);
expect(mockBotService.start).toHaveBeenCalledTimes(1);
});
});
39 changes: 35 additions & 4 deletions src/modules/bot/bot.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { Request } from 'express';
import { extname } from 'path';
import fs from 'fs';
import { DeleteBotsDTO } from './dto/delete-bot-dto';
import { ModifyNotificationDTO } from './dto/update-bot.dto';


const editFileName = (req: Request, file: Express.Multer.File, callback) => {
Expand Down Expand Up @@ -200,7 +201,7 @@ export class BotController {
AddOwnerInfoInterceptor,
AddROToResponseInterceptor,
)
async startOne(@Param('id') id: string, @Headers() headers) {
async startOne(@Param('id') id: string, @Query('triggerTime') triggerTime: string | undefined, @Headers() headers) {
const bot: Prisma.BotGetPayload<{
include: {
users: {
Expand Down Expand Up @@ -228,10 +229,29 @@ export class BotController {
if (bot?.status == BotStatus.DISABLED) {
throw new ServiceUnavailableException("Bot is not enabled!");
}
if (triggerTime) {
const currentTime = new Date();
const scheduledTime = new Date(triggerTime);
if (scheduledTime.getTime() > currentTime.getTime()) {
await this.botService.scheduleNotification(id, scheduledTime, bot?.users[0].all?.config, headers['conversation-authorization']);
return;
}
}
const res = await this.botService.start(id, bot?.users[0].all?.config, headers['conversation-authorization']);
return res;
}

@Delete('/schedule/:id')
@UseInterceptors(
AddResponseObjectInterceptor,
AddAdminHeaderInterceptor,
AddOwnerInfoInterceptor,
AddROToResponseInterceptor,
)
async deleteSchedule(@Param('id') id: string) {
await this.botService.deleteSchedule(id);
}

@Get('/:id/addUser/:userId')
@UseInterceptors(
AddResponseObjectInterceptor,
Expand Down Expand Up @@ -281,8 +301,8 @@ export class BotController {
AddOwnerInfoInterceptor,
AddROToResponseInterceptor,
)
@Get('/getAllUsers/:id/:page?')
async getAllUsers(@Param('id') id: string, @Headers() headers, @Param('page') page?: number) {
@Get('/getAllUsers/:id/:segment/:page?')
async getAllUsers(@Param('id') id: string, @Headers() headers, @Param('segment') segment: number, @Param('page') page?: number) {
const bot: Prisma.BotGetPayload<{
include: {
users: {
Expand All @@ -300,7 +320,7 @@ export class BotController {
}> | null = await this.botService.findOne(id);
bot ? console.log('Users for the bot', bot['users']) : '';
if (bot && bot.users[0].all) {
const users = await this.service.resolve(bot.users[0].all, page, bot.ownerID, headers['conversation-authorization']);
const users = await this.service.resolve(bot.users[0].all, segment, page, bot.ownerID, headers['conversation-authorization']);
return users;
}
return bot;
Expand Down Expand Up @@ -372,4 +392,15 @@ export class BotController {
}
return await this.botService.getBroadcastReport(botId, limit, nextPage);
}

@Post('/modifyNotification/:botId')
@UseInterceptors(
AddResponseObjectInterceptor,
AddAdminHeaderInterceptor,
AddOwnerInfoInterceptor,
AddROToResponseInterceptor,
)
async modifyNotification(@Param('botId') botId: string, @Body() body: ModifyNotificationDTO) {
await this.botService.modifyNotification(botId, body.title, body.description);
}
}
Loading

0 comments on commit f06da7e

Please sign in to comment.