Skip to content

Commit 0360a1b

Browse files
committed
Misc PR adjustments; reorg + amend implementation; add associated unit tests.
1 parent 5055f9d commit 0360a1b

File tree

7 files changed

+200
-62
lines changed

7 files changed

+200
-62
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Request, Response } from "express";
2+
import * as restrictionService from "../../services/admin/restriction.service";
3+
4+
import logger from "../../config/loggingConfig";
5+
6+
export const checkSanctionStatus = async (req: Request, res: Response) => {
7+
const { latitude, longitude } = req.body;
8+
9+
if (typeof latitude !== 'number' || typeof longitude !== 'number') {
10+
logger.error(`Invalid coordinates provided as ${latitude}, ${longitude}`);
11+
return res.status(400).json({ error: 'Unexpected coordinates provided' });
12+
}
13+
14+
try {
15+
const result = await restrictionService.validateSellerLocation(longitude, latitude);
16+
const isSanctioned = !!result;
17+
logger.info(`User at [${latitude}, ${longitude}] is ${isSanctioned ? '' : 'not '} in a sanctioned zone.`);
18+
return res.status(200).json({
19+
message: `Sell center is set within a ${isSanctioned ? 'sanctioned' : 'unsanctioned' } zone`,
20+
isSanctioned
21+
});
22+
} catch (error) {
23+
logger.error('Failed to get sanctioned status:', error);
24+
return res.status(500).json({
25+
message: 'An error occurred while checking sanction status; please try again later'
26+
});
27+
}
28+
};

src/controllers/sanctionedRegionsController.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,50 @@
11
import {Router} from "express";
2-
import { verifyAdminToken } from "../middlewares/verifyToken";
3-
import * as sanctionedRegionsController from "../controllers/sanctionedRegionsController"
2+
import * as restrictionController from "../controllers/admin/restrictionController";
43

5-
const sanctionedRegionsRoutes = Router();
4+
const restrictionRoutes = Router();
65

76
/**
87
* @swagger
9-
* /api/v1/reports/check-in-sanctioned-region:
8+
* /api/v1/restrictions/check-sanction-status:
109
* post:
1110
* tags:
12-
* - Report
13-
* summary: Check if a point is within a sanctioned region
11+
* - Restriction
12+
* summary: Check if a [latitude, longitude] coordinate is within sanctioned boundaries.
1413
* requestBody:
1514
* required: true
1615
* content:
1716
* application/json:
1817
* schema:
1918
* type: object
20-
* required:
21-
* - latitude
22-
* - longitude
2319
* properties:
2420
* latitude:
2521
* type: number
2622
* example: -1.94995
2723
* longitude:
2824
* type: number
2925
* example: 30.0588
26+
* required:
27+
* - latitude
28+
* - longitude
3029
* responses:
3130
* 200:
32-
* description: Indicates whether the point is in a sanctioned region
31+
* description: Successful response
3332
* content:
3433
* application/json:
3534
* schema:
3635
* type: object
3736
* properties:
38-
* isRestricted:
37+
* isSanctioned:
3938
* type: boolean
4039
* example: true
4140
* 400:
42-
* description: Invalid coordinates provided
41+
* description: Bad request
4342
* 500:
4443
* description: Internal server error
4544
*/
46-
sanctionedRegionsRoutes.post(
47-
"/check-in-sanctioned-region",
48-
// verifyAdminToken,
49-
sanctionedRegionsController.checkIfPointInRegion
45+
restrictionRoutes.post(
46+
"/check-sanction-status",
47+
restrictionController.checkSanctionStatus
5048
);
5149

52-
export default sanctionedRegionsRoutes;
50+
export default restrictionRoutes;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import SanctionedRegion from "../../models/misc/SanctionedRegion";
2+
3+
export const validateSellerLocation = async (longitude: number, latitude: number) => {
4+
const sellCenter = {
5+
type: 'Point' as const,
6+
coordinates: [longitude, latitude],
7+
};
8+
9+
const isSanctionedLocation = await SanctionedRegion.findOne({
10+
boundary: {
11+
$geoIntersects: {
12+
$geometry: sellCenter
13+
}
14+
}
15+
}).exec();
16+
17+
return isSanctionedLocation;
18+
};

src/utils/app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import reviewFeedbackRoutes from "../routes/reviewFeedback.routes";
1616
import mapCenterRoutes from "../routes/mapCenter.routes";
1717
import reportRoutes from "../routes/report.routes";
1818
import toggleRoutes from "../routes/toggle.routes";
19-
import sanctionedRegionsRoutes from "../routes/sanctionedRegions.routes";
19+
import restrictionRoutes from "../routes/restriction.routes";
2020

2121
dotenv.config();
2222

@@ -46,7 +46,7 @@ app.use("/api/v1/review-feedback", reviewFeedbackRoutes);
4646
app.use("/api/v1/map-center", mapCenterRoutes);
4747
app.use("/api/v1/reports", reportRoutes);
4848
app.use("/api/v1/toggles", toggleRoutes);
49-
app.use("/api/v1/sanctioned-regions", sanctionedRegionsRoutes);
49+
app.use("/api/v1/restrictions", restrictionRoutes);
5050

5151
app.use("/", homeRoutes);
5252

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { checkSanctionStatus } from '../../../src/controllers/admin/restrictionController';
2+
import * as restrictionService from '../../../src/services/admin/restriction.service';
3+
import { RestrictedArea } from '../../../src/models/enums/restrictedArea';
4+
5+
jest.mock('../../../src/services/admin/restriction.service', () => ({
6+
validateSellerLocation: jest.fn(),
7+
}));
8+
9+
describe('RestrictionController', () => {
10+
let req: any;
11+
let res: any;
12+
13+
beforeEach(() => {
14+
req = { body: {} };
15+
res = {
16+
status: jest.fn().mockReturnThis(),
17+
json: jest.fn(),
18+
};
19+
});
20+
21+
describe('checkSanctionedStatus function', () => {
22+
const mockSanctionedRegion = {
23+
location: RestrictedArea.NORTH_KOREA,
24+
boundary: {
25+
type: 'Polygon',
26+
coordinates: [[
27+
[123.5, 37.5],
28+
[131.2, 37.5],
29+
[131.2, 43.0],
30+
[123.5, 43.0],
31+
[123.5, 37.5],
32+
]],
33+
},
34+
};
35+
36+
it('should return [200] and isSanctioned true if seller is in a sanctioned zone', async () => {
37+
req.body = { latitude: 123.5, longitude: 40.5 };
38+
(restrictionService.validateSellerLocation as jest.Mock).mockResolvedValue(mockSanctionedRegion);
39+
40+
await checkSanctionStatus(req, res);
41+
expect(restrictionService.validateSellerLocation).toHaveBeenCalled();
42+
expect(res.status).toHaveBeenCalledWith(200);
43+
expect(res.json).toHaveBeenCalledWith({
44+
message: 'Sell center is set within a sanctioned zone',
45+
isSanctioned: true,
46+
});
47+
});
48+
49+
it('should return [200] and isSanctioned false if seller is not in a sanctioned zone', async () => {
50+
req.body = { latitude: 23.5, longitude: 40.5 };
51+
(restrictionService.validateSellerLocation as jest.Mock).mockResolvedValue(null);
52+
53+
await checkSanctionStatus(req, res);
54+
expect(restrictionService.validateSellerLocation).toHaveBeenCalled();
55+
expect(res.status).toHaveBeenCalledWith(200);
56+
expect(res.json).toHaveBeenCalledWith({
57+
message: 'Sell center is set within a unsanctioned zone',
58+
isSanctioned: false,
59+
});
60+
});
61+
62+
it('should return [400] if latitude or longitude is missing or invalid', async () => {
63+
req.body = { latitude: "malformed", longitude: null };
64+
65+
await checkSanctionStatus(req, res);
66+
67+
expect(res.status).toHaveBeenCalledWith(400);
68+
expect(res.json).toHaveBeenCalledWith({ error: "Unexpected coordinates provided" });
69+
});
70+
71+
it('should return appropriate [500] if check sanction status fails', async () => {
72+
const mockError = new Error('An error occurred while checking sanction status; please try again later');
73+
74+
req.body = { latitude: 23.5, longitude: 40.5 };
75+
(restrictionService.validateSellerLocation as jest.Mock).mockRejectedValue(mockError);
76+
77+
await checkSanctionStatus(req, res);
78+
expect(restrictionService.validateSellerLocation).toHaveBeenCalled();
79+
expect(res.status).toHaveBeenCalledWith(500);
80+
expect(res.json).toHaveBeenCalledWith({ message: mockError.message });
81+
});
82+
});
83+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { validateSellerLocation } from '../../../src/services/admin/restriction.service';
2+
import SanctionedRegion from '../../../src/models/misc/SanctionedRegion';
3+
import { RestrictedArea } from '../../../src/models/enums/restrictedArea';
4+
5+
jest.mock('../../../src/models/misc/SanctionedRegion');
6+
7+
describe('validateSellerLocation function', () => {
8+
const longitude = 123.5;
9+
const latitude = 40.5;
10+
11+
const mockSanctionedRegion = {
12+
location: RestrictedArea.NORTH_KOREA,
13+
boundary: {
14+
type: 'Polygon',
15+
coordinates: [[
16+
[123.5, 37.5],
17+
[131.2, 37.5],
18+
[131.2, 43.0],
19+
[123.5, 43.0],
20+
[123.5, 37.5],
21+
]],
22+
},
23+
};
24+
25+
it('should return a sanctioned region given the coordinates is found', async () => {
26+
(SanctionedRegion.findOne as jest.Mock).mockReturnValue({
27+
exec: jest.fn().mockResolvedValue(mockSanctionedRegion),
28+
});
29+
30+
const result = await validateSellerLocation(longitude, latitude);
31+
32+
expect(SanctionedRegion.findOne).toHaveBeenCalledWith({
33+
boundary: {
34+
$geoIntersects: {
35+
$geometry: {
36+
type: 'Point',
37+
coordinates: [longitude, latitude],
38+
},
39+
},
40+
},
41+
});
42+
43+
expect(result).toEqual(mockSanctionedRegion);
44+
});
45+
46+
it('should return null if no sanctioned region given the coordinates is found', async () => {
47+
(SanctionedRegion.findOne as jest.Mock).mockReturnValue({
48+
exec: jest.fn().mockResolvedValue(null),
49+
});
50+
51+
const result = await validateSellerLocation(longitude, latitude);
52+
expect(result).toBeNull();
53+
});
54+
});

0 commit comments

Comments
 (0)