From d81c43134997c462728e85f24ec337a52fa4f53a Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Tue, 7 Oct 2025 12:52:14 +0530
Subject: [PATCH 01/27] Fix: added time zone property for decoding the user
time
---
src/modules/common/enum/testflow.enum.ts | 2 +-
src/modules/common/models/testflow.model.ts | 6 ++++++
.../workspace/payloads/testflow.payload.ts | 5 +++++
.../workspace/services/testflow.service.ts | 21 +++++++++++++++----
4 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/src/modules/common/enum/testflow.enum.ts b/src/modules/common/enum/testflow.enum.ts
index 8e6b02c13..f6bc04d5f 100644
--- a/src/modules/common/enum/testflow.enum.ts
+++ b/src/modules/common/enum/testflow.enum.ts
@@ -32,7 +32,7 @@ export enum RequestDataTypeEnum {
export interface EmailData {
userName: string;
scheduleName: string;
- scheduleLastestRun: Date;
+ scheduleLastestRun: string;
scheduleRunResult: string;
scheduleRunPassedCount: number;
scheduleRunFailedCount: number;
diff --git a/src/modules/common/models/testflow.model.ts b/src/modules/common/models/testflow.model.ts
index 766915d74..5a0b4d236 100644
--- a/src/modules/common/models/testflow.model.ts
+++ b/src/modules/common/models/testflow.model.ts
@@ -549,6 +549,12 @@ export class TestflowSchedular {
@IsOptional()
schedularRunHistory?: TestFlowSchedularRunHistory[];
+
+ @IsString()
+ @ApiProperty({ required: true, example: "Asia/Kolkata" })
+ @IsOptional()
+ timeZone:string;
+
@IsDate()
@IsOptional()
createdAt?: Date;
diff --git a/src/modules/workspace/payloads/testflow.payload.ts b/src/modules/workspace/payloads/testflow.payload.ts
index 3e15cc72c..15c9f20b8 100644
--- a/src/modules/workspace/payloads/testflow.payload.ts
+++ b/src/modules/workspace/payloads/testflow.payload.ts
@@ -181,4 +181,9 @@ export class CreateTestflowSchedularDto {
@ValidateNested()
@Type(() => NotificationDto)
notification?: NotificationDto;
+
+ @IsString()
+ @ApiProperty({ required: true, example: "Asia/Kolkata" })
+ @IsOptional()
+ timeZone:string;
}
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 3ea1426d5..bf8158f0e 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -151,11 +151,11 @@ export class TestflowService implements OnModuleInit {
throw new NotFoundException("Schedule not found");
}
- let environmentName = "";
+ let environmentName = "";
if(updateScheduleDto?.environmentId){
const environmentData = await this.environmentReposistory.get(updateScheduleDto?.environmentId);
- environmentName = environmentData?.name || "";
- }
+ environmentName = environmentData?.name || "";
+ }
// Merge update fields, ensure id is present
const updatedSchedular: TestflowSchedular = {
...existingSchedular,
@@ -537,6 +537,7 @@ export class TestflowService implements OnModuleInit {
schedularName: jobName,
executedCount: 0,
lastExecuted: undefined,
+ timeZone: schedularData?.timeZone,
createdAt: new Date(),
updatedAt: new Date(),
createdBy: user._id.toString(),
@@ -789,7 +790,19 @@ export class TestflowService implements OnModuleInit {
const emailData: EmailData = {
userName: userDetails?.name,
scheduleName: getSchedular.name,
- scheduleLastestRun: new Date(getSchedular.lastExecuted),
+ scheduleLastestRun: new Date(getSchedular.lastExecuted).toLocaleString(
+ "en-IN",
+ {
+ year: "numeric",
+ month: "short",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: true,
+ timeZone: getSchedular.timeZone,
+ },
+ ),
scheduleRunResult: scheduleRunResult,
scheduleRunPassedCount: data.successRequests,
scheduleRunFailedCount: data.failedRequests,
From d1b67f417464bc7800a6bc6fded27b2a54e123ff Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Tue, 7 Oct 2025 17:28:39 +0530
Subject: [PATCH 02/27] Fix: I have updated module function to handle empty
testflows
---
.../repositories/testflow.repository.ts | 29 ++++++---
.../workspace/services/testflow.service.ts | 62 +++++++++++--------
2 files changed, 58 insertions(+), 33 deletions(-)
diff --git a/src/modules/workspace/repositories/testflow.repository.ts b/src/modules/workspace/repositories/testflow.repository.ts
index 8bb4f666e..310de46b0 100644
--- a/src/modules/workspace/repositories/testflow.repository.ts
+++ b/src/modules/workspace/repositories/testflow.repository.ts
@@ -282,13 +282,28 @@ export class TestflowRepository {
}
async getAll(): Promise[]> {
- const data = await this.db
- .collection(Collections.TESTFLOW)
- .find({})
- .toArray();
- if (!data || data.length === 0) {
- throw new BadRequestException("No Testflow data found");
+ try {
+ // Check if the Testflow collection exists in the database
+ const collections = await this.db.listCollections().toArray();
+ const testflowCollectionExists = collections.some(
+ (col) => col.name === Collections.TESTFLOW,
+ );
+ if (!testflowCollectionExists) {
+ console.log("Testflow collection does not exist, skipping...");
+ return []; // Return empty array
+ }
+ const data = await this.db
+ .collection(Collections.TESTFLOW)
+ .find({})
+ .toArray();
+ if (!data || data.length === 0) {
+ console.log("No Testflow data found");
+ return [];
+ }
+ return data;
+ } catch (error) {
+ console.error("Error fetching Testflow data:", error);
+ return [];
}
- return data;
}
}
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 3ea1426d5..fe5ebea17 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -84,28 +84,34 @@ export class TestflowService implements OnModuleInit {
async onModuleInit() {
this.logger.log("Bootstrapping schedulers from DB...");
const testflows = await this.testflowRepository.getAll();
- for (const tf of testflows) {
- if (!tf.schedules?.length) continue;
- for (const schedule of tf.schedules) {
- const runCycleConfig = this.buildRunCycleConfig(
- schedule.runConfiguration,
- );
- if (schedule.isActive && schedule.cronExpression) {
- await this.testflowSchedulerService.addSchedulerJob(
- runCycleConfig,
- this.getScheduledExecutionCallback(
- tf._id.toString(),
- schedule.environmentId,
- tf.workspaceId,
- schedule.id,
- ),
- schedule.schedularName,
- schedule.cronExpression,
- schedule.id,
- "UTC",
+ if (testflows.length > 0) {
+ for (const tf of testflows) {
+ if (!tf.schedules?.length) continue;
+
+ for (const schedule of tf.schedules) {
+ const runCycleConfig = this.buildRunCycleConfig(
+ schedule.runConfiguration,
);
+
+ if (schedule.isActive && schedule.cronExpression) {
+ await this.testflowSchedulerService.addSchedulerJob(
+ runCycleConfig,
+ this.getScheduledExecutionCallback(
+ tf._id.toString(),
+ schedule.environmentId,
+ tf.workspaceId,
+ schedule.id,
+ ),
+ schedule.schedularName,
+ schedule.cronExpression,
+ schedule.id,
+ "UTC",
+ );
+ }
}
}
+ } else {
+ this.logger.log("No testflows found, skipping scheduler bootstrap");
}
}
@@ -151,11 +157,13 @@ export class TestflowService implements OnModuleInit {
throw new NotFoundException("Schedule not found");
}
- let environmentName = "";
- if(updateScheduleDto?.environmentId){
- const environmentData = await this.environmentReposistory.get(updateScheduleDto?.environmentId);
- environmentName = environmentData?.name || "";
- }
+ let environmentName = "";
+ if (updateScheduleDto?.environmentId) {
+ const environmentData = await this.environmentReposistory.get(
+ updateScheduleDto?.environmentId,
+ );
+ environmentName = environmentData?.name || "";
+ }
// Merge update fields, ensure id is present
const updatedSchedular: TestflowSchedular = {
...existingSchedular,
@@ -520,8 +528,10 @@ export class TestflowService implements OnModuleInit {
throw new BadRequestException("Invalid run cycle configuration");
}
let environmentName = "";
- if(schedularData?.environmentId){
- const environmentData = await this.environmentReposistory.get(schedularData?.environmentId);
+ if (schedularData?.environmentId) {
+ const environmentData = await this.environmentReposistory.get(
+ schedularData?.environmentId,
+ );
environmentName = environmentData?.name || "";
}
// Save scheduler details in DB
From 1d834dc06918d1e16e60d6e293d76acca8ab1efb Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Tue, 7 Oct 2025 17:52:51 +0530
Subject: [PATCH 03/27] Fix: I have updated module function to handle empty
testflows
---
.../repositories/testflow.repository.ts | 39 ++++++++++---------
.../workspace/services/testflow.service.ts | 26 ++++++++++---
2 files changed, 42 insertions(+), 23 deletions(-)
diff --git a/src/modules/workspace/repositories/testflow.repository.ts b/src/modules/workspace/repositories/testflow.repository.ts
index 310de46b0..68b3c877a 100644
--- a/src/modules/workspace/repositories/testflow.repository.ts
+++ b/src/modules/workspace/repositories/testflow.repository.ts
@@ -282,28 +282,31 @@ export class TestflowRepository {
}
async getAll(): Promise[]> {
+ const data = await this.db
+ .collection(Collections.TESTFLOW)
+ .find({})
+ .toArray();
+ if (!data || data.length === 0) {
+ throw new BadRequestException("No Testflow data found");
+ }
+ return data;
+ }
+
+ async getAllCollectionNames(): Promise {
try {
- // Check if the Testflow collection exists in the database
const collections = await this.db.listCollections().toArray();
- const testflowCollectionExists = collections.some(
- (col) => col.name === Collections.TESTFLOW,
- );
- if (!testflowCollectionExists) {
- console.log("Testflow collection does not exist, skipping...");
- return []; // Return empty array
- }
- const data = await this.db
- .collection(Collections.TESTFLOW)
- .find({})
- .toArray();
- if (!data || data.length === 0) {
- console.log("No Testflow data found");
- return [];
+ const collectionNames = collections.map((col) => col.name);
+
+ if (!collectionNames || collectionNames.length === 0) {
+ throw new BadRequestException("No collections found in the database");
}
- return data;
+
+ return collectionNames;
} catch (error) {
- console.error("Error fetching Testflow data:", error);
- return [];
+ console.error("Error fetching collection names:", error);
+ throw new BadRequestException(
+ error.message || "Failed to fetch collection names",
+ );
}
}
}
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index fe5ebea17..a42a7ce9a 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -61,6 +61,7 @@ import { Logger } from "@nestjs/common";
import { OnModuleInit } from "@nestjs/common";
import { UserRepository } from "@src/modules/identity/repositories/user.repository";
import { EnvironmentRepository } from "../repositories/environment.repository";
+import { Collections } from "@src/modules/common/enum/database.collection.enum";
/**
* Testflow Service
@@ -82,9 +83,24 @@ export class TestflowService implements OnModuleInit {
) {}
async onModuleInit() {
- this.logger.log("Bootstrapping schedulers from DB...");
- const testflows = await this.testflowRepository.getAll();
- if (testflows.length > 0) {
+ try {
+ this.logger.log("Bootstrapping schedulers from DB...");
+ const collectionItems =
+ await this.testflowRepository.getAllCollectionNames();
+ const testflowCollectionExists = collectionItems.some(
+ (col) => col === Collections.TESTFLOW,
+ );
+ if (!testflowCollectionExists) {
+ this.logger.warn(
+ "Testflow collection does not exist — skipping scheduler bootstrap.",
+ );
+ return;
+ }
+ const testflows = await this.testflowRepository.getAll();
+ if (testflows.length === 0) {
+ this.logger.log("No testflows found — skipping scheduler bootstrap.");
+ return;
+ }
for (const tf of testflows) {
if (!tf.schedules?.length) continue;
@@ -110,8 +126,8 @@ export class TestflowService implements OnModuleInit {
}
}
}
- } else {
- this.logger.log("No testflows found, skipping scheduler bootstrap");
+ } catch (error) {
+ this.logger.error("Error during scheduler bootstrap:", error);
}
}
From 06f35b7420d7fe8e3634b0086394065837504d34 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Tue, 7 Oct 2025 18:11:42 +0530
Subject: [PATCH 04/27] Fix: I have updated module function to handle empty
testflows
---
.../repositories/testflow.repository.ts | 18 +++++++-----------
.../workspace/services/testflow.service.ts | 9 +++------
2 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/src/modules/workspace/repositories/testflow.repository.ts b/src/modules/workspace/repositories/testflow.repository.ts
index 68b3c877a..f8b5045d0 100644
--- a/src/modules/workspace/repositories/testflow.repository.ts
+++ b/src/modules/workspace/repositories/testflow.repository.ts
@@ -292,21 +292,17 @@ export class TestflowRepository {
return data;
}
- async getAllCollectionNames(): Promise {
+ async isTestflowCollectionExist(): Promise {
try {
const collections = await this.db.listCollections().toArray();
- const collectionNames = collections.map((col) => col.name);
-
- if (!collectionNames || collectionNames.length === 0) {
- throw new BadRequestException("No collections found in the database");
- }
+ const testflowExists = collections.some(
+ (col) => col.name === Collections.TESTFLOW,
+ );
- return collectionNames;
+ return testflowExists;
} catch (error) {
- console.error("Error fetching collection names:", error);
- throw new BadRequestException(
- error.message || "Failed to fetch collection names",
- );
+ console.error("Error checking Testflow collection existence:", error);
+ return false; // Return false if any error occurs
}
}
}
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index a42a7ce9a..4c3ab0f8b 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -85,12 +85,9 @@ export class TestflowService implements OnModuleInit {
async onModuleInit() {
try {
this.logger.log("Bootstrapping schedulers from DB...");
- const collectionItems =
- await this.testflowRepository.getAllCollectionNames();
- const testflowCollectionExists = collectionItems.some(
- (col) => col === Collections.TESTFLOW,
- );
- if (!testflowCollectionExists) {
+ const testflowCollection =
+ await this.testflowRepository.isTestflowCollectionExist();
+ if (!testflowCollection) {
this.logger.warn(
"Testflow collection does not exist — skipping scheduler bootstrap.",
);
From dda25e97f486090199e870ed19161778ae831b30 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Tue, 7 Oct 2025 18:22:11 +0530
Subject: [PATCH 05/27] Fix: I have updated module function to handle empty
testflows
---
.../workspace/repositories/testflow.repository.ts | 14 --------------
src/modules/workspace/services/testflow.service.ts | 8 --------
2 files changed, 22 deletions(-)
diff --git a/src/modules/workspace/repositories/testflow.repository.ts b/src/modules/workspace/repositories/testflow.repository.ts
index f8b5045d0..8bb4f666e 100644
--- a/src/modules/workspace/repositories/testflow.repository.ts
+++ b/src/modules/workspace/repositories/testflow.repository.ts
@@ -291,18 +291,4 @@ export class TestflowRepository {
}
return data;
}
-
- async isTestflowCollectionExist(): Promise {
- try {
- const collections = await this.db.listCollections().toArray();
- const testflowExists = collections.some(
- (col) => col.name === Collections.TESTFLOW,
- );
-
- return testflowExists;
- } catch (error) {
- console.error("Error checking Testflow collection existence:", error);
- return false; // Return false if any error occurs
- }
- }
}
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 4c3ab0f8b..08ca17322 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -85,14 +85,6 @@ export class TestflowService implements OnModuleInit {
async onModuleInit() {
try {
this.logger.log("Bootstrapping schedulers from DB...");
- const testflowCollection =
- await this.testflowRepository.isTestflowCollectionExist();
- if (!testflowCollection) {
- this.logger.warn(
- "Testflow collection does not exist — skipping scheduler bootstrap.",
- );
- return;
- }
const testflows = await this.testflowRepository.getAll();
if (testflows.length === 0) {
this.logger.log("No testflows found — skipping scheduler bootstrap.");
From 07c940e1d946e19860c462acd2e79ffc44e52df3 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Tue, 7 Oct 2025 20:13:43 +0530
Subject: [PATCH 06/27] Fix: I have resolved template issues
---
src/modules/common/enum/testflow.enum.ts | 2 +-
.../views/testflowScheduleRunEmail.handlebars | 26 +++++++++++++------
.../workspace/services/testflow.service.ts | 5 ++--
3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/src/modules/common/enum/testflow.enum.ts b/src/modules/common/enum/testflow.enum.ts
index f6bc04d5f..b1ed21226 100644
--- a/src/modules/common/enum/testflow.enum.ts
+++ b/src/modules/common/enum/testflow.enum.ts
@@ -37,7 +37,7 @@ export interface EmailData {
scheduleRunPassedCount: number;
scheduleRunFailedCount: number;
scheduleRunTotalRequest: number;
- scheduleRunPassPercentage: number;
+ scheduleRunPassPercentage: string;
scheduleTotalTime: string | number;
scheduleRunEnvName: string;
isSuccess: boolean;
diff --git a/src/modules/views/testflowScheduleRunEmail.handlebars b/src/modules/views/testflowScheduleRunEmail.handlebars
index 37c613f37..49b508abe 100644
--- a/src/modules/views/testflowScheduleRunEmail.handlebars
+++ b/src/modules/views/testflowScheduleRunEmail.handlebars
@@ -116,7 +116,7 @@
{{else if isFailed}}
❌️ Failed
{{else if isPartial}}
- ⚠️ Partially Failed({{scheduleRunPassedCount}}/{{scheduleRunTotalRequest}} Requests Failed)
+ ⚠️ Partially Failed({{scheduleRunFailedCount}}/{{scheduleRunTotalRequest}} Requests Failed)
{{/if}}
@@ -126,7 +126,7 @@
- Run Summary
+ Run Summary
|
@@ -143,7 +143,7 @@
| Pass Rate |
- {{scheduleRunPassPercentage}} |
+ {{scheduleRunPassPercentage}}% |
| Total Duration |
@@ -178,11 +178,21 @@
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index bf8158f0e..bf4a8491a 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -785,8 +785,7 @@ export class TestflowService implements OnModuleInit {
const userDetails = await this.userReposistory.getUserById(
data.createdBy,
);
- const successPercentage =
- (data.successRequests / totalRequestCount) * 100;
+ const successPercentage = Math.round((data.successRequests / totalRequestCount) * 100 * 100) / 100;
const emailData: EmailData = {
userName: userDetails?.name,
scheduleName: getSchedular.name,
@@ -807,7 +806,7 @@ export class TestflowService implements OnModuleInit {
scheduleRunPassedCount: data.successRequests,
scheduleRunFailedCount: data.failedRequests,
scheduleRunTotalRequest: data.successRequests + data.failedRequests,
- scheduleRunPassPercentage: successPercentage,
+ scheduleRunPassPercentage: successPercentage.toString(),
scheduleTotalTime: data.totalTime,
scheduleRunEnvName: response.environmentName,
isSuccess: data.successRequests === totalRequestCount,
From 678361c04671b58535da09c6b82b077a3ecc448d Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Wed, 8 Oct 2025 13:37:18 +0530
Subject: [PATCH 07/27] fix: time updated according to the Utc
---
src/modules/workspace/services/testflow.service.ts | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 451afff62..ddcaaee94 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -825,19 +825,7 @@ export class TestflowService implements OnModuleInit {
const emailData: EmailData = {
userName: userDetails?.name,
scheduleName: getSchedular.name,
- scheduleLastestRun: new Date(getSchedular.lastExecuted).toLocaleString(
- "en-IN",
- {
- year: "numeric",
- month: "short",
- day: "2-digit",
- hour: "2-digit",
- minute: "2-digit",
- second: "2-digit",
- hour12: true,
- timeZone: getSchedular.timeZone,
- },
- ),
+ scheduleLastestRun: new Date(getSchedular.lastExecuted).toString(),
scheduleRunResult: scheduleRunResult,
scheduleRunPassedCount: data.successRequests,
scheduleRunFailedCount: data.failedRequests,
From 79f42e4930cd312cdbb596b9741f4f11249f70c8 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Wed, 8 Oct 2025 13:39:56 +0530
Subject: [PATCH 08/27] fix: time updated according to the Utc
---
src/modules/common/models/testflow.model.ts | 2 +-
src/modules/workspace/payloads/testflow.payload.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/modules/common/models/testflow.model.ts b/src/modules/common/models/testflow.model.ts
index 5a0b4d236..3d2882c20 100644
--- a/src/modules/common/models/testflow.model.ts
+++ b/src/modules/common/models/testflow.model.ts
@@ -553,7 +553,7 @@ export class TestflowSchedular {
@IsString()
@ApiProperty({ required: true, example: "Asia/Kolkata" })
@IsOptional()
- timeZone:string;
+ timeZone?:string;
@IsDate()
@IsOptional()
diff --git a/src/modules/workspace/payloads/testflow.payload.ts b/src/modules/workspace/payloads/testflow.payload.ts
index 15c9f20b8..4ef0caa4c 100644
--- a/src/modules/workspace/payloads/testflow.payload.ts
+++ b/src/modules/workspace/payloads/testflow.payload.ts
@@ -185,5 +185,5 @@ export class CreateTestflowSchedularDto {
@IsString()
@ApiProperty({ required: true, example: "Asia/Kolkata" })
@IsOptional()
- timeZone:string;
+ timeZone?:string;
}
From 9a5021869f6aa7227542c13f14e8feecf1ffd22c Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Wed, 8 Oct 2025 15:14:31 +0530
Subject: [PATCH 09/27] feat: add permission check to get a testflow
---
.../controllers/testflow.controller.ts | 19 +++++++-----
.../workspace/services/testflow.service.ts | 29 +++++--------------
2 files changed, 18 insertions(+), 30 deletions(-)
diff --git a/src/modules/workspace/controllers/testflow.controller.ts b/src/modules/workspace/controllers/testflow.controller.ts
index a79f8a896..5f4c75aa0 100644
--- a/src/modules/workspace/controllers/testflow.controller.ts
+++ b/src/modules/workspace/controllers/testflow.controller.ts
@@ -109,7 +109,7 @@ export class TestflowController {
* @description This will retrieve a specific Testflow using its ID,
* returning the Testflow object if found.
*/
- @Get("testflow/:testflowId")
+ @Get(":workspaceId/testflow/:testflowId")
@ApiOperation({
summary: "Get Individual Testflow",
description: "This will get individual testflow of a workspace",
@@ -121,10 +121,13 @@ export class TestflowController {
})
@ApiResponse({ status: 400, description: "Fetch Testflow Request Failed" })
async getTestflow(
+ @Param("workspaceId") workspaceId: string,
@Param("testflowId") testflowId: string,
@Res() res: FastifyReply,
+ @Req() request: ExtendedFastifyRequest,
) {
- const testflow = await this.testflowService.getTestflow(testflowId);
+ const user = request.user;
+ const testflow = await this.testflowService.getTestflow(workspaceId, testflowId, user._id);
const responseData = new ApiResponseService(
"Success",
HttpStatusCode.OK,
@@ -321,8 +324,8 @@ export class TestflowController {
const response = await this.testflowService.createTestflowSchedular(
createTestflowSchedularDto,
user,
- );
- const testflow = await this.testflowService.getTestflow(createTestflowSchedularDto.testflowId);
+ );
+ const testflow = await this.testflowService.getTestflow(createTestflowSchedularDto.workspaceId, createTestflowSchedularDto.testflowId, user._id);
const result = {
testflow,
schedule:response
@@ -359,7 +362,7 @@ export class TestflowController {
workspaceId,
user,
);
- const testflow = await this.testflowService.getTestflow(testflowId);
+ const testflow = await this.testflowService.getTestflow(workspaceId, testflowId, user._id);
const responseData = new ApiResponseService(
"Success",
HttpStatusCode.OK,
@@ -390,7 +393,7 @@ export class TestflowController {
workspaceId,
user,
);
- const testflow = await this.testflowService.getTestflow(testflowId);
+ const testflow = await this.testflowService.getTestflow(workspaceId, testflowId, user._id);
const responseData = new ApiResponseService(
"Success",
HttpStatusCode.OK,
@@ -421,7 +424,7 @@ export class TestflowController {
workspaceId,
user,
);
- const testflow = await this.testflowService.getTestflow(testflowId);
+ const testflow = await this.testflowService.getTestflow(workspaceId, testflowId, user._id);
const responseData = new ApiResponseService(
"Success",
HttpStatusCode.OK,
@@ -448,7 +451,7 @@ export class TestflowController {
) {
const user = request.user;
await this.testflowService.deleteScheduleRunHistory(workspaceId, testflowId, scheduleId, runHistoryId, user);
- const testflow = await this.testflowService.getTestflow(testflowId);
+ const testflow = await this.testflowService.getTestflow(workspaceId, testflowId, user._id);
const responseData = new ApiResponseService(
"Success",
HttpStatusCode.OK,
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 4500a6888..4768573d9 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -330,8 +330,9 @@ export class TestflowService implements OnModuleInit {
* Fetches single testflow.
* @param id - Testflow id you want to fetch.
*/
- async getTestflow(id: string): Promise> {
- return await this.testflowRepository.get(id);
+ async getTestflow(workspaceId: string, testflowId: string, userId: ObjectId): Promise> {
+ await this.checkPermission(workspaceId, userId);
+ return await this.testflowRepository.get(testflowId);
}
/**
@@ -341,6 +342,9 @@ export class TestflowService implements OnModuleInit {
*/
async checkPermission(workspaceId: string, userid: ObjectId): Promise {
const workspace = await this.workspaceService.get(workspaceId);
+ if(workspace.workspaceType === WorkspaceType.PUBLIC){
+ return;
+ }
const hasPermission = workspace.users.some((user) => {
return user.id.toString() === userid.toString();
});
@@ -502,26 +506,7 @@ export class TestflowService implements OnModuleInit {
user: DecodedUserObject,
) {
try {
- const workspaceUsers = await this.workspaceReposistory.get(
- schedularData?.workspaceId,
- );
- if (!workspaceUsers) {
- throw new NotFoundException("Workspace not found.");
- }
- const userDetails = workspaceUsers.users.find(
- (item) => item.id === user._id.toString(),
- );
- if (!userDetails) {
- throw new NotFoundException("User not found in workspace.");
- }
- if (
- userDetails.role !== WorkspaceRole.ADMIN &&
- userDetails.role !== WorkspaceRole.EDITOR
- ) {
- throw new ForbiddenException(
- "User does not have permission to perform this action.",
- );
- }
+ await this.isWorkspaceAdminorEditor(schedularData?.workspaceId, user._id);
// Build cron config
const runCycleConfig = this.buildRunCycleConfig(
schedularData.runConfiguration,
From b9c634a9f4c19e56c6033fc492fcf8d12c899d60 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Wed, 8 Oct 2025 15:58:45 +0530
Subject: [PATCH 10/27] fix: time updated according to the Utc
---
src/modules/common/models/testflow.model.ts | 6 --
.../workspace/payloads/testflow.payload.ts | 5 --
.../repositories/testflow.repository.ts | 68 +++++++++----------
.../workspace/services/testflow.service.ts | 14 ++--
4 files changed, 43 insertions(+), 50 deletions(-)
diff --git a/src/modules/common/models/testflow.model.ts b/src/modules/common/models/testflow.model.ts
index 3d2882c20..766915d74 100644
--- a/src/modules/common/models/testflow.model.ts
+++ b/src/modules/common/models/testflow.model.ts
@@ -549,12 +549,6 @@ export class TestflowSchedular {
@IsOptional()
schedularRunHistory?: TestFlowSchedularRunHistory[];
-
- @IsString()
- @ApiProperty({ required: true, example: "Asia/Kolkata" })
- @IsOptional()
- timeZone?:string;
-
@IsDate()
@IsOptional()
createdAt?: Date;
diff --git a/src/modules/workspace/payloads/testflow.payload.ts b/src/modules/workspace/payloads/testflow.payload.ts
index 4ef0caa4c..3e15cc72c 100644
--- a/src/modules/workspace/payloads/testflow.payload.ts
+++ b/src/modules/workspace/payloads/testflow.payload.ts
@@ -181,9 +181,4 @@ export class CreateTestflowSchedularDto {
@ValidateNested()
@Type(() => NotificationDto)
notification?: NotificationDto;
-
- @IsString()
- @ApiProperty({ required: true, example: "Asia/Kolkata" })
- @IsOptional()
- timeZone?:string;
}
diff --git a/src/modules/workspace/repositories/testflow.repository.ts b/src/modules/workspace/repositories/testflow.repository.ts
index 7243b9054..76f27984e 100644
--- a/src/modules/workspace/repositories/testflow.repository.ts
+++ b/src/modules/workspace/repositories/testflow.repository.ts
@@ -206,7 +206,7 @@ export class TestflowRepository {
schedularId: string,
runHistoryItem: TestFlowSchedularRunHistory,
): Promise {
- const now = new Date();
+ const nowUtc = new Date().toISOString();
if (!testflowId || !schedularId) {
throw new Error("Both testflowId and schedularId are required");
}
@@ -215,8 +215,8 @@ export class TestflowRepository {
{
$inc: { "schedules.$[elem].executedCount": 1 },
$set: {
- "schedules.$[elem].lastExecuted": now,
- updatedAt: now,
+ "schedules.$[elem].lastExecuted": nowUtc,
+ updatedAt: nowUtc,
},
$push: {
"schedules.$[elem].schedularRunHistory": {
@@ -231,38 +231,38 @@ export class TestflowRepository {
);
}
- /**
- * Edit a schedular execution (run history item) in a testflow's schedule.
- * @param {string} testflowId - The testflow document ID.
- * @param {string} schedularId - The schedule ID.
- * @param {Partial} updatedRunHistory - The updated fields for the run history item.
- * @returns {Promise} - The result of the update operation.
- */
- async editSchedularExecution(
- testflowId: string,
- schedularId: string,
- updatedRunHistory: Partial,
- ): Promise {
- if (!testflowId || !schedularId || !updatedRunHistory.id) {
- throw new Error("testflowId, schedularId, and runHistoryId are required");
- }
- // Build the update object for only the provided fields
- const setObj: Record = {};
- for (const [key, value] of Object.entries(updatedRunHistory)) {
- setObj[`schedules.$[elem].schedularRunHistory.$[run].${key}`] = value;
- }
- setObj["updatedAt"] = new Date();
- return this.db.collection(Collections.TESTFLOW).updateOne(
- { _id: new ObjectId(testflowId) },
- { $set: setObj },
- {
- arrayFilters: [
- { "elem.id": schedularId },
- { "run.id": updatedRunHistory.id },
- ],
- },
- );
+ /**
+ * Edit a schedular execution (run history item) in a testflow's schedule.
+ * @param {string} testflowId - The testflow document ID.
+ * @param {string} schedularId - The schedule ID.
+ * @param {Partial} updatedRunHistory - The updated fields for the run history item.
+ * @returns {Promise} - The result of the update operation.
+ */
+ async editSchedularExecution(
+ testflowId: string,
+ schedularId: string,
+ updatedRunHistory: Partial,
+ ): Promise {
+ if (!testflowId || !schedularId || !updatedRunHistory.id) {
+ throw new Error("testflowId, schedularId, and runHistoryId are required");
+ }
+ // Build the update object for only the provided fields
+ const setObj: Record = {};
+ for (const [key, value] of Object.entries(updatedRunHistory)) {
+ setObj[`schedules.$[elem].schedularRunHistory.$[run].${key}`] = value;
}
+ setObj["updatedAt"] = new Date();
+ return this.db.collection(Collections.TESTFLOW).updateOne(
+ { _id: new ObjectId(testflowId) },
+ { $set: setObj },
+ {
+ arrayFilters: [
+ { "elem.id": schedularId },
+ { "run.id": updatedRunHistory.id },
+ ],
+ },
+ );
+ }
async updateSchedularStatus(
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index ddcaaee94..52656bed7 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -553,7 +553,6 @@ export class TestflowService implements OnModuleInit {
schedularName: jobName,
executedCount: 0,
lastExecuted: undefined,
- timeZone: schedularData?.timeZone,
createdAt: new Date(),
updatedAt: new Date(),
createdBy: user._id.toString(),
@@ -764,12 +763,12 @@ export class TestflowService implements OnModuleInit {
isScheduled,
status: "pending",
requests: [],
- responses:[],
+ responses: [],
nodes: [],
edges: [],
failedRequests: 0,
successRequests: 0,
- totalTime:"0 ms",
+ totalTime: "0 ms",
createdAt: new Date(),
};
//Save execution result in DB
@@ -821,11 +820,16 @@ export class TestflowService implements OnModuleInit {
const userDetails = await this.userReposistory.getUserById(
data.createdBy,
);
- const successPercentage = Math.round((data.successRequests / totalRequestCount) * 100 * 100) / 100;
+ const successPercentage =
+ Math.round((data.successRequests / totalRequestCount) * 100 * 100) /
+ 100;
const emailData: EmailData = {
userName: userDetails?.name,
scheduleName: getSchedular.name,
- scheduleLastestRun: new Date(getSchedular.lastExecuted).toString(),
+ scheduleLastestRun: new Date(getSchedular.lastExecuted)
+ .toISOString()
+ .replace("T", " ")
+ .replace("Z", " UTC"),
scheduleRunResult: scheduleRunResult,
scheduleRunPassedCount: data.successRequests,
scheduleRunFailedCount: data.failedRequests,
From d2c63a591513c3efe1a6b42a8cb05552a4c569a3 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Wed, 8 Oct 2025 16:12:39 +0530
Subject: [PATCH 11/27] fix: time updated according to the Utc
---
.../workspace/services/testflow.service.ts | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 52656bed7..6fbeb86bc 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -826,10 +826,18 @@ export class TestflowService implements OnModuleInit {
const emailData: EmailData = {
userName: userDetails?.name,
scheduleName: getSchedular.name,
- scheduleLastestRun: new Date(getSchedular.lastExecuted)
- .toISOString()
- .replace("T", " ")
- .replace("Z", " UTC"),
+ scheduleLastestRun:
+ new Date(getSchedular.lastExecuted).toLocaleString("en-US", {
+ timeZone: "UTC",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ fractionalSecondDigits: 3,
+ hour12: false,
+ }) + " UTC",
scheduleRunResult: scheduleRunResult,
scheduleRunPassedCount: data.successRequests,
scheduleRunFailedCount: data.failedRequests,
From 2aa3ec5570ae01224d68464efec2a5f55c6b2732 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Wed, 8 Oct 2025 16:34:55 +0530
Subject: [PATCH 12/27] fix: time updated according to the Utc
---
src/modules/workspace/services/testflow.service.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 6fbeb86bc..1b719ed3e 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -835,7 +835,6 @@ export class TestflowService implements OnModuleInit {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
- fractionalSecondDigits: 3,
hour12: false,
}) + " UTC",
scheduleRunResult: scheduleRunResult,
From c406072c8c25d84076c4aeaba0e3bdff067d2981 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Wed, 8 Oct 2025 18:35:43 +0530
Subject: [PATCH 13/27] feat: add permission check to get a testflow
---
src/modules/workspace/services/testflow-run.service.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/modules/workspace/services/testflow-run.service.ts b/src/modules/workspace/services/testflow-run.service.ts
index d4dc848f3..4d9aaec50 100644
--- a/src/modules/workspace/services/testflow-run.service.ts
+++ b/src/modules/workspace/services/testflow-run.service.ts
@@ -82,8 +82,10 @@ export class TestflowRunService {
);
let environmentData;
if (environmentId) {
- environmentData =
- await this.environmentReposistory.get(environmentId);
+ try{
+ environmentData =
+ await this.environmentReposistory.get(environmentId);
+ }catch(err){}
}
const activeVariables = this.combineEnvironmentData(
globalEnvDetails?.variable || [],
From 03e55aa4cb38b0778f827794f02c46a725fdb702 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 00:47:35 +0530
Subject: [PATCH 14/27] fix: cron expression on edit
---
.../workspace/services/testflow.service.ts | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 4768573d9..3cb9fef40 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -170,6 +170,18 @@ export class TestflowService implements OnModuleInit {
);
environmentName = environmentData?.name || "";
}
+ let cronExpression;
+ if(updateScheduleDto.runConfiguration){
+ const runCycleConfig = this.buildRunCycleConfig(updateScheduleDto.runConfiguration);
+ cronExpression = this.generateCronExpression(runCycleConfig);
+ if (!cronExpression) {
+ updateScheduleDto.cronExpression = null;
+ }
+ else{
+ updateScheduleDto.cronExpression = cronExpression;
+ }
+ }
+
// Merge update fields, ensure id is present
const updatedSchedular: TestflowSchedular = {
...existingSchedular,
@@ -187,7 +199,6 @@ export class TestflowService implements OnModuleInit {
);
// Optionally update cron job if runConfiguration or isActive changed
if (
- updateScheduleDto.runConfiguration ||
updateScheduleDto.isActive !== undefined
) {
const schedular = await this.testflowRepository.getSchedularById(
@@ -202,9 +213,7 @@ export class TestflowService implements OnModuleInit {
const runCycleConfig = this.buildRunCycleConfig(
schedular.runConfiguration,
);
- const cronExpression =
- schedular.cronExpression ||
- this.generateCronExpression(runCycleConfig);
+ const cronExpression = schedular.cronExpression;
await this.testflowSchedulerService.addSchedulerJob(
runCycleConfig,
this.getScheduledExecutionCallback(
From 3e40503a305041fa053095f760963d815f56b906 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 01:01:07 +0530
Subject: [PATCH 15/27] fix: cron expression on edit
---
src/modules/workspace/services/testflow.service.ts | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 3cb9fef40..ab8364058 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -197,7 +197,7 @@ export class TestflowService implements OnModuleInit {
scheduleId,
updatedSchedular,
);
- // Optionally update cron job if runConfiguration or isActive changed
+ // Optionally update cron job if isActive changed
if (
updateScheduleDto.isActive !== undefined
) {
@@ -383,6 +383,16 @@ export class TestflowService implements OnModuleInit {
id,
user._id,
);
+
+ // Remove all associated cronjobs for this testflow
+ if (testflow?.schedules && Array.isArray(testflow.schedules)) {
+ for (const schedule of testflow.schedules) {
+ if (schedule?.id) {
+ await this.testflowSchedulerService.removeSchedulerJob(schedule.id);
+ }
+ }
+ }
+
const updateMessage = `"${testflow.name}" testflow is deleted from "${workspace.name}" workspace`;
const currentWorkspaceObject = new ObjectId(workspaceId);
const updateWorkspaceData: Partial = {
From 0f8f274b50a70d2c873e91b4f3611c26df828e3a Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 01:10:50 +0530
Subject: [PATCH 16/27] fix: testflow cron job configurations
---
.../workspace/services/testflow.service.ts | 57 +++++++++----------
1 file changed, 27 insertions(+), 30 deletions(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index ab8364058..c93ccd6c3 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -197,39 +197,36 @@ export class TestflowService implements OnModuleInit {
scheduleId,
updatedSchedular,
);
- // Optionally update cron job if isActive changed
- if (
- updateScheduleDto.isActive !== undefined
- ) {
- const schedular = await this.testflowRepository.getSchedularById(
- testflowId,
- scheduleId,
- );
- if (schedular) {
- // Remove old job
- await this.testflowSchedulerService.removeSchedulerJob(scheduleId);
- // If still active, re-add job
- if (schedular.isActive) {
- const runCycleConfig = this.buildRunCycleConfig(
- schedular.runConfiguration,
- );
- const cronExpression = schedular.cronExpression;
- await this.testflowSchedulerService.addSchedulerJob(
- runCycleConfig,
- this.getScheduledExecutionCallback(
- testflowId,
- schedular.environmentId,
- workspaceId,
- scheduleId,
- user,
- ),
- schedular.schedularName,
- cronExpression,
+
+ const schedular = await this.testflowRepository.getSchedularById(
+ testflowId,
+ scheduleId,
+ );
+ if (schedular) {
+ // Remove old job
+ await this.testflowSchedulerService.removeSchedulerJob(scheduleId);
+ // If still active, re-add job
+ if (schedular.isActive) {
+ const runCycleConfig = this.buildRunCycleConfig(
+ schedular.runConfiguration,
+ );
+ const cronExpression = schedular.cronExpression;
+ await this.testflowSchedulerService.addSchedulerJob(
+ runCycleConfig,
+ this.getScheduledExecutionCallback(
+ testflowId,
+ schedular.environmentId,
+ workspaceId,
scheduleId,
- );
- }
+ user,
+ ),
+ schedular.schedularName,
+ cronExpression,
+ scheduleId,
+ );
}
}
+
return result;
}
From 524e1d4af40d1a275d70cfa430aaab86188fe1fc Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 01:14:35 +0530
Subject: [PATCH 17/27] fix: testflow cron job configurations
---
src/modules/workspace/services/testflow.service.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index c93ccd6c3..b5833778a 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -170,10 +170,10 @@ export class TestflowService implements OnModuleInit {
);
environmentName = environmentData?.name || "";
}
- let cronExpression;
+
if(updateScheduleDto.runConfiguration){
const runCycleConfig = this.buildRunCycleConfig(updateScheduleDto.runConfiguration);
- cronExpression = this.generateCronExpression(runCycleConfig);
+ const cronExpression = this.generateCronExpression(runCycleConfig);
if (!cronExpression) {
updateScheduleDto.cronExpression = null;
}
From fe16c1dcd667b99296bfe62ab8291a1cdaaa249a Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 13:36:30 +0530
Subject: [PATCH 18/27] feat: add error handling for proxy fail
---
.../workspace/services/testflow-run.service.ts | 13 +++++++++++--
src/modules/workspace/services/testflow.service.ts | 6 +++++-
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/modules/workspace/services/testflow-run.service.ts b/src/modules/workspace/services/testflow-run.service.ts
index 4d9aaec50..fd337a1a2 100644
--- a/src/modules/workspace/services/testflow-run.service.ts
+++ b/src/modules/workspace/services/testflow-run.service.ts
@@ -113,11 +113,20 @@ export class TestflowRunService {
nodes:testflowDetails.nodes,
edges:testflowDetails.edges,
}
+ // throw new NotFoundException("Testflow not found");
// Return only history or any relevant part
return finalResult;
} catch (error: any) {
- console.error("Testflow proxy execution failed:", error.message || error);
- throw new Error(error?.message || "Testflow execution failed.");
+ return {
+ result:{
+ history: {
+ status: "error"
+ }
+ },
+ environmentName:environmentData?.name,
+ nodes:testflowDetails.nodes,
+ edges:testflowDetails.edges,
+ }
}
};
}
\ No newline at end of file
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index b5833778a..bbee19ba9 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -814,7 +814,11 @@ export class TestflowService implements OnModuleInit {
scheduleRunResult = "failed";
} else if (data.status === "success") {
scheduleRunResult = "success";
- } else {
+ }
+ else if (data.status === "error") {
+ scheduleRunResult = "error";
+ }
+ else {
scheduleRunResult = "partial";
}
const totalRequestCount = data.successRequests + data.failedRequests;
From 2af92914d3d2238d67c5cf83e0ea151d091218c0 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 13:43:53 +0530
Subject: [PATCH 19/27] feat: add error handling for proxy fail
---
.../services/testflow-run.service.ts | 65 +++++++++----------
1 file changed, 32 insertions(+), 33 deletions(-)
diff --git a/src/modules/workspace/services/testflow-run.service.ts b/src/modules/workspace/services/testflow-run.service.ts
index fd337a1a2..f9215310c 100644
--- a/src/modules/workspace/services/testflow-run.service.ts
+++ b/src/modules/workspace/services/testflow-run.service.ts
@@ -73,35 +73,35 @@ export class TestflowRunService {
testflowId:string,
user?: DecodedUserObject,
): Promise => {
- // Fetch testflow and environment data
- const testflowDetails = await this.testflowRepository.get(testflowId);
- const workspaceID = await this.workspaceReposistory.get(workspaceId);
- const globalEnvironment = workspaceID.environments[0];
- const globalEnvDetails = await this.environmentReposistory.get(
- globalEnvironment.id.toString(),
- );
- let environmentData;
- if (environmentId) {
- try{
- environmentData =
- await this.environmentReposistory.get(environmentId);
- }catch(err){}
- }
- const activeVariables = this.combineEnvironmentData(
- globalEnvDetails?.variable || [],
- environmentData?.variable || [],
- );
- // Build proxy URL
- const sparrowProxy = this.configService.get("sparrowProxy.baseUrl");
- const proxyUrl = `${sparrowProxy}/proxy/testflow/execute`;
- // Prepare request body for proxy API
- const body = {
- nodes: testflowDetails.nodes || [],
- variables: activeVariables || [],
- edges: testflowDetails.edges,
- userId: user?._id || new ObjectId("000000000000000000000000"),
- };
try {
+ // Fetch testflow and environment data
+ const testflowDetails = await this.testflowRepository.get(testflowId);
+ const workspaceID = await this.workspaceReposistory.get(workspaceId);
+ const globalEnvironment = workspaceID.environments[0];
+ const globalEnvDetails = await this.environmentReposistory.get(
+ globalEnvironment.id.toString(),
+ );
+ let environmentData;
+ if (environmentId) {
+ try{
+ environmentData =
+ await this.environmentReposistory.get(environmentId);
+ }catch(err){}
+ }
+ const activeVariables = this.combineEnvironmentData(
+ globalEnvDetails?.variable || [],
+ environmentData?.variable || [],
+ );
+ // Build proxy URL
+ const sparrowProxy = this.configService.get("sparrowProxy.baseUrl");
+ const proxyUrl = `${sparrowProxy}/proxy/testflow/execute`;
+ // Prepare request body for proxy API
+ const body = {
+ nodes: testflowDetails.nodes || [],
+ variables: activeVariables || [],
+ edges: testflowDetails.edges,
+ userId: user?._id || new ObjectId("000000000000000000000000"),
+ };
const response = await axios.post(proxyUrl, body, {
headers: {
"Content-Type": "application/json",
@@ -113,8 +113,7 @@ export class TestflowRunService {
nodes:testflowDetails.nodes,
edges:testflowDetails.edges,
}
- // throw new NotFoundException("Testflow not found");
- // Return only history or any relevant part
+ throw new Error("Test flow execution failed");
return finalResult;
} catch (error: any) {
return {
@@ -123,9 +122,9 @@ export class TestflowRunService {
status: "error"
}
},
- environmentName:environmentData?.name,
- nodes:testflowDetails.nodes,
- edges:testflowDetails.edges,
+ environmentName: "",
+ nodes: [],
+ edges: [],
}
}
};
From 97838cd5d5073b4223dcfd7260f3259b0756cd4a Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Thu, 9 Oct 2025 13:45:32 +0530
Subject: [PATCH 20/27] feat: add error handling for proxy fail
---
src/modules/workspace/services/testflow-run.service.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/modules/workspace/services/testflow-run.service.ts b/src/modules/workspace/services/testflow-run.service.ts
index f9215310c..0c4c45d2c 100644
--- a/src/modules/workspace/services/testflow-run.service.ts
+++ b/src/modules/workspace/services/testflow-run.service.ts
@@ -113,7 +113,6 @@ export class TestflowRunService {
nodes:testflowDetails.nodes,
edges:testflowDetails.edges,
}
- throw new Error("Test flow execution failed");
return finalResult;
} catch (error: any) {
return {
From 7ef80c86cfcde7313c810731f50f2c2947c7f8e9 Mon Sep 17 00:00:00 2001
From: "aakash.reddy@techdome.net.in"
Date: Thu, 9 Oct 2025 15:54:07 +0530
Subject: [PATCH 21/27] fix: added margin for text name
---
src/modules/views/testflowScheduleRunEmail.handlebars | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modules/views/testflowScheduleRunEmail.handlebars b/src/modules/views/testflowScheduleRunEmail.handlebars
index 49b508abe..4abe68f28 100644
--- a/src/modules/views/testflowScheduleRunEmail.handlebars
+++ b/src/modules/views/testflowScheduleRunEmail.handlebars
@@ -126,7 +126,7 @@
- Run Summary
+ Run Summary
|
From 16f0a275a9cf5a40d1662464f389d623ee0bd7e5 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Fri, 10 Oct 2025 03:09:50 +0530
Subject: [PATCH 22/27] feat: add hourly cron fix
---
src/modules/common/enum/testflow.enum.ts | 1 +
.../services/testflow-schedular.service.ts | 88 ++++++++++++-
.../workspace/services/testflow.service.ts | 121 +++++++++++++-----
3 files changed, 175 insertions(+), 35 deletions(-)
diff --git a/src/modules/common/enum/testflow.enum.ts b/src/modules/common/enum/testflow.enum.ts
index 8e6b02c13..96c5c9530 100644
--- a/src/modules/common/enum/testflow.enum.ts
+++ b/src/modules/common/enum/testflow.enum.ts
@@ -61,6 +61,7 @@ export interface DailyConfig {
export interface HourlyConfig {
type: RunCycleEnum.HOURLY;
+ executeAt: Date;
intervalHours: number;
startTime?: {
hour: number;
diff --git a/src/modules/workspace/services/testflow-schedular.service.ts b/src/modules/workspace/services/testflow-schedular.service.ts
index 8d5010363..ef6d28aa8 100644
--- a/src/modules/workspace/services/testflow-schedular.service.ts
+++ b/src/modules/workspace/services/testflow-schedular.service.ts
@@ -27,7 +27,86 @@ export class TestflowSchedulerService {
return false;
}
try {
- // Create cron job with UTC timezone
+ // For HOURLY rolling interval, create a one-time cron job for the next execution
+ if (runCycle.type === RunCycleEnum.HOURLY && runCycle.intervalHours) {
+
+ const scheduleNext = async (lastRun: Date) => {
+ // Calculate next run time
+ const nextRun = new Date(lastRun.getTime() + runCycle.intervalHours * 60 * 60 * 1000);
+ // Generate one-time cron expression for nextRun
+ const second = nextRun.getUTCSeconds();
+ const minute = nextRun.getUTCMinutes();
+ const hour = nextRun.getUTCHours();
+ const dayOfMonth = nextRun.getUTCDate();
+ const month = nextRun.getUTCMonth() + 1;
+ const oneTimeCron = `${second} ${minute} ${hour} ${dayOfMonth} ${month} *`;
+ const nextJobName = schedularId; // Always use scheduleId as job name
+ this.logger.log(`Next rolling interval job ${nextJobName} scheduled for ${nextRun.toISOString()} (cron: ${oneTimeCron})`);
+ // Remove any existing job with this name before adding
+ if (this.schedulerRegistry.doesExist("cron", nextJobName)) {
+ const oldJob = this.schedulerRegistry.getCronJob(nextJobName);
+ oldJob.stop();
+ this.schedulerRegistry.deleteCronJob(nextJobName);
+ }
+ const job = new CronJob(
+ oneTimeCron,
+ async () => {
+ this.logger.log(`Executing rolling interval job ${nextJobName} at ${new Date().toISOString()} (UTC)`);
+ if (runApis) {
+ try {
+ await runApis(schedularId);
+ } catch (error) {
+ this.logger.error(`Error executing job ${nextJobName}: ${error.message}`, error.stack);
+ }
+ }
+ job.stop();
+ this.schedulerRegistry.deleteCronJob(nextJobName);
+ this.logAllCronJobs();
+ // Schedule next job
+ await scheduleNext(nextRun);
+ this.logAllCronJobs();
+ },
+ null,
+ false,
+ timezone,
+ );
+ this.schedulerRegistry.addCronJob(nextJobName, job);
+ job.start();
+ };
+ // Use the provided initial cron expression for the first run
+ const nextJobName = schedularId;
+ // Remove any existing job with this name before adding
+ if (this.schedulerRegistry.doesExist("cron", nextJobName)) {
+ const oldJob = this.schedulerRegistry.getCronJob(nextJobName);
+ oldJob.stop();
+ this.schedulerRegistry.deleteCronJob(nextJobName);
+ }
+ const job = new CronJob(
+ cronExpression,
+ async () => {
+ this.logger.log(`Executing rolling interval job ${nextJobName} at ${new Date().toISOString()} (UTC)`);
+ if (runApis) {
+ try {
+ await runApis(schedularId);
+ } catch (error) {
+ this.logger.error(`Error executing job ${nextJobName}: ${error.message}`, error.stack);
+ }
+ }
+ job.stop();
+ this.schedulerRegistry.deleteCronJob(nextJobName);
+ // After first run, start rolling with scheduleNext
+ await scheduleNext(new Date());
+ },
+ null,
+ false,
+ timezone,
+ );
+ this.schedulerRegistry.addCronJob(nextJobName, job);
+ job.start();
+ this.logger.log(`Rolling interval scheduler job ${jobName} registered with interval: ${runCycle.intervalHours}h, timezone: ${timezone}`);
+ return true;
+ }
+ // Default: Create cron job with UTC timezone
const job = new CronJob(
cronExpression,
async () => {
@@ -119,6 +198,11 @@ export class TestflowSchedulerService {
}
private generateJobName(schedulerId: string): string {
- return `scheduler_${schedulerId}`;
+ return `${schedulerId}`;
+ }
+
+ public logAllCronJobs() {
+ const jobs = Array.from(this.schedulerRegistry.getCronJobs().keys());
+ console.log('Active cron jobs: ' + jobs.join(', '));
}
}
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index bbee19ba9..15466e2db 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -83,6 +83,38 @@ export class TestflowService implements OnModuleInit {
private readonly environmentReposistory: EnvironmentRepository,
) {}
+ async getNextFutureCronExpression(pastCron: string, intervalHours: number): Promise {
+ // Expecting cron in format: 's m h * * *'
+ const parts = pastCron.trim().split(/\s+/);
+ if (parts.length !== 6) return pastCron;
+
+ let second = parseInt(parts[0], 10);
+ let minute = parseInt(parts[1], 10);
+ let hour = parseInt(parts[2], 10);
+ let day = parseInt(parts[3], 10);
+ let month = parseInt(parts[4], 10) - 1;
+
+ // Start from the past time
+ let now = new Date();
+ let next = new Date(Date.UTC(
+ now.getUTCFullYear(),
+ month,
+ day,
+ hour,
+ minute,
+ second,
+ 0
+ ));
+
+ // If the past time is in the past, keep adding interval until it's in the future
+ while (next <= now) {
+ next.setUTCHours(next.getUTCHours() + intervalHours);
+ }
+
+ // Return new cron expression
+ return `${next.getUTCSeconds()} ${next.getUTCMinutes()} ${next.getUTCHours()} ${next.getUTCDate()} ${next.getUTCMonth() + 1} *`;
+ }
+
async onModuleInit() {
try {
this.logger.log("Bootstrapping schedulers from DB...");
@@ -95,24 +127,32 @@ export class TestflowService implements OnModuleInit {
if (!tf.schedules?.length) continue;
for (const schedule of tf.schedules) {
- const runCycleConfig = this.buildRunCycleConfig(
- schedule.runConfiguration,
- );
-
- if (schedule.isActive && schedule.cronExpression) {
- await this.testflowSchedulerService.addSchedulerJob(
- runCycleConfig,
- this.getScheduledExecutionCallback(
- tf._id.toString(),
- schedule.environmentId,
- tf.workspaceId,
- schedule.id,
- ),
- schedule.schedularName,
- schedule.cronExpression,
- schedule.id,
- "UTC",
+ try{
+ const runCycleConfig = this.buildRunCycleConfig(
+ schedule.runConfiguration,
);
+ if (schedule.isActive && schedule.cronExpression) {
+ let cronExpression = schedule.cronExpression;
+ if(schedule.runConfiguration.runCycle === RunCycleEnum.HOURLY){
+ const intervalHours = schedule.runConfiguration.intervalHours;
+ cronExpression = await this.getNextFutureCronExpression(cronExpression, intervalHours);
+ }
+ await this.testflowSchedulerService.addSchedulerJob(
+ runCycleConfig,
+ this.getScheduledExecutionCallback(
+ tf._id.toString(),
+ schedule.environmentId,
+ tf.workspaceId,
+ schedule.id,
+ ),
+ schedule.schedularName,
+ cronExpression,
+ schedule.id,
+ "UTC",
+ );
+ }
+ }catch(error){
+ this.logger.error("Error adding scheduler job:", error);
}
}
}
@@ -180,6 +220,17 @@ export class TestflowService implements OnModuleInit {
else{
updateScheduleDto.cronExpression = cronExpression;
}
+ }else{
+ if(existingSchedular.runConfiguration.runCycle === RunCycleEnum.HOURLY){
+ const runCycleConfig = this.buildRunCycleConfig(existingSchedular.runConfiguration);
+ const cronExpression = this.generateCronExpression(runCycleConfig);
+ if (!cronExpression) {
+ updateScheduleDto.cronExpression = null;
+ }
+ else{
+ updateScheduleDto.cronExpression = cronExpression;
+ }
+ }
}
// Merge update fields, ensure id is present
@@ -227,7 +278,7 @@ export class TestflowService implements OnModuleInit {
}
}
- return result;
+ return result;
}
/**
@@ -624,6 +675,7 @@ export class TestflowService implements OnModuleInit {
}
return {
type: RunCycleEnum.HOURLY,
+ executeAt: new Date(Date.now() + 1 * 60 * 1000),
intervalHours: runConfig.intervalHours,
startTime: runConfig.time
? this.parseTime(runConfig.time)
@@ -710,17 +762,17 @@ export class TestflowService implements OnModuleInit {
}
private generateHourlyCronExpression(config: HourlyConfig): string {
- const { intervalHours, startTime } = config;
- if (startTime) {
- const { hour, minute, second = 0 } = startTime;
- return `${second} ${minute} ${hour}-23/${intervalHours} * * *`;
- } else {
- const now = new Date();
- const utcHour = now.getUTCHours();
- const utcMinute = now.getUTCMinutes();
- const utcSecond = now.getUTCSeconds();
- return `${utcSecond} ${utcMinute} ${utcHour}-23/${intervalHours} * * *`;
+ const executeAt = config.executeAt;
+ const now = new Date();
+ if (executeAt <= now) {
+ return null;
}
+ const second = executeAt.getUTCSeconds();
+ const minute = executeAt.getUTCMinutes();
+ const hour = executeAt.getUTCHours();
+ const dayOfMonth = executeAt.getUTCDate();
+ const month = executeAt.getUTCMonth() + 1;
+ return `${second} ${minute} ${hour} ${dayOfMonth} ${month} *`;
}
private generateWeeklyCronExpression(config: WeeklyConfig): string {
@@ -790,6 +842,7 @@ export class TestflowService implements OnModuleInit {
nodes: response.nodes,
edges: response.edges,
...response.result.history,
+ status: response?.result?.history?.status || "error",
};
//Save execution result in DB
await this.testflowRepository.editSchedularExecution(
@@ -808,14 +861,16 @@ export class TestflowService implements OnModuleInit {
false,
);
}
- const data = response.result.history;
+ const data = response?.result?.history;
let scheduleRunResult;
- if (data.status === "fail" && data.successRequests < 1) {
+ if(!response?.status){
+ scheduleRunResult = "error";
+ }
+ else if (data?.status === "fail" && data?.successRequests < 1) {
scheduleRunResult = "failed";
- } else if (data.status === "success") {
+ } else if (data?.status === "success") {
scheduleRunResult = "success";
- }
- else if (data.status === "error") {
+ } else if (data?.status === "error") {
scheduleRunResult = "error";
}
else {
From 409ca1052156c9d6482183126b696d14a14c43ab Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Fri, 10 Oct 2025 11:10:28 +0530
Subject: [PATCH 23/27] feat: updated hourly cron expression
---
.../repositories/testflow.repository.ts | 31 +++++++++++++++++++
.../services/testflow-schedular.service.ts | 5 ++-
.../workspace/services/testflow.service.ts | 15 +++++++++
3 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/src/modules/workspace/repositories/testflow.repository.ts b/src/modules/workspace/repositories/testflow.repository.ts
index 7243b9054..c356236aa 100644
--- a/src/modules/workspace/repositories/testflow.repository.ts
+++ b/src/modules/workspace/repositories/testflow.repository.ts
@@ -264,6 +264,37 @@ export class TestflowRepository {
);
}
+ /**
+ * Edit a schedular execution (run history item) in a testflow's schedule.
+ * @param {string} testflowId - The testflow document ID.
+ * @param {string} schedularId - The schedule ID.
+ * @param {Partial} updatedSchedular - The updated fields for the schedule item.
+ * @returns {Promise} - The result of the update operation.
+ */
+ async editSchedular(
+ testflowId: string,
+ schedularId: string,
+ updatedSchedular: Partial,
+ ): Promise {
+ if (!testflowId || !schedularId) {
+ throw new Error("testflowId and schedularId are required");
+ }
+ // Build the update object for only the provided fields
+ const setObj: Record = {};
+ for (const [key, value] of Object.entries(updatedSchedular)) {
+ setObj[`schedules.$[elem].${key}`] = value;
+ }
+ return this.db.collection(Collections.TESTFLOW).updateOne(
+ { _id: new ObjectId(testflowId) },
+ { $set: setObj },
+ {
+ arrayFilters: [
+ { "elem.id": schedularId },
+ ],
+ },
+ );
+ }
+
async updateSchedularStatus(
testflowId: string,
diff --git a/src/modules/workspace/services/testflow-schedular.service.ts b/src/modules/workspace/services/testflow-schedular.service.ts
index ef6d28aa8..1a502efd1 100644
--- a/src/modules/workspace/services/testflow-schedular.service.ts
+++ b/src/modules/workspace/services/testflow-schedular.service.ts
@@ -3,12 +3,13 @@ import { SchedulerRegistry } from "@nestjs/schedule";
import { CronJob } from "cron";
import { RunCycleEnum } from "@src/modules/common/enum/testflow.enum";
import { RunCycleConfig } from "@src/modules/common/enum/testflow.enum";
+import { TestflowRepository } from "../repositories/testflow.repository";
@Injectable()
export class TestflowSchedulerService {
private readonly logger = new Logger(TestflowSchedulerService.name);
- constructor(private schedulerRegistry: SchedulerRegistry) {}
+ constructor(private schedulerRegistry: SchedulerRegistry, private testflowRepository: TestflowRepository) {}
/**
* Add a cron job
@@ -16,6 +17,7 @@ export class TestflowSchedulerService {
async addSchedulerJob(
runCycle: RunCycleConfig,
runApis: (schedularId: string) => Promise,
+ afterJobCreate: (cronExpression: string) => void,
jobName: string,
cronExpression: string,
schedularId: string,
@@ -70,6 +72,7 @@ export class TestflowSchedulerService {
false,
timezone,
);
+ afterJobCreate(oneTimeCron);
this.schedulerRegistry.addCronJob(nextJobName, job);
job.start();
};
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index 15466e2db..be94eb2f5 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -145,6 +145,11 @@ export class TestflowService implements OnModuleInit {
tf.workspaceId,
schedule.id,
),
+ (_cronExpression: string)=>{
+ this.testflowRepository.editSchedular(tf._id.toString(), schedule.id, {
+ cronExpression: _cronExpression,
+ });
+ },
schedule.schedularName,
cronExpression,
schedule.id,
@@ -271,6 +276,11 @@ export class TestflowService implements OnModuleInit {
scheduleId,
user,
),
+ (_cronExpression: string)=>{
+ this.testflowRepository.editSchedular(testflowId, scheduleId, {
+ cronExpression: _cronExpression,
+ });
+ },
schedular.schedularName,
cronExpression,
scheduleId,
@@ -624,6 +634,11 @@ export class TestflowService implements OnModuleInit {
schedulerId,
user,
),
+ (_cronExpression: string)=>{
+ this.testflowRepository.editSchedular(schedularData.testflowId, schedulerId, {
+ cronExpression: _cronExpression,
+ });
+ },
jobName,
cronExpression,
schedulerId,
From f38693b2f405eb2fbc3fa57eebae9aec78569790 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Fri, 10 Oct 2025 11:43:12 +0530
Subject: [PATCH 24/27] feat: updated hourly cron expression
---
src/modules/workspace/services/testflow.service.ts | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index be94eb2f5..b61f10890 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -47,6 +47,7 @@ import { v4 as uuidv4 } from "uuid";
import { TestflowSchedulerService } from "./testflow-schedular.service";
import {
DailyConfig,
+ DayOfWeek,
EmailData,
HourlyConfig,
NotificationReceiveType,
@@ -793,7 +794,17 @@ export class TestflowService implements OnModuleInit {
private generateWeeklyCronExpression(config: WeeklyConfig): string {
const { days, time } = config;
const { hour, minute, second = 0 } = time;
- return `${second} ${minute} ${hour} * * ${days.join(",")}`;
+ const validDays = days.filter((day) => {
+ return day >= DayOfWeek.SUNDAY && day <= DayOfWeek.SATURDAY;
+ });
+ if (validDays.length === 0) {
+ throw new BadRequestException(
+ "Invalid days specified. Days must be valid DayOfWeek values (0=Sunday, 1=Monday, ..., 6=Saturday)",
+ );
+ }
+ const sortedDays = validDays.sort((a, b) => a - b);
+ const daysString = sortedDays.join(",");
+ return `${second} ${minute} ${hour} * * ${daysString}`;
}
public getScheduledExecutionCallback(
From e8702c3377397ed5c885cdc4ab582ec213fdfd03 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Fri, 10 Oct 2025 11:48:13 +0530
Subject: [PATCH 25/27] feat: updated hourly cron expression
---
.../services/testflow-schedular.service.ts | 20 +------------------
1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/src/modules/workspace/services/testflow-schedular.service.ts b/src/modules/workspace/services/testflow-schedular.service.ts
index 1a502efd1..fb2fb846d 100644
--- a/src/modules/workspace/services/testflow-schedular.service.ts
+++ b/src/modules/workspace/services/testflow-schedular.service.ts
@@ -63,10 +63,8 @@ export class TestflowSchedulerService {
}
job.stop();
this.schedulerRegistry.deleteCronJob(nextJobName);
- this.logAllCronJobs();
// Schedule next job
await scheduleNext(nextRun);
- this.logAllCronJobs();
},
null,
false,
@@ -87,7 +85,6 @@ export class TestflowSchedulerService {
const job = new CronJob(
cronExpression,
async () => {
- this.logger.log(`Executing rolling interval job ${nextJobName} at ${new Date().toISOString()} (UTC)`);
if (runApis) {
try {
await runApis(schedularId);
@@ -106,16 +103,12 @@ export class TestflowSchedulerService {
);
this.schedulerRegistry.addCronJob(nextJobName, job);
job.start();
- this.logger.log(`Rolling interval scheduler job ${jobName} registered with interval: ${runCycle.intervalHours}h, timezone: ${timezone}`);
return true;
}
// Default: Create cron job with UTC timezone
const job = new CronJob(
cronExpression,
async () => {
- this.logger.log(
- `Executing job ${jobName} at ${new Date().toISOString()} (UTC)`
- );
if (runApis) {
try {
await runApis(schedularId);
@@ -130,7 +123,6 @@ export class TestflowSchedulerService {
if (runCycle.type === RunCycleEnum.ONCE) {
job.stop();
this.schedulerRegistry.deleteCronJob(jobName);
- this.logger.log(`One-time scheduler ${jobName} completed and removed`);
}
},
null, // onComplete callback
@@ -139,10 +131,6 @@ export class TestflowSchedulerService {
);
this.schedulerRegistry.addCronJob(jobName, job);
job.start();
- this.logger.log(
- `Scheduler job ${jobName} registered with cycle: ${runCycle.type}, ` +
- `timezone: ${timezone}, cron: ${cronExpression}`
- );
return true;
} catch (error) {
this.logger.error(
@@ -164,9 +152,6 @@ export class TestflowSchedulerService {
job.stop();
this.schedulerRegistry.deleteCronJob(jobName);
}
- this.logger.log(
- `Scheduler job ${jobName} (ID: ${schedulerId}) removed from DB + registry`,
- );
return true;
} catch (error) {
this.logger.warn(
@@ -184,7 +169,6 @@ export class TestflowSchedulerService {
if (this.schedulerRegistry.doesExist("cron", jobName)) {
const job = this.schedulerRegistry.getCronJob(jobName);
job.stop();
- this.logger.log(`Scheduler job ${jobName} paused`);
}
}
@@ -196,7 +180,6 @@ export class TestflowSchedulerService {
if (this.schedulerRegistry.doesExist("cron", jobName)) {
const job = this.schedulerRegistry.getCronJob(jobName);
job.start();
- this.logger.log(`Scheduler job ${jobName} resumed`);
}
}
@@ -205,7 +188,6 @@ export class TestflowSchedulerService {
}
public logAllCronJobs() {
- const jobs = Array.from(this.schedulerRegistry.getCronJobs().keys());
- console.log('Active cron jobs: ' + jobs.join(', '));
+ return Array.from(this.schedulerRegistry.getCronJobs().keys());
}
}
From beb6eda292e4be9439a17e86bcad5923301aaaa9 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Fri, 10 Oct 2025 11:48:54 +0530
Subject: [PATCH 26/27] feat: updated hourly cron expression
---
src/modules/workspace/services/testflow-schedular.service.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modules/workspace/services/testflow-schedular.service.ts b/src/modules/workspace/services/testflow-schedular.service.ts
index fb2fb846d..292d98a5f 100644
--- a/src/modules/workspace/services/testflow-schedular.service.ts
+++ b/src/modules/workspace/services/testflow-schedular.service.ts
@@ -9,7 +9,7 @@ import { TestflowRepository } from "../repositories/testflow.repository";
export class TestflowSchedulerService {
private readonly logger = new Logger(TestflowSchedulerService.name);
- constructor(private schedulerRegistry: SchedulerRegistry, private testflowRepository: TestflowRepository) {}
+ constructor(private schedulerRegistry: SchedulerRegistry ) {}
/**
* Add a cron job
From 353fc7ecb8d8f329e8fe64e3a08e9050d433b564 Mon Sep 17 00:00:00 2001
From: Md Asif Raza
Date: Fri, 10 Oct 2025 14:57:36 +0530
Subject: [PATCH 27/27] feat: updated hourly cron expression
---
.../workspace/services/testflow-schedular.service.ts | 12 +++++-------
src/modules/workspace/services/testflow.service.ts | 4 +---
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/src/modules/workspace/services/testflow-schedular.service.ts b/src/modules/workspace/services/testflow-schedular.service.ts
index 292d98a5f..6fc81fbd2 100644
--- a/src/modules/workspace/services/testflow-schedular.service.ts
+++ b/src/modules/workspace/services/testflow-schedular.service.ts
@@ -18,14 +18,12 @@ export class TestflowSchedulerService {
runCycle: RunCycleConfig,
runApis: (schedularId: string) => Promise,
afterJobCreate: (cronExpression: string) => void,
- jobName: string,
cronExpression: string,
schedularId: string,
timezone: string = "UTC", // Always use UTC by default
): Promise {
if (!cronExpression) {
- console.error(`Invalid run cycle configuration for job ${jobName}`);
- this.logger.log(`Invalid run cycle configuration for job ${jobName}`);
+ this.logger.log(`Invalid run cycle configuration for job ${schedularId}`);
return false;
}
try {
@@ -114,7 +112,7 @@ export class TestflowSchedulerService {
await runApis(schedularId);
} catch (error) {
this.logger.error(
- `Error executing job ${jobName}: ${error.message}`,
+ `Error executing job ${schedularId}: ${error.message}`,
error.stack
);
}
@@ -122,19 +120,19 @@ export class TestflowSchedulerService {
// Handle one-time jobs
if (runCycle.type === RunCycleEnum.ONCE) {
job.stop();
- this.schedulerRegistry.deleteCronJob(jobName);
+ this.schedulerRegistry.deleteCronJob(schedularId);
}
},
null, // onComplete callback
false, // start - we'll call start() manually
timezone, // Set timezone to UTC
);
- this.schedulerRegistry.addCronJob(jobName, job);
+ this.schedulerRegistry.addCronJob(schedularId, job);
job.start();
return true;
} catch (error) {
this.logger.error(
- `Failed to create scheduler job ${jobName}: ${error.message}`,
+ `Failed to create scheduler job ${schedularId}: ${error.message}`,
error.stack
);
return false;
diff --git a/src/modules/workspace/services/testflow.service.ts b/src/modules/workspace/services/testflow.service.ts
index b61f10890..271f232f9 100644
--- a/src/modules/workspace/services/testflow.service.ts
+++ b/src/modules/workspace/services/testflow.service.ts
@@ -151,7 +151,6 @@ export class TestflowService implements OnModuleInit {
cronExpression: _cronExpression,
});
},
- schedule.schedularName,
cronExpression,
schedule.id,
"UTC",
@@ -282,9 +281,9 @@ export class TestflowService implements OnModuleInit {
cronExpression: _cronExpression,
});
},
- schedular.schedularName,
cronExpression,
scheduleId,
+ "UTC",
);
}
}
@@ -640,7 +639,6 @@ export class TestflowService implements OnModuleInit {
cronExpression: _cronExpression,
});
},
- jobName,
cronExpression,
schedulerId,
"UTC",