Skip to content

Commit a6a4c66

Browse files
author
Tarash Agarwal
committed
Adding Cache Rules and Tests
1 parent 394b847 commit a6a4c66

File tree

5 files changed

+159
-52
lines changed

5 files changed

+159
-52
lines changed

src/api/routes/linkry.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,28 @@ const linkryRoutes: FastifyPluginAsync = async (fastify, _options) => {
176176
},
177177
async (request, reply) => {
178178
try {
179+
const ifNoneMatch = request.headers["if-none-match"];
180+
if (ifNoneMatch) {
181+
const etag = await getCacheCounter(
182+
fastify.dynamoClient,
183+
"linkry-etag-all",
184+
);
185+
186+
if (
187+
ifNoneMatch === `"${etag.toString()}"` ||
188+
ifNoneMatch === etag.toString()
189+
) {
190+
return reply.code(304).header("ETag", etag).send();
191+
}
192+
reply.header("etag", etag);
193+
} else {
194+
const etag = await getCacheCounter(
195+
fastify.dynamoClient,
196+
"linkry-etag-all",
197+
);
198+
reply.header("etag", etag);
199+
}
200+
179201
const username = request.username;
180202
const fetchAllOwnerRecords = new QueryCommand({
181203
TableName: genericConfig.LinkryDynamoTableName,
@@ -598,9 +620,17 @@ const linkryRoutes: FastifyPluginAsync = async (fastify, _options) => {
598620
new TransactWriteItemsCommand({ TransactItems: TransactItems }),
599621
);
600622

601-
reply
602-
.code(201)
603-
.send({ message: "Slug Created", id: request.body.slug });
623+
await atomicIncrementCacheCounter(
624+
fastify.dynamoClient,
625+
"linkry-etag-all",
626+
1,
627+
false,
628+
);
629+
630+
reply.code(201).send({
631+
message: "New Shortened Link Created",
632+
id: request.body.slug,
633+
});
604634
} catch (e: unknown) {
605635
console.log(e);
606636
throw new DatabaseInsertError({
@@ -964,6 +994,13 @@ const linkryRoutes: FastifyPluginAsync = async (fastify, _options) => {
964994
new TransactWriteItemsCommand({ TransactItems: transactItems }),
965995
);
966996

997+
await atomicIncrementCacheCounter(
998+
fastify.dynamoClient,
999+
"linkry-etag-all",
1000+
1,
1001+
false,
1002+
);
1003+
9671004
reply.code(200).send({ message: "Record Edited successfully" });
9681005
} catch (error) {
9691006
console.error("Error updating slug:", error);
@@ -1101,6 +1138,13 @@ const linkryRoutes: FastifyPluginAsync = async (fastify, _options) => {
11011138

11021139
await Promise.all(deletePromises);
11031140

1141+
await atomicIncrementCacheCounter(
1142+
fastify.dynamoClient,
1143+
"linkry-etag-all",
1144+
1,
1145+
false,
1146+
);
1147+
11041148
reply.code(200).send({
11051149
message: `All records with slug '${slug}' deleted successfully`,
11061150
});

tests/unit/auth.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@ const ddbMock = mockClient(SecretsManagerClient);
1919

2020
const app = await init();
2121
const jwt_secret = secretObject["jwt_key"];
22-
export function createJwt(date?: Date, group?: string, email?: string) {
22+
export function createJwt(
23+
date?: Date,
24+
group?: string,
25+
email?: string,
26+
roles?: string[], // Add roles parameter
27+
) {
2328
let modifiedPayload = {
2429
...jwtPayload,
2530
email: email || jwtPayload.email,
2631
groups: [...jwtPayload.groups],
32+
roles: roles || jwtPayload.roles, // Use provided roles or default roles
2733
};
34+
2835
if (date) {
2936
const nowMs = Math.floor(date.valueOf() / 1000);
3037
const laterMs = nowMs + 3600 * 24;
@@ -39,6 +46,7 @@ export function createJwt(date?: Date, group?: string, email?: string) {
3946
if (group) {
4047
modifiedPayload.groups[0] = group;
4148
}
49+
4250
return jwt.sign(modifiedPayload, jwt_secret, { algorithm: "HS256" });
4351
}
4452

tests/unit/linkry.test.ts

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,111 @@ smMock.on(GetSecretValueCommand).resolves({
5050
SecretString: secretJson,
5151
});
5252

53-
// Mock successful DynamoDB operations
54-
ddbMock.on(PutItemCommand).resolves({});
53+
const testJwt = createJwt(
54+
undefined, // No specific date
55+
undefined, // No specific group
56+
"[email protected]", // Test email
57+
["AppRoles.LINKS_MANAGER", "AppRoles.LINKS_ADMIN"], // Add required roles
58+
);
5559

56-
// Mock ScanCommand to return empty Items array
57-
ddbMock.on(ScanCommand).resolves({
58-
Items: [],
59-
});
60+
test("Happy path: Fetch all linkry redirects with proper roles", async () => {
61+
// Create a test JWT with roles
6062

61-
ddbMock.on(QueryCommand).resolves({
62-
Items: [],
63-
});
63+
// Mock successful DynamoDB operations
64+
ddbMock.on(QueryCommand).resolves({
65+
Items: [], // Simulate no existing records
66+
});
6467

65-
const testJwt = createJwt(undefined, "83c275f8-e533-4987-b537-a94b86c9d28e");
68+
// Make the request to the /api/v1/linkry/redir endpoint
69+
const response = await app.inject({
70+
method: "GET",
71+
url: "/api/v1/linkry/redir",
72+
headers: {
73+
Authorization: `Bearer ${testJwt}`, // Include the JWT with roles
74+
},
75+
});
6676

67-
const allLinkryResponse = await app.inject({
68-
method: "GET",
69-
url: "/api/v1/linkry/redir",
70-
headers: {
71-
Authorization: `Bearer ${testJwt}`,
72-
},
77+
// Assert the response status code
78+
expect(response.statusCode).toBe(200);
79+
expect(response.headers.etag).toBe("0");
7380
});
7481

75-
expect(allLinkryResponse.statusCode).toBe(200);
82+
//2. Create a new link using supertest
83+
// const eventResponse = await supertest(app.server)
84+
// .post("/api/v1/linkry/redir/")
85+
// .set("Authorization", `Bearer ${testJwt}`)
86+
// .send({
87+
// description: "Test event for ETag verification",
88+
// host: "Social Committee",
89+
// location: "Siebel Center",
90+
// start: "2024-09-25T18:00:00",
91+
// title: "ETag Test Event",
92+
// featured: false,
93+
// });
94+
95+
// expect(eventResponse.statusCode).toBe(201);
96+
// const eventId = eventResponse.body.id;
97+
98+
// test("Happy path: Create or update a linkry redirect", async () => {
99+
// // Mock successful DynamoDB operations
100+
// ddbMock.on(QueryCommand).resolves({
101+
// Items: [], // Simulate no existing records for the slug
102+
// });
103+
104+
// // Define the request payload
105+
// const payload = {
106+
// access: [],
107+
// counter: 0,
108+
// isEdited: true,
109+
// redirect: "https://www.rainbow.com",
110+
// slug: "bQjryt",
111+
// };
112+
113+
// // Make the request to the /api/v1/linkry/redir/ endpoint
114+
// const response = await supertest(app.server)
115+
// .post("/api/v1/linkry/redir/")
116+
// .set("Authorization", `Bearer ${testJwt}`) // Add authorization header
117+
// .send(payload); // Send the payload
118+
119+
// // Assert the response status code
120+
// expect(response.statusCode).toBe(201);
121+
122+
// // Assert the response body (optional, based on your API's response structure)
123+
// expect(response.body).toStrictEqual({
124+
// message: "Linkry redirect created or updated successfully",
125+
// slug: "bQjryt",
126+
// });
127+
// });
128+
129+
test("Happy path: Create a new linkry redirect", async () => {
130+
// Mock successful DynamoDB operations
131+
ddbMock.on(QueryCommand).resolves({
132+
Items: [], // Simulate no existing records for the slug
133+
});
134+
135+
ddbMock.on(PutItemCommand).resolves({}); // Simulate successful insertion
136+
137+
// Define the request payload
138+
const payload = {
139+
access: [],
140+
counter: 0,
141+
isEdited: true,
142+
redirect: "https://www.acm.illinois.edu/",
143+
slug: "acm-test-slug",
144+
};
145+
146+
// Make the request to the /api/v1/linkry/redir/ endpoint
147+
const response = await supertest(app.server)
148+
.post("/api/v1/linkry/redir")
149+
.set("Authorization", `Bearer ${testJwt}`) // Include the JWT with roles
150+
.send(payload); // Send the payload
151+
152+
// Assert the response status code
153+
expect(response.statusCode).toBe(201);
154+
155+
// Assert the response body
156+
expect(response.body).toStrictEqual({
157+
message: "New Shortened Link Created",
158+
id: "acm-test-slug",
159+
});
160+
});

tests/unit/linkry.tests.ts

Lines changed: 0 additions & 31 deletions
This file was deleted.

tests/unit/secret.testdata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const jwtPayload = {
2626
appidacr: "1",
2727
2828
groups: ["0"],
29+
roles: ["manage:links", "admin:links"], // Add roles here
2930
idp: "https://login.microsoftonline.com",
3031
ipaddr: "192.168.1.1",
3132
name: "John Doe",

0 commit comments

Comments
 (0)