mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
[DEVIN: Konsti] Add userCount property to Project table with automatic update trigger (#506)
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
This commit is contained in:
parent
0e17833609
commit
271ea9b175
@ -0,0 +1,51 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Project" ADD COLUMN "userCount" INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
-- Initialize userCount for existing projects
|
||||
UPDATE "Project" SET "userCount" = (
|
||||
SELECT COUNT(*) FROM "ProjectUser"
|
||||
WHERE "ProjectUser"."mirroredProjectId" = "Project"."id"
|
||||
);
|
||||
|
||||
-- Create function to update userCount
|
||||
CREATE OR REPLACE FUNCTION update_project_user_count()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
-- Increment userCount when a new ProjectUser is added
|
||||
UPDATE "Project" SET "userCount" = "userCount" + 1
|
||||
WHERE "id" = NEW."mirroredProjectId";
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
-- Decrement userCount when a ProjectUser is deleted
|
||||
UPDATE "Project" SET "userCount" = "userCount" - 1
|
||||
WHERE "id" = OLD."mirroredProjectId";
|
||||
ELSIF TG_OP = 'UPDATE' AND OLD."mirroredProjectId" <> NEW."mirroredProjectId" THEN
|
||||
-- If mirroredProjectId changed, decrement count for old project and increment for new project
|
||||
UPDATE "Project" SET "userCount" = "userCount" - 1
|
||||
WHERE "id" = OLD."mirroredProjectId";
|
||||
|
||||
UPDATE "Project" SET "userCount" = "userCount" + 1
|
||||
WHERE "id" = NEW."mirroredProjectId";
|
||||
END IF;
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create triggers
|
||||
DROP TRIGGER IF EXISTS project_user_insert_trigger ON "ProjectUser";
|
||||
CREATE TRIGGER project_user_insert_trigger
|
||||
AFTER INSERT ON "ProjectUser"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_project_user_count();
|
||||
|
||||
DROP TRIGGER IF EXISTS project_user_update_trigger ON "ProjectUser";
|
||||
CREATE TRIGGER project_user_update_trigger
|
||||
AFTER UPDATE ON "ProjectUser"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_project_user_count();
|
||||
|
||||
DROP TRIGGER IF EXISTS project_user_delete_trigger ON "ProjectUser";
|
||||
CREATE TRIGGER project_user_delete_trigger
|
||||
AFTER DELETE ON "ProjectUser"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_project_user_count();
|
||||
@ -21,6 +21,7 @@ model Project {
|
||||
configId String @db.Uuid
|
||||
config ProjectConfig @relation(fields: [configId], references: [id], onDelete: Cascade)
|
||||
isProductionMode Boolean
|
||||
userCount Int @default(0)
|
||||
|
||||
apiKeySets ApiKeySet[]
|
||||
projectUsers ProjectUser[]
|
||||
|
||||
@ -351,11 +351,6 @@ export function getProjectQuery(projectId: string): RawQuery<ProjectsCrud["Admin
|
||||
)
|
||||
FROM "ProjectConfig"
|
||||
WHERE "ProjectConfig"."id" = "Project"."configId"
|
||||
),
|
||||
'userCount', (
|
||||
SELECT count(*)
|
||||
FROM "ProjectUser"
|
||||
WHERE "ProjectUser"."mirroredProjectId" = "Project"."id"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -1272,3 +1272,67 @@ it("has a correctly formatted JWKS endpoint", async ({ expect }) => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should increment and decrement userCount when a user is added to a project", async ({ expect }) => {
|
||||
const { adminAccessToken } = await Project.createAndSwitch({
|
||||
config: {
|
||||
magic_link_enabled: true,
|
||||
}
|
||||
});
|
||||
const initialProjectResponse = await niceBackendFetch("/api/v1/projects/current", { accessType: "admin" });
|
||||
expect(initialProjectResponse.status).toBe(200);
|
||||
expect(initialProjectResponse.body.user_count).toBe(0);
|
||||
|
||||
|
||||
// Create a new user in the project
|
||||
await Auth.Password.signUpWithEmail();
|
||||
|
||||
// Check that the userCount has been incremented
|
||||
const updatedProjectResponse = await niceBackendFetch("/api/v1/projects/current", { accessType: "admin" });
|
||||
expect(updatedProjectResponse.status).toBe(200);
|
||||
expect(updatedProjectResponse).toMatchInlineSnapshot(`
|
||||
NiceResponse {
|
||||
"status": 200,
|
||||
"body": {
|
||||
"config": {
|
||||
"allow_localhost": true,
|
||||
"client_team_creation_enabled": false,
|
||||
"client_user_deletion_enabled": false,
|
||||
"create_team_on_sign_up": false,
|
||||
"credential_enabled": true,
|
||||
"domains": [],
|
||||
"email_config": { "type": "shared" },
|
||||
"enabled_oauth_providers": [],
|
||||
"id": "<stripped UUID>",
|
||||
"magic_link_enabled": true,
|
||||
"oauth_providers": [],
|
||||
"passkey_enabled": false,
|
||||
"sign_up_enabled": true,
|
||||
"team_creator_default_permissions": [{ "id": "admin" }],
|
||||
"team_member_default_permissions": [{ "id": "member" }],
|
||||
},
|
||||
"created_at_millis": <stripped field 'created_at_millis'>,
|
||||
"description": "",
|
||||
"display_name": "New Project",
|
||||
"id": "<stripped UUID>",
|
||||
"is_production_mode": false,
|
||||
"user_count": 1,
|
||||
},
|
||||
"headers": Headers { <some fields may have been hidden> },
|
||||
}
|
||||
`);
|
||||
expect(updatedProjectResponse.body.user_count).toBe(1);
|
||||
|
||||
// Delete the user
|
||||
const deleteRes = await niceBackendFetch("/api/v1/users/me", {
|
||||
accessType: "admin",
|
||||
method: "DELETE",
|
||||
});
|
||||
expect(deleteRes.status).toBe(200);
|
||||
|
||||
// Check that the userCount has been decremented
|
||||
const finalProjectResponse = await niceBackendFetch("/api/v1/projects/current", { accessType: "admin" });
|
||||
expect(finalProjectResponse.status).toBe(200);
|
||||
expect(finalProjectResponse.body.user_count).toBe(0);
|
||||
});
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"codegen": "pnpm pre && turbo run codegen && pnpm run generate-sdks",
|
||||
"deps-compose": "docker compose -p stack-dependencies -f docker/dependencies/docker.compose.yaml",
|
||||
"stop-deps": "POSTGRES_DELAY_MS=0 pnpm run deps-compose kill && POSTGRES_DELAY_MS=0 pnpm run deps-compose down -v",
|
||||
"init-db": "pnpm pre && pnpm run prisma db push && pnpm run prisma db seed",
|
||||
"init-db": "pnpm pre && pnpm run prisma migrate deploy && pnpm run prisma db seed",
|
||||
"wait-until-postgres-is-ready:pg_isready": "until pg_isready -h localhost -p 5432; do sleep 1; done",
|
||||
"wait-until-postgres-is-ready": "command -v pg_isready >/dev/null 2>&1 && pnpm run wait-until-postgres-is-ready:pg_isready || sleep 10 # not everyone has pg_isready installed, so we fallback to sleeping",
|
||||
"start-deps:no-delay": "pnpm pre && pnpm run deps-compose up --detach --build && pnpm run wait-until-postgres-is-ready && pnpm run init-db && echo \"\\nDependencies started in the background as Docker containers. 'pnpm run stop-deps' to stop them\"n",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user