Skip to content

Commit 1fc0930

Browse files
add: tests
1 parent f669133 commit 1fc0930

File tree

9 files changed

+122
-124
lines changed

9 files changed

+122
-124
lines changed

config/custom-environment-variables.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ module.exports = {
6161
discordBot: {
6262
baseUrl: "DISCORD_BASE_URL",
6363
},
64+
featureFlag: {
65+
baseUrl: "FEATURE_FLAG_SERVICE_BASE_URL",
66+
apiKey: "FEATURE_FLAG_SERVICE_API_KEY",
67+
},
6468
},
6569

6670
emailServiceConfig: {

config/default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ module.exports = {
7272
},
7373
featureFlag: {
7474
baseUrl: "<FEATURE_FLAG_SERVICE_BASE_URL>",
75+
apiKey: "<FEATURE_FLAG_SERVICE_API_KEY>",
7576
},
7677
},
7778

controllers/featureFlags.ts

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ export const getAllFeatureFlags = async (req: CustomRequest, res: CustomResponse
66
try {
77
const serviceResponse = await featureFlagService.getAllFeatureFlags();
88

9-
if (!serviceResponse) {
10-
return res.status(500).json({
11-
error: "Failed to fetch feature flags"
9+
if (serviceResponse.error) {
10+
return res.status(serviceResponse.status).json({
11+
error: serviceResponse.error.message
1212
});
1313
}
1414

@@ -28,18 +28,22 @@ export const getFeatureFlagById = async (req: CustomRequest, res: CustomResponse
2828
try {
2929
const { flagId } = req.params;
3030
const serviceResponse = await featureFlagService.getFeatureFlagById(flagId);
31-
32-
if (!serviceResponse) {
33-
return res.status(404).json({ error: "Feature flag not found" });
34-
}
3531

36-
return res.status(200).json({
37-
message: "Feature flag retrieved successfully",
38-
data: serviceResponse
39-
});
32+
if (serviceResponse.data) {
33+
return res.status(serviceResponse.status).json({
34+
message: "Feature flag retrieved successfully",
35+
data: serviceResponse.data,
36+
});
37+
} else if (serviceResponse.error) {
38+
return res.status(serviceResponse.status).json({
39+
error: serviceResponse.error.message || "Internal server error",
40+
});
41+
} else {
42+
return res.status(500).json({ error: "Unknown error occurred" });
43+
}
4044
} catch (err) {
41-
logger.error(`Error in fetching feature flag: ${err}`);
42-
return res.boom.badImplementation('Internal server error');
45+
logger.error(`Unexpected error in fetching feature flag: ${err}`);
46+
return res.boom.badImplementation("Internal server error");
4347
}
4448
};
4549

@@ -67,31 +71,3 @@ export const createFeatureFlag = async (req: CustomRequest, res: CustomResponse)
6771
return res.boom.badImplementation("Internal server error.");
6872
}
6973
};
70-
71-
72-
export const updateFeatureFlag = async (req: CustomRequest, res: CustomResponse) => {
73-
try {
74-
const { flagId } = req.params;
75-
const updateData: UpdateFeatureFlagRequestBody = {
76-
Status: req.body.Status,
77-
UserId: req.body.UserId,
78-
};
79-
80-
const serviceResponse = await featureFlagService.updateFeatureFlag(flagId, updateData);
81-
82-
if (serviceResponse.status >= 400) {
83-
return res.status(serviceResponse.status).json({
84-
error: serviceResponse.error?.message || "An error occurred while updating the feature flag",
85-
});
86-
}
87-
88-
return res.status(serviceResponse.status).json({
89-
message: "Feature flag updated successfully",
90-
data: serviceResponse.data,
91-
});
92-
} catch (err) {
93-
logger.error(`Error in updating feature flag: ${err}`);
94-
return res.boom.badImplementation('Internal server error');
95-
}
96-
};
97-

middlewares/validators/featureFlag.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,6 @@ import Joi from 'joi';
22
import { Request, Response, NextFunction } from 'express';
33
import { CustomResponse } from '../../types/global';
44

5-
const updateFeatureFlagSchema = Joi.object({
6-
Status: Joi.string()
7-
.valid('ENABLED', 'DISABLED')
8-
.required()
9-
.messages({
10-
'string.valid': 'Allowed values of Status are ENABLED, DISABLED',
11-
'any.required': 'Status is required'
12-
}),
13-
UserId: Joi.string()
14-
.required()
15-
.messages({
16-
'any.required': 'UserId is required'
17-
})
18-
});
19-
20-
export const validateUpdateFeatureFlag = async (req: Request, res: CustomResponse, next: NextFunction) => {
21-
try {
22-
await updateFeatureFlagSchema.validateAsync(req.body);
23-
next();
24-
} catch (error) {
25-
logger.error(`Error validating update feature flag payload: ${error.message}`);
26-
res.boom.badRequest(error.message);
27-
}
28-
};
29-
305
const createFeatureFlagSchema = Joi.object({
316
Name: Joi.string()
327
.required()

routes/featureFlag.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import express from "express";
22
const router = express.Router();
33
import authenticate from "../middlewares/authenticate";
44
const authorizeRoles = require("../middlewares/authorizeRoles");
5-
import { createFeatureFlag, getAllFeatureFlags, getFeatureFlagById, updateFeatureFlag } from "../controllers/featureFlags";
5+
import { createFeatureFlag, getAllFeatureFlags, getFeatureFlagById } from "../controllers/featureFlags";
66
const { SUPERUSER } = require("../constants/roles");
7-
import { validateUpdateFeatureFlag, validateCreateFeatureFlag } from '../middlewares/validators/featureFlag';
7+
import { validateCreateFeatureFlag } from '../middlewares/validators/featureFlag';
88

99
router.get("/getAllFeatureFlags", authenticate, getAllFeatureFlags);
1010
router.get("/getFeatureFlag/:flagId", authenticate, getFeatureFlagById);
1111
router.post('/createFeatureFlag', authenticate, authorizeRoles([SUPERUSER]), validateCreateFeatureFlag, createFeatureFlag);
12-
router.patch('/updateFeatureFlag/:flagId', authenticate, authorizeRoles([SUPERUSER]), validateUpdateFeatureFlag, updateFeatureFlag);
1312

1413
module.exports = router;

services/featureFlagService.ts

Lines changed: 34 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
import config from "config";
2-
import { FeatureFlag, FeatureFlagResponse, FeatureFlagService, UpdateFeatureFlagRequestBody } from "../types/featureFlags";
2+
import { FeatureFlag, FeatureFlagResponse, FeatureFlagService } from "../types/featureFlags";
33

44
const FEATURE_FLAG_BASE_URL = config.get<string>("services.featureFlag.baseUrl");
55
const FEATURE_FLAG_API_KEY = config.get<string>("services.featureFlag.apiKey");
66

7-
const generateHeaders = (): HeadersInit => {
8-
return {
7+
const defaultHeaders: HeadersInit = {
98
"Content-Type": "application/json",
10-
"x-api-key": `${FEATURE_FLAG_API_KEY}`,
11-
};
9+
"x-api-key": FEATURE_FLAG_API_KEY,
1210
};
1311

1412
const getAllFeatureFlags = async (): Promise<FeatureFlagResponse> => {
1513
try {
1614
const response = await fetch(`${FEATURE_FLAG_BASE_URL}/feature-flags`, {
1715
method: "GET",
18-
headers: generateHeaders(),
16+
headers: defaultHeaders,
1917
});
2018

2119
if (!response.ok) {
@@ -35,7 +33,7 @@ const createFeatureFlag = async (flagData: any): Promise<{ status: number; data?
3533
try {
3634
const response = await fetch(`${FEATURE_FLAG_BASE_URL}/feature-flags`, {
3735
method: "POST",
38-
headers: generateHeaders(),
36+
headers: defaultHeaders,
3937
body: JSON.stringify(flagData),
4038
});
4139
const status = response.status;
@@ -57,66 +55,48 @@ const createFeatureFlag = async (flagData: any): Promise<{ status: number; data?
5755
}
5856
};
5957

60-
const updateFeatureFlag = async (
61-
flagId: string,
62-
updateData: UpdateFeatureFlagRequestBody
63-
): Promise<FeatureFlagResponse> => {
58+
const getFeatureFlagById = async (flagId: string): Promise<{ status: number; data?: any; error?: any }> => {
6459
try {
6560
const response = await fetch(`${FEATURE_FLAG_BASE_URL}/feature-flags/${flagId}`, {
66-
method: "PATCH",
67-
headers: generateHeaders(),
68-
body: JSON.stringify(updateData),
61+
method: "GET",
62+
headers: defaultHeaders,
6963
});
64+
const status = response.status;
65+
const responseText = await response.text();
7066

71-
if (!response.ok) {
72-
const error = await response.json();
73-
return {
74-
status: response.status,
75-
error: error || { message: `HTTP error! status: ${response.status}` }
76-
};
67+
if (response.ok) {
68+
try {
69+
const parsedData = JSON.parse(responseText);
70+
return {
71+
status,
72+
data: parsedData
73+
};
74+
} catch (parseError) {
75+
logger.error("Error parsing success response:", parseError);
76+
return {
77+
status: 500,
78+
error: { message: "Error parsing service response" }
79+
};
80+
}
7781
}
78-
79-
const data = await response.json();
80-
return { status: response.status, data };
82+
return {
83+
status,
84+
error: { message: responseText }
85+
};
8186
} catch (err) {
82-
logger.error("Error in updating feature flag", err);
83-
return {
84-
status: 500,
85-
error: { message: "Internal error while connecting to the feature flag service" }
87+
logger.error("Error in getFeatureFlagById service:", err);
88+
return {
89+
status: 500,
90+
error: {
91+
message: err instanceof Error ? err.message : "Internal error while connecting to the feature flag service"
92+
}
8693
};
8794
}
8895
};
8996

90-
const getFeatureFlagById = async (flagId: string): Promise<FeatureFlagResponse> => {
91-
try {
92-
const response = await fetch(`${FEATURE_FLAG_BASE_URL}/feature-flags/${flagId}`, {
93-
method: "GET",
94-
headers: generateHeaders(),
95-
});
96-
97-
if (!response.ok) {
98-
logger.error(`Failed to fetch feature flag. Status: ${response.status}`);
99-
return {
100-
status: response.status,
101-
error: { message: `HTTP error! status: ${response.status}` }
102-
};
103-
}
104-
105-
const data = await response.json();
106-
return { status: response.status, data };
107-
} catch (err) {
108-
logger.error("Error in fetching feature flag by ID", err);
109-
return {
110-
status: 500,
111-
error: { message: "Internal error while connecting to the feature flag service" }
112-
};
113-
}
114-
};
115-
11697
const featureFlagService: FeatureFlagService = {
11798
getAllFeatureFlags,
11899
createFeatureFlag,
119-
updateFeatureFlag,
120100
getFeatureFlagById,
121101
};
122102

test/config/test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ module.exports = {
7272
secretKey: "<goalSecretKey>",
7373
cookieName: `goals-session-test`,
7474
},
75+
featureFlag: {
76+
baseUrl: "FEATURE_FLAG_SERVICE_BASE_URL",
77+
apiKey: "FEATURE_FLAG_SERVICE_API_KEY",
78+
},
7579
},
7680

7781
cors: {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
interface FeatureFlag {
2+
id: string;
3+
name: string;
4+
description: string;
5+
status: string;
6+
createdAt: number;
7+
createdBy: string;
8+
updatedAt: number;
9+
updatedBy: string;
10+
}
11+
12+
const featureFlagData = [
13+
{
14+
id: "60b00c3a-3928-4f0c-8581-dfce71aa8605",
15+
name: "feature-flag",
16+
description: "It is a demo project",
17+
status: "ENABLED",
18+
createdAt: 1718139019,
19+
createdBy: "sduhasdjasdas",
20+
updatedAt: 1718139019,
21+
updatedBy: "sduhasdjasdas"
22+
},
23+
{
24+
id: "flag-1",
25+
name: "feature-flag-1",
26+
description: "First demo flag",
27+
status: "ENABLED",
28+
createdAt: 1718139019,
29+
createdBy: "user1",
30+
updatedAt: 1718139019,
31+
updatedBy: "user1"
32+
},
33+
{
34+
id: "flag-2",
35+
name: "feature-flag-2",
36+
description: "Second demo flag",
37+
status: "DISABLED",
38+
createdAt: 1718139019,
39+
createdBy: "user2",
40+
updatedAt: 1718139019,
41+
updatedBy: "user2"
42+
}
43+
];
44+
45+
const newFeatureFlag = {
46+
Name: "Demo-feature",
47+
Description: "Description for demo feature",
48+
UserId: "superUserId"
49+
};
50+
51+
const invalidFeatureFlag = {
52+
Description: "Missing required name",
53+
UserId: "superUserId"
54+
};
55+
56+
module.exports = {
57+
featureFlagData,
58+
newFeatureFlag,
59+
invalidFeatureFlag
60+
};

types/featureFlags.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export interface FeatureFlagResponse {
2020
export interface FeatureFlagService {
2121
getAllFeatureFlags(): Promise<FeatureFlagResponse>;
2222
createFeatureFlag(flagData: Partial<FeatureFlag>): Promise<FeatureFlagResponse>;
23-
updateFeatureFlag(flagId: string, updateData: UpdateFeatureFlagRequestBody): Promise<FeatureFlagResponse>;
2423
getFeatureFlagById: (flagId: string) => Promise<FeatureFlagResponse>;
2524
}
2625

0 commit comments

Comments
 (0)