Skip to content

Commit 607b6ab

Browse files
committed
Require form and pub pub_type agreement in pub memberships
1 parent 804498e commit 607b6ab

File tree

6 files changed

+112
-440
lines changed

6 files changed

+112
-440
lines changed

core/playwright/externalFormInvite.spec.ts

+29
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,35 @@ test.describe("Inviting a new user to fill out a form", () => {
303303
await expect(newPage.getByText("This page could not be found.")).toHaveCount(1);
304304
});
305305

306+
test("Invite fails if pub and form pub_types don't match", async () => {
307+
const pubDetailsPage = new PubDetailsPage(
308+
page,
309+
community.community.slug,
310+
community.pubs[2].id
311+
);
312+
await pubDetailsPage.goTo();
313+
314+
await pubDetailsPage.runAction(
315+
ACTION_NAME_EMAIL,
316+
async (runActionDialog) => {
317+
await runActionDialog
318+
.getByLabel("Email body")
319+
.fill(
320+
`Please fill out :link[this form]{form=${community.pubTypes.Evaluation.defaultForm.slug}}`
321+
);
322+
},
323+
false
324+
);
325+
await page.getByText("Failed to Send Email", { exact: true }).waitFor();
326+
await expect(
327+
page
328+
.getByLabel("Notifications (F8)")
329+
.getByText(
330+
"Invitation failed. The specified form is for Evaluation pubs but this pub's type is Submission"
331+
)
332+
).toBeVisible();
333+
});
334+
306335
// happy path
307336
test("Invites without creating a new user", async () => {
308337
await test.step("admin sends invite to non-existing user", async () => {

core/playwright/fixtures/pub-details-page.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export class PubDetailsPage {
1515

1616
async runAction(
1717
actionName: string,
18-
configureCallback?: (runActionDialog: Locator) => Promise<void>
18+
configureCallback?: (runActionDialog: Locator) => Promise<void>,
19+
waitForSuccess = true
1920
) {
2021
await this.page.getByTestId("run-action-primary").click();
2122
await this.page.getByRole("button", { name: actionName }).click();
@@ -26,11 +27,13 @@ export class PubDetailsPage {
2627
await configureCallback?.(runActionDialog);
2728

2829
await runActionDialog.getByRole("button", { name: "Run", exact: true }).click();
29-
await this.page
30-
.getByRole("status")
31-
.filter({ hasText: "Action ran successfully!" })
32-
.waitFor();
33-
await runActionDialog.getByRole("button", { name: "Close", exact: true }).click();
34-
await runActionDialog.waitFor({ state: "hidden" });
30+
if (waitForSuccess) {
31+
await this.page
32+
.getByRole("status")
33+
.filter({ hasText: "Action ran successfully!" })
34+
.waitFor();
35+
await runActionDialog.getByRole("button", { name: "Close", exact: true }).click();
36+
await runActionDialog.waitFor({ state: "hidden" });
37+
}
3538
}
3639
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
-- For all new pub memberships that specify a form, make sure that the form's pub type is the same
2+
-- as the pub's pub type
3+
CREATE OR REPLACE FUNCTION check_pub_membership_pubtype_agreement () RETURNS TRIGGER AS $$
4+
DECLARE
5+
pub_pub_type RECORD;
6+
form_pub_type RECORD;
7+
BEGIN
8+
IF (NEW."formId" IS NULL) THEN
9+
RETURN NEW;
10+
END IF;
11+
12+
SELECT pt.id, pt.name INTO pub_pub_type
13+
FROM pubs JOIN pub_types pt ON pt.id = pubs."pubTypeId"
14+
WHERE pubs.id = NEW."pubId";
15+
16+
SELECT pt.id, pt.name INTO form_pub_type
17+
FROM forms JOIN pub_types pt ON pt.id = forms."pubTypeId"
18+
WHERE forms.id = NEW."formId";
19+
20+
IF (pub_pub_type.id != form_pub_type.id) THEN
21+
RAISE EXCEPTION 'Pub membership creation failed. The specified form is for % pubs but this pub''s type is %', form_pub_type.name, pub_pub_type.name;
22+
END IF;
23+
RETURN NEW;
24+
END;
25+
$$ LANGUAGE plpgsql;
26+
27+
CREATE TRIGGER check_pub_membership_pubtype_agreement
28+
AFTER INSERT ON "pub_memberships" FOR EACH ROW
29+
EXECUTE FUNCTION check_pub_membership_pubtype_agreement ();
30+
31+
-- For all new invites that create a pub membership with a form, make sure that the form's pub type
32+
-- is the same as the pub's pub type The above trigger would also fire in this situation, but not
33+
-- until the user claims the invite. This error message is intended for an admin, so we use an
34+
-- additional trigger to raise it when the invite is created
35+
CREATE OR REPLACE FUNCTION check_invite_pubtype_agreement () RETURNS TRIGGER AS $$
36+
DECLARE
37+
pub_pub_type RECORD;
38+
form_pub_type RECORD;
39+
BEGIN
40+
IF (NEW.type != 'pub') THEN
41+
RETURN NEW;
42+
END IF;
43+
44+
SELECT pt.id, pt.name INTO pub_pub_type FROM invites
45+
JOIN pubs ON invites."pubId" = pubs.id
46+
JOIN pub_types pt ON pt.id = pubs."pubTypeId"
47+
WHERE invites.id = NEW."inviteId";
48+
49+
SELECT pt.id, pt.name INTO form_pub_type
50+
FROM forms JOIN pub_types pt ON pt.id = forms."pubTypeId"
51+
WHERE forms.id = NEW."formId";
52+
53+
IF (pub_pub_type.id != form_pub_type.id) THEN
54+
RAISE EXCEPTION 'Invitation failed. The specified form is for % pubs but this pub''s type is %', form_pub_type.name, pub_pub_type.name;
55+
END IF;
56+
RETURN NEW;
57+
END;
58+
$$ LANGUAGE plpgsql;
59+
60+
CREATE TRIGGER check_invite_pubtype_agreement
61+
AFTER INSERT ON "invite_forms" FOR EACH ROW
62+
EXECUTE FUNCTION check_invite_pubtype_agreement ();

core/prisma/seed.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { db } from "~/kysely/database";
88
import { isUniqueConstraintError } from "~/kysely/errors";
99
import { createPasswordHash } from "~/lib/authentication/password";
1010
import { env } from "~/lib/env/env";
11-
import { setupInviteTestCommunity } from "./seeds/invite-test-community";
1211
import { seedLegacy } from "./seeds/legacy";
1312
import { seedStarter } from "./seeds/starter";
1413

@@ -93,7 +92,7 @@ async function main() {
9392

9493
const legacyPromise = shouldSeedLegacy ? seedLegacy(legacyId) : null;
9594

96-
await Promise.all([seedStarter(starterId), legacyPromise, setupInviteTestCommunity()]);
95+
await Promise.all([seedStarter(starterId), legacyPromise]);
9796

9897
await Promise.all([
9998
createUserMembers({

0 commit comments

Comments
 (0)