diff --git a/packages/postgrest/supabase/tests/cleanup-builds.sql b/packages/postgrest/supabase/tests/cleanup-builds.sql new file mode 100644 index 000000000000..d2a0c90b6501 --- /dev/null +++ b/packages/postgrest/supabase/tests/cleanup-builds.sql @@ -0,0 +1,227 @@ +BEGIN; +SET LOCAL search_path = pgtap, public; +-- Initialize the testing environment without planning any specific number of tests +-- We are using SELECT no_plan() because we don't specify the exact number of tests upfront. +SELECT no_plan(); + +-- ========================================= +-- Setup: Insert initial data for Users, Projects, Domains, ProjectDomains, and Builds +-- ========================================= + +-- Insert a user into the "User" table +INSERT INTO "public"."User" ("id", "createdAt", "email", "username") +VALUES + ('user1', '2023-01-01 00:00:00+00', 'user1@517cce32-9af3-example.com', 'user1'); + +-- Insert projects associated with the user into the "Project" table +INSERT INTO "public"."Project" ("id", "title", "domain", "userId", "isDeleted", "createdAt") +VALUES + ('project1', 'Project One', '517cce32-9af3-project1-domain1', 'user1', false, '2023-01-01 00:00:00+00'), + ('project2', 'Project Two', '517cce32-9af3-project2-domain1', 'user1', false, '2023-01-01 00:00:00+00'); + +-- Insert custom domains into the "Domain" table +INSERT INTO "public"."Domain" ("id", "domain", "createdAt", "status", "updatedAt") +VALUES + ('project-1-custom-domain-1', '517cce32-9af3-project-1-custom-domain-1.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), + ('project-1-custom-domain-2', '517cce32-9af3-project-1-custom-domain-2.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), + ('project-2-custom-domain-1', '517cce32-9af3-project-2-custom-domain-1.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), + ('project-2-custom-domain-2', '517cce32-9af3-project-2-custom-domain-2.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'); + +-- Establish relationships between projects and custom domains in the "ProjectDomain" table +INSERT INTO "public"."ProjectDomain" ("projectId", "domainId", "createdAt", "txtRecord", "cname") +VALUES + ('project1', 'project-1-custom-domain-1', '2023-01-01 00:00:00+00', 'txtRecord1', 'cname1'), + ('project1', 'project-1-custom-domain-2', '2023-01-01 00:00:00+00', 'txtRecord2', 'cname2'), + ('project2', 'project-2-custom-domain-1', '2023-01-01 00:00:00+00', 'p2-txtRecord1', 'cname1'), + ('project2', 'project-2-custom-domain-2', '2023-01-01 00:00:00+00', 'p2-txtRecord2', 'cname2'); + +-- Insert initial builds into the "Build" table +INSERT INTO "public"."Build" ( + "id", + "createdAt", + "pages", + "projectId", + "deployment", + "updatedAt", + "publishStatus" +) +VALUES + -- Development Build for Project1 + ( + 'build1-development', + '1990-01-01 00:00:00+00', + 'home', + 'project1', + NULL, + '1990-01-01 00:00:00+00', + 'PENDING' + ), + -- Development Build for Project2 + ( + 'project2-build1-development', + '1990-01-01 00:00:00+00', + 'home', + 'project2', + NULL, + '1990-01-01 00:00:00+00', + 'PENDING' + ), + -- Custom Domain Build for Project2 + ( + 'project2-build1-for-custom-domain-1', + '1990-01-02 00:00:00+00', + 'home', + 'project2', + '{"domains": ["517cce32-9af3-project-2-custom-domain-1.com"]}'::text, + '1990-01-02 00:00:00+00', + 'PUBLISHED' + ), + -- Project Domain Build for Project2 + ( + 'project2-build1-for-project-domain-1', + '1990-01-01 00:00:00+00', + 'home', + 'project2', + '{"domains": ["517cce32-9af3-project2-domain1"]}'::text, + '1990-01-01 00:00:00+00', + 'PUBLISHED' + ), + -- Custom Domain Build for Project1 + ( + 'build1-for-custom-domain-1', + '1990-01-02 00:00:00+00', + 'home', + 'project1', + '{"domains": ["517cce32-9af3-project-1-custom-domain-1.com"]}'::text, + '1990-01-02 00:00:00+00', + 'PUBLISHED' + ), + -- Project Domain Build for Project1 + ( + 'build1-for-project-domain-1', + '1990-01-01 00:00:00+00', + 'home', + 'project1', + '{"domains": ["517cce32-9af3-project1-domain1"]}'::text, + '1990-01-01 00:00:00+00', + 'PUBLISHED' + ); + +-- ========================================= +-- Test 1: Verify that initial cleanup does not clean any builds +-- ========================================= + +-- Run the database cleanup function for builds created in 1990 +SELECT database_cleanup('1990-01-01 00:00:00', '1990-12-31 23:59:59'); + +-- Assert that no builds have been cleaned up initially +SELECT is( + (SELECT count(*)::integer FROM "Build" WHERE "createdAt" BETWEEN '1990-01-01' AND '1990-12-31' AND "isCleaned" = TRUE), + 0, + 'Test 1: No builds should be cleaned up on initial cleanup' +); + +-- ========================================= +-- Test 2: Insert a new project domain build and verify cleanup +-- ========================================= + +-- Insert a new build for the project domain +INSERT INTO "public"."Build" ( + "id", + "createdAt", + "pages", + "projectId", + "deployment", + "updatedAt", + "publishStatus" +) +VALUES + -- New Project Domain Build for Project1 + ( + 'build2-for-project-domain-1', + '1990-01-02 00:00:00+00', -- A later date than the previous build + 'home', + 'project1', + '{"domains": ["517cce32-9af3-project1-domain1"]}'::text, + '1990-01-02 00:00:00+00', + 'PUBLISHED' + ); + +-- Run the database cleanup function again +SELECT database_cleanup('1990-01-01 00:00:00', '1990-12-31 23:59:59'); + +-- Assert that one build has been cleaned (the previous project domain build) +SELECT is( + (SELECT count(*)::integer FROM "Build" WHERE "createdAt" BETWEEN '1990-01-01' AND '1990-12-31' AND "isCleaned" = TRUE), + 1, + 'Test 2: Previous project domain build should be cleaned up after new build is published' +); + +-- Assert that the specific build cleaned is 'build1-for-project-domain-1' +SELECT is( + (SELECT id FROM "Build" WHERE "createdAt" BETWEEN '1990-01-01' AND '1990-12-31' AND "isCleaned" = TRUE), + 'build1-for-project-domain-1', + 'Test 2: Build "build1-for-project-domain-1" should be marked as cleaned' +); + +-- ========================================= +-- Test 3: Insert a new custom domain build and verify cleanup +-- ========================================= + +-- Insert a new build for the custom domain +INSERT INTO "public"."Build" ( + "id", + "createdAt", + "pages", + "projectId", + "deployment", + "updatedAt", + "publishStatus" +) +VALUES + -- New Custom Domain Build for Project1 + ( + 'build2-for-custom-domain-1', + '1990-01-03 00:00:00+00', -- A later date than the previous build + 'home', + 'project1', + '{"domains": ["517cce32-9af3-project-1-custom-domain-1.com"]}'::text, + '1990-01-03 00:00:00+00', + 'PUBLISHED' + ); + +-- Run the database cleanup function again +SELECT database_cleanup('1990-01-01 00:00:00', '1990-12-31 23:59:59'); + +-- Assert that two builds have been cleaned (the previous project domain and custom domain builds) +SELECT is( + (SELECT count(*)::integer FROM "Build" WHERE "createdAt" BETWEEN '1990-01-01' AND '1990-12-31' AND "isCleaned" = TRUE), + 2, + 'Test 3: Previous custom domain build should be cleaned up after new build is published' +); + +-- Assert that the builds cleaned are 'build1-for-custom-domain-1' and 'build1-for-project-domain-1' +SELECT results_eq( + $$ + SELECT id FROM "Build" WHERE "createdAt" BETWEEN '1990-01-01' AND '1990-12-31' AND "isCleaned" = TRUE ORDER BY id + $$, + $$ + SELECT * FROM ( + VALUES + ('build1-for-custom-domain-1'), + ('build1-for-project-domain-1') + ) AS expected(id) + ORDER BY "id" + $$, + 'Test 3: Builds "build1-for-custom-domain-1" and "build1-for-project-domain-1" should be marked as cleaned' +); + +-- ========================================= +-- Finish the test +-- ========================================= + +-- Finish the test by calling the finish() function, which outputs the test summary +SELECT finish(); + +-- Rollback the transaction to ensure no changes are persisted in the database +ROLLBACK; diff --git a/packages/postgrest/supabase/tests/latest-builds-domains.sql b/packages/postgrest/supabase/tests/latest-builds-domains.sql index 82e1d79aa9ae..afcb3a58423f 100644 --- a/packages/postgrest/supabase/tests/latest-builds-domains.sql +++ b/packages/postgrest/supabase/tests/latest-builds-domains.sql @@ -9,19 +9,19 @@ SELECT no_plan(); -- Insert a new user into the User table INSERT INTO "public"."User" ("id", "createdAt", "email", "username") VALUES - ('user1', '2023-01-01 00:00:00+00', 'user1@example.com', 'user1'); + ('user1', '2023-01-01 00:00:00+00', 'user1@517cce32-9af3-example.com', 'user1'); -- Insert projects associated with the user INSERT INTO "public"."Project" ("id", "title", "domain", "userId", "isDeleted", "createdAt") VALUES - ('project1', 'Project One', 'project1-domain1', 'user1', false, '2023-01-01 00:00:00+00'), - ('project2', 'Project Two', 'project2-domain1', 'user1', false, '2023-01-01 00:00:00+00'); + ('project1', 'Project One', '517cce32-9af3-project1-domain1', 'user1', false, '2023-01-01 00:00:00+00'), + ('project2', 'Project Two', '517cce32-9af3-project2-domain1', 'user1', false, '2023-01-01 00:00:00+00'); -- Insert custom domains into the Domain table INSERT INTO "public"."Domain" ("id", "domain", "createdAt", "status", "updatedAt") VALUES - ('project-1-custom-domain-1', 'project-1-custom-domain-1.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), - ('project-1-custom-domain-2', 'project-1-custom-domain-2.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'); + ('project-1-custom-domain-1', '517cce32-9af3-project-1-custom-domain-1.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), + ('project-1-custom-domain-2', '517cce32-9af3-project-1-custom-domain-2.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'); -- Establish relationships between projects and custom domains INSERT INTO "public"."ProjectDomain" ("projectId", "domainId", "createdAt", "txtRecord", "cname") @@ -76,7 +76,7 @@ VALUES '2023-01-02 00:00:00+00', 'home', 'project1', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-1.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-1.com"]}'::text, '2023-01-02 00:00:00+00', 'PUBLISHED' ); @@ -113,7 +113,7 @@ VALUES '2024-01-02 00:00:00+00', 'home', 'project1', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-1.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-1.com"]}'::text, '2024-01-02 00:00:00+00', 'PUBLISHED' ); @@ -150,7 +150,7 @@ VALUES '2024-01-03 00:00:00+00', 'home', 'project1', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-2.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-2.com"]}'::text, '2024-01-03 00:00:00+00', 'PUBLISHED' ); @@ -187,7 +187,7 @@ VALUES '2024-01-04 00:00:00+00', 'home', 'project1', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-2.com", "project-1-custom-domain-1.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-2.com", "517cce32-9af3-project-1-custom-domain-1.com"]}'::text, '2024-01-04 00:00:00+00', 'PUBLISHED' ); @@ -230,7 +230,7 @@ VALUES '2025-01-04 00:00:00+00', 'home', 'project2', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-2.com", "project-1-custom-domain-1.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-2.com", "517cce32-9af3-project-1-custom-domain-1.com"]}'::text, '2025-01-04 00:00:00+00', 'PUBLISHED' ); diff --git a/packages/postgrest/supabase/tests/latest-builds-projects.sql b/packages/postgrest/supabase/tests/latest-builds-projects.sql index 0106c99d8c78..631d302e7445 100644 --- a/packages/postgrest/supabase/tests/latest-builds-projects.sql +++ b/packages/postgrest/supabase/tests/latest-builds-projects.sql @@ -16,8 +16,8 @@ VALUES -- Insert projects associated with the user INSERT INTO "public"."Project" ("id", "title", "domain", "userId", "isDeleted", "createdAt") VALUES - ('project1', 'Project One', 'project1-domain1', 'user1', false, '2023-01-01 00:00:00+00'), - ('project2', 'Project Two', 'project2-domain1', 'user1', false, '2023-01-01 00:00:00+00'); + ('project1', 'Project One', '517cce32-9af3-project1-domain1', 'user1', false, '2023-01-01 00:00:00+00'), + ('project2', 'Project Two', '517cce32-9af3-project2-domain1', 'user1', false, '2023-01-01 00:00:00+00'); -- Insert builds with different deployment formats INSERT INTO "public"."Build" ( @@ -36,7 +36,7 @@ VALUES '2023-01-01 00:00:00+00', 'home', 'project1', - '{"projectDomain": "project1-domain1", "domains": [""]}'::text, + '{"projectDomain": "517cce32-9af3-project1-domain1", "domains": [""]}'::text, '2023-01-01 00:00:00+00', 'PUBLISHED' ), @@ -45,7 +45,7 @@ VALUES '2022-01-01 00:00:00+00', 'home', 'project1', - '{"projectDomain": "project1-domain1", "domains": [""]}'::text, + '{"projectDomain": "517cce32-9af3-project1-domain1", "domains": [""]}'::text, '2022-01-01 00:00:00+00', 'PUBLISHED' ), @@ -64,7 +64,7 @@ VALUES '2023-01-02 00:00:00+00', 'home', 'project2', - '{"domains": ["project2-domain1"]}'::text, + '{"domains": ["517cce32-9af3-project2-domain1"]}'::text, '2023-01-02 00:00:00+00', 'PENDING' ), @@ -73,7 +73,7 @@ VALUES '2022-01-02 00:00:00+00', 'home', 'project2', - '{"domains": ["project2-domain1"]}'::text, + '{"domains": ["517cce32-9af3-project2-domain1"]}'::text, '2022-01-02 00:00:00+00', 'PENDING' ); @@ -92,8 +92,24 @@ SELECT is ( ) ) ), - ARRAY['build1', 'project1-domain1'], - 'Test Case 1: Should return the latest build for project1 with domain matching projectDomain.' + ARRAY['build1', '517cce32-9af3-project1-domain1'], + 'Test Case 1.1: Should return the latest build for project1 with domain matching projectDomain.' +); + + +SELECT is ( + ( + SELECT ARRAY["buildId", "domain"] + FROM "public"."latestProjectDomainBuildVirtual"( + ( + SELECT (p.*)::"Project" + FROM "public"."Project" p + WHERE p."id" = 'project1' + ) + ) + ), + ARRAY['build1', '517cce32-9af3-project1-domain1'], + 'Test Case 1.2: Should return the latest build for project1 with domain matching projectDomain.' ); -------------------------------------------------------------------------------- @@ -111,9 +127,23 @@ SELECT is ( ) ), 'build2', - 'Test Case 2: Should return the latest build for project2 with domain present in domains array.' + 'Test Case 2.1: Should return the latest build for project2 with domain present in domains array.' ); +SELECT is ( + ( + SELECT "buildId" + FROM "public"."latestProjectDomainBuildVirtual"( + ( + SELECT (p.*)::"Project" + FROM "public"."Project" p + WHERE p."id" = 'project2' + ) + ) + ), + 'build2', + 'Test Case 2.2: Should return the latest build for project2 domain with domain present in domains array.' +); -------------------------------------------------------------------------------- -- Test Case 3: Update Project Domain and Verify No Build Exists for the New Domain -------------------------------------------------------------------------------- @@ -135,7 +165,22 @@ SELECT is ( ) ), 0, - 'Test Case 3: Should return 0 as no build exists for the updated domain project1-domain2.' + 'Test Case 3.1: Should return 0 as no build exists for the updated domain project1-domain2.' +); + +SELECT is ( + ( + SELECT COUNT(*)::integer + FROM "public"."latestProjectDomainBuildVirtual"( + ( + SELECT (p.*)::"Project" + FROM "public"."Project" p + WHERE p."id" = 'project1' + ) + ) + ), + 0, + 'Test Case 3.2: Should return 0 as no build exists for the updated domain project1-domain2.' ); -------------------------------------------------------------------------------- @@ -175,9 +220,25 @@ SELECT is ( ) ), ARRAY['build1-for-domain2','project1-domain2'], - 'Test Case 4: Should return the latest build for project1 with the updated domain in domains array.' + 'Test Case 4.1: Should return the latest build for project1 with the updated domain in domains array.' +); + +SELECT is ( + ( + SELECT ARRAY["buildId", "domain"] + FROM "public"."latestProjectDomainBuildVirtual"( + ( + SELECT (p.*)::"Project" + FROM "public"."Project" p + WHERE p."id" = 'project1' + ) + ) + ), + ARRAY['build1-for-domain2','project1-domain2'], + 'Test Case 4.2: Should return the latest build for project1 domain with the updated domain in domains array.' ); + -------------------------------------------------------------------------------- -- Test Case 5: Register Custom Domains and Verify Latest Build for a Custom Domain -------------------------------------------------------------------------------- @@ -190,8 +251,8 @@ INSERT INTO "public"."Domain" ( "updatedAt" ) VALUES - ('project-1-custom-domain-1', 'project-1-custom-domain-1.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), - ('project-1-custom-domain-2', 'project-1-custom-domain-2.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'); + ('project-1-custom-domain-1', '517cce32-9af3-project-1-custom-domain-1.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'), + ('project-1-custom-domain-2', '517cce32-9af3-project-1-custom-domain-2.com', '2023-01-01 00:00:00+00', 'INITIALIZING', '2023-01-01 00:00:00+00'); -- Establish relationships between project1 and custom domains INSERT INTO "public"."ProjectDomain" ( @@ -221,7 +282,7 @@ VALUES '2023-01-02 00:00:00+00', 'home', 'project1', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-1.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-1.com"]}'::text, '2023-01-02 00:00:00+00', 'PUBLISHED' ); @@ -238,8 +299,26 @@ SELECT is ( ) ) ), - ARRAY['build1-for-custom-domain-1','project-1-custom-domain-1.com'], - 'Test Case 5: Should return the latest build for project1 with a registered custom domain in domains array.' + ARRAY['build1-for-custom-domain-1','517cce32-9af3-project-1-custom-domain-1.com'], + 'Test Case 5.1: Should return the latest build for project1 with a registered custom domain in domains array.' +); + + +-- Ensure the latest project domain build has not changed +-- The difference between latestProjectDomainBuildVirtual and latestBuildVirtual is that the first returns data only for the project domain +SELECT is ( + ( + SELECT ARRAY["buildId", "domain"] + FROM "public"."latestProjectDomainBuildVirtual"( + ( + SELECT (p.*)::"Project" + FROM "public"."Project" p + WHERE p."id" = 'project1' + ) + ) + ), + ARRAY['build1-for-domain2','project1-domain2'], + 'Test Case 5.2: Should return the latest build for project1 domain and not affected by custom domains' ); -------------------------------------------------------------------------------- @@ -261,7 +340,7 @@ VALUES '2023-01-03 00:00:00+00', 'home', 'project1', - '{"domains": ["project-1-custom-domain-1.com", "project1-domain2"]}'::text, + '{"domains": ["517cce32-9af3-project-1-custom-domain-1.com", "project1-domain2"]}'::text, '2023-01-03 00:00:00+00', 'PUBLISHED' ); @@ -279,9 +358,25 @@ SELECT is ( ) ), ARRAY['build1-for-domain2-new', 'project1-domain2'], - 'Test Case 6: Should return the latest build for project1 with the preview domain in domains array.' + 'Test Case 6.1: Should return the latest build for project1 with the preview domain in domains array.' +); + +SELECT is ( + ( + SELECT ARRAY["buildId", "domain"] + FROM "public"."latestProjectDomainBuildVirtual"( + ( + SELECT (p.*)::"Project" + FROM "public"."Project" p + WHERE p."id" = 'project1' + ) + ) + ), + ARRAY['build1-for-domain2-new', 'project1-domain2'], + 'Test Case 6.2: Should return the latest build for project1 with the preview domain in domains array.' ); + -------------------------------------------------------------------------------- -- Test Case 7: Publish a New Build for a Custom Domain, Delete the Custom Domain, and Verify Latest Build Update -------------------------------------------------------------------------------- @@ -301,7 +396,7 @@ VALUES '2023-01-04 00:00:00+00', 'home', 'project1', - '{"domains": ["some-other-domain.com", "project-1-custom-domain-1.com"]}'::text, + '{"domains": ["some-other-domain.com", "517cce32-9af3-project-1-custom-domain-1.com"]}'::text, '2023-01-04 00:00:00+00', 'PUBLISHED' ); diff --git a/packages/postgrest/supabase/tests/project-domains.sql b/packages/postgrest/supabase/tests/project-domains.sql index b1120da71f9a..08ca2d746756 100644 --- a/packages/postgrest/supabase/tests/project-domains.sql +++ b/packages/postgrest/supabase/tests/project-domains.sql @@ -8,19 +8,19 @@ SELECT no_plan(); -- We're inserting user_1 as the user for the test projects INSERT INTO "public"."User" ("id", "createdAt", "email", "username") VALUES - ('user_1', '2023-01-01 00:00:00+00', 'user1@example.com', 'user1'); + ('user_1', '2023-01-01 00:00:00+00', 'user1@517cce32-9af3-example.com', 'user1'); -- Insert test projects into the Project table -- project_1 and project_2 belong to user_1 and are not deleted (isDeleted = false) INSERT INTO "Project" (id, title, domain, "userId", "isDeleted") VALUES -('project_1', 'Test Project 1', 'testproject1.com', 'user_1', false), -('project_2', 'Test Project 1', 'testproject2.com', 'user_1', false); +('project_1', 'Test Project 1', '517cce32-9af3-testproject1.com', 'user_1', false), +('project_2', 'Test Project 1', '517cce32-9af3-testproject2.com', 'user_1', false); -- Insert test domains into the Domain table --- We are inserting two domains: example.com and example.org with different statuses +-- We are inserting two domains: 517cce32-9af3-example.com and 517cce32-9af3-example.org with different statuses INSERT INTO "Domain" (id, domain, status, "txtRecord") VALUES -('domain_1', 'example.com', 'INITIALIZING', 'txtRecord1'), -('domain_2', 'example.org', 'ACTIVE', 'txtRecord21'); +('domain_1', '517cce32-9af3-example.com', 'INITIALIZING', 'txtRecord1'), +('domain_2', '517cce32-9af3-example.org', 'ACTIVE', 'txtRecord21'); -- Insert test data into the ProjectDomain table -- Mapping domains to projects, project_1 has two domains, project_2 has one domain @@ -43,8 +43,8 @@ SELECT results_eq( $$ SELECT * FROM ( VALUES - ('example.com','INITIALIZING'::"DomainStatus",NULL,E'txtRecord1',E'txtRecord1',TRUE), -- Verified domain (TXT records match) - ('example.org','ACTIVE'::"DomainStatus",NULL,E'txtRecord21',E'txtRecord22',FALSE) -- Not verified domain (TXT records do not match) + ('517cce32-9af3-example.com','INITIALIZING'::"DomainStatus",NULL,E'txtRecord1',E'txtRecord1',TRUE), -- Verified domain (TXT records match) + ('517cce32-9af3-example.org','ACTIVE'::"DomainStatus",NULL,E'txtRecord21',E'txtRecord22',FALSE) -- Not verified domain (TXT records do not match) ) AS expected(domain, status, error, "domainTxtRecord", "expectedTxtRecord", verified) ORDER BY "domain" $$, @@ -63,7 +63,7 @@ SELECT results_eq( $$ SELECT * FROM ( VALUES - ('example.com','INITIALIZING'::"DomainStatus",NULL,E'txtRecord1',E'txtRecord3',FALSE) -- Not verified domain (TXT records do not match) + ('517cce32-9af3-example.com','INITIALIZING'::"DomainStatus",NULL,E'txtRecord1',E'txtRecord3',FALSE) -- Not verified domain (TXT records do not match) ) AS expected(domain, status, error, "domainTxtRecord", "expectedTxtRecord", verified) ORDER BY "domain" $$, diff --git a/packages/prisma-client/prisma/migrations/20240924174536_cleanup/migration.sql b/packages/prisma-client/prisma/migrations/20240924174536_cleanup/migration.sql new file mode 100644 index 000000000000..a9f7496a1a6d --- /dev/null +++ b/packages/prisma-client/prisma/migrations/20240924174536_cleanup/migration.sql @@ -0,0 +1,81 @@ +ALTER TABLE "Build" ADD COLUMN "isCleaned" BOOLEAN DEFAULT FALSE; + +-- PostgREST will use this function as a computed field +-- See: https://docs.postgrest.org/en/v12/references/api/resource_embedding.html#computed-relationships +CREATE OR REPLACE FUNCTION "latestProjectDomainBuildVirtual"("Project") +RETURNS SETOF "latestBuildVirtual" +ROWS 1 AS $$ -- The function is expected to return 1 row + +-- This function selects the latest build for a given project domain where: +-- 1. The "deployment" field is not NULL, ensuring it is a production build. +-- 2. The 'destination' field in the JSONB "deployment" is either NULL (for backward compatibility) +-- or equal to 'saas', indicating a non-static build. +-- 3. The selected "domain" must exist in the "Domain" table (many-to-many relation with "Project" via "ProjectDomain") +-- or it must match the "Project.domain" field directly. +-- 4. If 'projectDomain' exists in the JSONB "deployment", it is used as the "domain". +-- If not, the first element of 'domains' in the JSONB "deployment" array is used as the "domain". +-- The function returns the most recent (by "createdAt") valid build. +SELECT + b.id AS "buildId", + b."projectId", + '' as "domainsVirtualId", + p.domain AS "domain", + b."createdAt", + b."publishStatus" +FROM "Build" b +JOIN "Project" p ON b."projectId" = p.id +LEFT JOIN "ProjectDomain" pd ON pd."projectId" = p.id +WHERE b."projectId" = $1.id + AND b.deployment IS NOT NULL + -- 'destination' IS NULL for backward compatibility; 'destination' = 'saas' for non-static builds + AND ((b.deployment::jsonb ->> 'destination') IS NULL OR (b.deployment::jsonb ->> 'destination') = 'saas') + AND ( + -- Check if 'projectDomain' matches p.domain + (b.deployment::jsonb ->> 'projectDomain') = p.domain + -- Check if 'domains' contains p.domain or d.domain + OR (b.deployment::jsonb -> 'domains') @> to_jsonb(array[p.domain]) + ) +ORDER BY b."createdAt" DESC +LIMIT 1; +$$ +STABLE +LANGUAGE sql; + +-- Comment on the function to provide additional context +COMMENT ON FUNCTION "latestProjectDomainBuildVirtual"("Project") IS 'This function computes the latest build for a project domain, ensuring it is a production (non-static) build, where the domain matches either the Project.domain field or exists in the related Domain table. It provides backward compatibility for older records with a missing "destination" field.'; + + + +CREATE OR REPLACE FUNCTION database_cleanup( + from_date timestamp DEFAULT '2020-01-01 00:00:00', + to_date timestamp DEFAULT '2099-12-31 23:59:59' -- SQL should die long before this date! +) RETURNS VOID AS $$ +BEGIN + WITH latest_builds AS ( + SELECT "buildId" FROM "Project" p, LATERAL "latestProjectDomainBuildVirtual"(p) + UNION + SELECT "buildId" FROM "Project" p, LATERAL "latestBuildVirtual"(p) + UNION + SELECT lb."buildId" + FROM "Project" p, LATERAL "domainsVirtual"(p) dv, LATERAL "latestBuildVirtual"(dv) lb + ) + UPDATE "Build" + SET + "styleSources" = '[]'::text, + styles = '[]'::text, + breakpoints = '[]'::text, + "styleSourceSelections" = '[]'::text, + props = '[]'::text, + instances = '[]'::text, + "dataSources" = '[]'::text, + resources = '[]'::text, + "marketplaceProduct" = '{}'::text, + "isCleaned" = TRUE + WHERE deployment IS NOT NULL + AND id NOT IN (SELECT "buildId" FROM latest_builds) + AND "isCleaned" = FALSE + AND "createdAt" BETWEEN from_date AND to_date; -- Filter by date range (for testing purposes) +END; +$$ LANGUAGE plpgsql; + +-- SELECT database_cleanup();