Skip to content

Commit b64cb17

Browse files
author
Liam Lloyd-Tucker
committed
Add DELETE /share-links endpoint
Users should be able to delete a share link. This commit ends an endpoint that does so.
1 parent 2b05786 commit b64cb17

File tree

4 files changed

+140
-0
lines changed

4 files changed

+140
-0
lines changed

Diff for: packages/api/src/share_link/controller.test.ts

+92
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ describe("GET /share-links", () => {
593593
});
594594

595595
afterEach(async () => {
596+
jest.restoreAllMocks();
596597
await clearDatabase();
597598
});
598599

@@ -697,3 +698,94 @@ describe("GET /share-links", () => {
697698
await agent.get("/api/v2/share-links?shareLinkIds[]=1000").expect(500);
698699
});
699700
});
701+
702+
describe("DELETE /share-links", () => {
703+
const agent = request(app);
704+
705+
beforeEach(async () => {
706+
(verifyUserAuthentication as jest.Mock).mockImplementation(
707+
(req: Request, _, next: NextFunction) => {
708+
(
709+
req.body as {
710+
emailFromAuthToken: string;
711+
userSubjectFromAuthToken: string;
712+
}
713+
).emailFromAuthToken = "[email protected]";
714+
(
715+
req.body as {
716+
emailFromAuthToken: string;
717+
userSubjectFromAuthToken: string;
718+
}
719+
).userSubjectFromAuthToken = "ceca5477-3f9c-4d0a-a7b8-04d5e5adac32";
720+
next();
721+
}
722+
);
723+
724+
await loadFixtures();
725+
});
726+
727+
afterEach(async () => {
728+
await clearDatabase();
729+
});
730+
731+
test("should return 204 for a valid request", async () => {
732+
await agent.delete("/api/v2/share-links/1000").expect(204);
733+
});
734+
735+
test("should return 401 if the caller is unauthenticated", async () => {
736+
(verifyUserAuthentication as jest.Mock).mockImplementation(
737+
(__: Request, _, next: NextFunction) => {
738+
next(new createError.Unauthorized("Invalid token"));
739+
}
740+
);
741+
await agent.delete("/api/v2/share-links/1000").expect(401);
742+
});
743+
744+
test("should return 400 if header values are missing", async () => {
745+
(verifyUserAuthentication as jest.Mock).mockImplementation(
746+
(__: Request, _, next: NextFunction) => {
747+
next();
748+
}
749+
);
750+
await agent.delete("/api/v2/share-links/1000").expect(400);
751+
});
752+
753+
test("should delete the share link", async () => {
754+
await agent.delete("/api/v2/share-links/1000").expect(204);
755+
const shareLinks = await agent
756+
.get("/api/v2/share-links?shareLinkIds[]=1000")
757+
.expect(200);
758+
expect((shareLinks.body as { items: ShareLink[] }).items.length).toEqual(0);
759+
});
760+
761+
test("should return 404 if the share link doesn't exist", async () => {
762+
await agent.delete("/api/v2/share-links/1000").expect(204);
763+
await agent.delete("/api/v2/share-links/1000").expect(404);
764+
});
765+
766+
test("should return 404 if the caller doesn't have access to the share link", async () => {
767+
(verifyUserAuthentication as jest.Mock).mockImplementation(
768+
(req: Request, _, next: NextFunction) => {
769+
(
770+
req.body as {
771+
emailFromAuthToken: string;
772+
userSubjectFromAuthToken: string;
773+
}
774+
).emailFromAuthToken = "[email protected]";
775+
(
776+
req.body as {
777+
emailFromAuthToken: string;
778+
userSubjectFromAuthToken: string;
779+
}
780+
).userSubjectFromAuthToken = "ceca5477-3f9c-4d0a-a7b8-04d5e5adac32";
781+
next();
782+
}
783+
);
784+
await agent.delete("/api/v2/share-links/1000").expect(404);
785+
});
786+
787+
test("should return 500 if the database call fails", async () => {
788+
jest.spyOn(db, "sql").mockRejectedValue(new Error("Test error"));
789+
await agent.delete("/api/v2/share-links/1000").expect(500);
790+
});
791+
});

Diff for: packages/api/src/share_link/controller.ts

+21
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,24 @@ shareLinkController.get(
7373
}
7474
}
7575
);
76+
77+
shareLinkController.delete(
78+
"/:shareLinkId",
79+
verifyUserAuthentication,
80+
async (req: Request, res: Response, next: NextFunction) => {
81+
try {
82+
validateBodyFromAuthentication(req.body);
83+
await shareLinkService.deleteShareLink(
84+
req.body.emailFromAuthToken,
85+
req.params["shareLinkId"] ?? ""
86+
);
87+
res.sendStatus(204);
88+
} catch (err) {
89+
if (isValidationError(err)) {
90+
res.status(400).json({ error: err });
91+
return;
92+
}
93+
next(err);
94+
}
95+
}
96+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DELETE FROM
2+
shareby_url
3+
USING
4+
account
5+
WHERE
6+
account.accountid = shareby_url.byaccountid
7+
AND account.primaryemail = :email
8+
AND shareby_urlid = :shareLinkId
9+
RETURNING shareby_urlid AS "id";

Diff for: packages/api/src/share_link/service.ts

+18
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,26 @@ const getShareLinks = async (
185185
return shareLinks.rows;
186186
};
187187

188+
const deleteShareLink = async (
189+
email: string,
190+
shareLinkId: string
191+
): Promise<void> => {
192+
const response = await db
193+
.sql<{ id: string }>("share_link.queries.delete_share_link", {
194+
email,
195+
shareLinkId,
196+
})
197+
.catch((err) => {
198+
logger.error(err);
199+
throw new Error("Failed to delete share link");
200+
});
201+
if (response.rows.length === 0)
202+
throw new createError.NotFound("Share link not found");
203+
};
204+
188205
export const shareLinkService = {
189206
createShareLink,
190207
updateShareLink,
191208
getShareLinks,
209+
deleteShareLink,
192210
};

0 commit comments

Comments
 (0)