@cosmicdrift/kumiko-bundled-features 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- package/src/cap-counter/__tests__/{cap-counter.integration.ts → cap-counter.integration.test.ts} +14 -3
- package/src/cap-counter/__tests__/enforce-cap.test.ts +8 -4
- package/src/cap-counter/__tests__/{with-cap-enforcement.integration.ts → with-cap-enforcement.integration.test.ts} +14 -3
- package/src/cap-counter/enforce-cap.ts +2 -4
- package/src/cap-counter/handlers/get-counter.query.ts +1 -3
- package/src/cap-counter/handlers/increment.write.ts +1 -2
- package/src/cap-counter/handlers/mark-soft-warned.write.ts +1 -2
- package/src/channel-in-app/in-app-channel.ts +1 -3
- package/src/custom-fields/__tests__/cross-tenant-field-delete.integration.test.ts +177 -0
- package/src/custom-fields/__tests__/{custom-fields.integration.ts → custom-fields.integration.test.ts} +105 -0
- package/src/custom-fields/db/queries/projection.ts +33 -4
- package/src/custom-fields/db/queries/retention.ts +2 -2
- package/src/custom-fields/db/queries/user-data-rights.ts +6 -3
- package/src/custom-fields/feature.ts +10 -4
- package/src/custom-fields/handlers/delete-system-field.write.ts +5 -1
- package/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
- package/src/custom-fields/handlers/set-custom-field.write.ts +33 -17
- package/src/custom-fields/lib/field-access.ts +39 -14
- package/src/custom-fields/lib/value-schema.ts +45 -0
- package/src/custom-fields/run-retention.ts +1 -1
- package/src/custom-fields/wire-for-entity.ts +22 -4
- package/src/custom-fields/wire-user-data-rights.ts +3 -2
- package/src/delivery/delivery-service.ts +1 -1
- package/src/delivery/types.ts +2 -2
- package/src/feature-toggles/__tests__/{feature-toggles.integration.ts → feature-toggles.integration.test.ts} +6 -6
- package/src/feature-toggles/handlers/set.write.ts +10 -8
- package/src/subscription-stripe/__tests__/{stripe-foundation.integration.ts → stripe-foundation.integration.test.ts} +7 -10
- package/src/tier-engine/__tests__/{resolver.integration.ts → resolver.integration.test.ts} +4 -3
- package/src/user-data-rights/__tests__/{audit-log.integration.ts → audit-log.integration.test.ts} +12 -5
- package/src/user-data-rights/__tests__/{cross-data-matrix.integration.ts → cross-data-matrix.integration.test.ts} +29 -12
- package/src/user-data-rights/__tests__/{download.integration.ts → download.integration.test.ts} +15 -7
- package/src/user-data-rights/__tests__/{export-job-idempotency.integration.ts → export-job-idempotency.integration.test.ts} +13 -11
- package/src/user-data-rights/__tests__/{request-cancel-deletion.integration.ts → request-cancel-deletion.integration.test.ts} +8 -7
- package/src/user-data-rights/__tests__/{request-deletion-callback.integration.ts → request-deletion-callback.integration.test.ts} +8 -5
- package/src/user-data-rights/__tests__/{request-export.integration.ts → request-export.integration.test.ts} +6 -3
- package/src/user-data-rights/__tests__/{restriction-flow.integration.ts → restriction-flow.integration.test.ts} +11 -8
- package/src/user-data-rights/__tests__/{run-export-jobs.integration.ts → run-export-jobs.integration.test.ts} +25 -13
- package/src/user-data-rights/__tests__/{run-forget-cleanup.integration.ts → run-forget-cleanup.integration.test.ts} +6 -3
- package/src/user-data-rights/__tests__/{run-user-export.integration.ts → run-user-export.integration.test.ts} +6 -3
- package/src/user-data-rights/__tests__/{user-data-rights.integration.ts → user-data-rights.integration.test.ts} +3 -1
- package/src/user-data-rights/db/queries/export-jobs.ts +6 -5
- package/src/user-data-rights/db/queries/forget-cleanup.ts +11 -6
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +5 -10
- package/src/user-data-rights/handlers/export-status.query.ts +12 -12
- package/src/user-data-rights/run-export-jobs.ts +2 -5
- package/src/user-data-rights/run-forget-cleanup.ts +0 -1
- package/src/user-data-rights-defaults/__tests__/{user-data-rights-defaults.integration.ts → user-data-rights-defaults.integration.test.ts} +2 -0
- /package/src/__tests__/{es-ops-e2e.integration.ts → es-ops-e2e.integration.test.ts} +0 -0
- /package/src/audit/__tests__/{audit.integration.ts → audit.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{account-lockout-no-redis.integration.ts → account-lockout-no-redis.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{account-lockout.integration.ts → account-lockout.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{auth-claims.integration.ts → auth-claims.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{auth.integration.ts → auth.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{email-verification.integration.ts → email-verification.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{identity-v3-login.integration.ts → identity-v3-login.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{invite-flow.integration.ts → invite-flow.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{multi-roles.integration.ts → multi-roles.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{password-reset.integration.ts → password-reset.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{public-routes-rate-limit.integration.ts → public-routes-rate-limit.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{seed-admin.integration.ts → seed-admin.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{session-callbacks.integration.ts → session-callbacks.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{session-strict-mode.integration.ts → session-strict-mode.integration.test.ts} +0 -0
- /package/src/auth-email-password/__tests__/{signup-flow.integration.ts → signup-flow.integration.test.ts} +0 -0
- /package/src/billing-foundation/__tests__/{billing-foundation.integration.ts → billing-foundation.integration.test.ts} +0 -0
- /package/src/compliance-profiles/__tests__/{compliance-profiles.integration.ts → compliance-profiles.integration.test.ts} +0 -0
- /package/src/compliance-profiles/__tests__/{seeding.integration.ts → seeding.integration.test.ts} +0 -0
- /package/src/config/__tests__/{cascade.integration.ts → cascade.integration.test.ts} +0 -0
- /package/src/config/__tests__/{config.integration.ts → config.integration.test.ts} +0 -0
- /package/src/custom-fields/__tests__/{audit-integration.integration.ts → audit-integration.integration.test.ts} +0 -0
- /package/src/custom-fields/__tests__/{field-access.integration.ts → field-access.integration.test.ts} +0 -0
- /package/src/custom-fields/__tests__/{quota.integration.ts → quota.integration.test.ts} +0 -0
- /package/src/custom-fields/__tests__/{retention.integration.ts → retention.integration.test.ts} +0 -0
- /package/src/custom-fields/__tests__/{user-data-rights.integration.ts → user-data-rights.integration.test.ts} +0 -0
- /package/src/data-retention/__tests__/{data-retention.integration.ts → data-retention.integration.test.ts} +0 -0
- /package/src/data-retention/__tests__/{policy-for.integration.ts → policy-for.integration.test.ts} +0 -0
- /package/src/delivery/__tests__/{delivery-events.integration.ts → delivery-events.integration.test.ts} +0 -0
- /package/src/delivery/__tests__/{delivery.integration.ts → delivery.integration.test.ts} +0 -0
- /package/src/file-foundation/__tests__/{file-foundation.integration.ts → file-foundation.integration.test.ts} +0 -0
- /package/src/files/__tests__/{files.integration.ts → files.integration.test.ts} +0 -0
- /package/src/files-provider-s3/__tests__/{s3-provider.integration.ts → s3-provider.integration.test.ts} +0 -0
- /package/src/jobs/__tests__/{job-system-user.integration.ts → job-system-user.integration.test.ts} +0 -0
- /package/src/jobs/__tests__/{jobs-events.integration.ts → jobs-events.integration.test.ts} +0 -0
- /package/src/jobs/__tests__/{jobs-feature.integration.ts → jobs-feature.integration.test.ts} +0 -0
- /package/src/legal-pages/__tests__/{legal-pages.integration.ts → legal-pages.integration.test.ts} +0 -0
- /package/src/mail-foundation/__tests__/{mail-foundation.integration.ts → mail-foundation.integration.test.ts} +0 -0
- /package/src/rate-limiting/__tests__/{rate-limiting.integration.ts → rate-limiting.integration.test.ts} +0 -0
- /package/src/renderer-foundation/__tests__/{collect-plugins.integration.ts → collect-plugins.integration.test.ts} +0 -0
- /package/src/secrets/__tests__/{rotate.integration.ts → rotate.integration.test.ts} +0 -0
- /package/src/secrets/__tests__/{secrets-events.integration.ts → secrets-events.integration.test.ts} +0 -0
- /package/src/secrets/__tests__/{secrets.integration.ts → secrets.integration.test.ts} +0 -0
- /package/src/sessions/__tests__/{cleanup.integration.ts → cleanup.integration.test.ts} +0 -0
- /package/src/sessions/__tests__/{password-auto-revoke.integration.ts → password-auto-revoke.integration.test.ts} +0 -0
- /package/src/sessions/__tests__/{sessions.integration.ts → sessions.integration.test.ts} +0 -0
- /package/src/subscription-mollie/__tests__/{mollie-foundation.integration.ts → mollie-foundation.integration.test.ts} +0 -0
- /package/src/template-resolver/__tests__/{handlers.integration.ts → handlers.integration.test.ts} +0 -0
- /package/src/template-resolver/__tests__/{template-resolver.integration.ts → template-resolver.integration.test.ts} +0 -0
- /package/src/tenant/__tests__/{multi-tenant.integration.ts → multi-tenant.integration.test.ts} +0 -0
- /package/src/tenant/__tests__/{seed-testing.integration.ts → seed-testing.integration.test.ts} +0 -0
- /package/src/tenant/__tests__/{tenant.integration.ts → tenant.integration.test.ts} +0 -0
- /package/src/text-content/__tests__/{text-content.integration.ts → text-content.integration.test.ts} +0 -0
- /package/src/tier-engine/__tests__/{auto-default-tier.integration.ts → auto-default-tier.integration.test.ts} +0 -0
- /package/src/tier-engine/__tests__/{tier-engine.integration.ts → tier-engine.integration.test.ts} +0 -0
- /package/src/user/__tests__/{seed-testing.integration.ts → seed-testing.integration.test.ts} +0 -0
- /package/src/user/__tests__/{user.integration.ts → user.integration.test.ts} +0 -0
|
@@ -16,14 +16,17 @@ import {
|
|
|
16
16
|
selectMany,
|
|
17
17
|
updateMany,
|
|
18
18
|
} from "@cosmicdrift/kumiko-framework/bun-db";
|
|
19
|
+
import { extractPgError } from "@cosmicdrift/kumiko-framework/db";
|
|
19
20
|
import {
|
|
20
21
|
setupTestStack,
|
|
21
22
|
type TestStack,
|
|
22
23
|
unsafeCreateEntityTable,
|
|
23
24
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
25
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
24
26
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
25
27
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
26
28
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
29
|
+
import { createSessionsFeature } from "../../sessions";
|
|
27
30
|
import { createUserFeature } from "../../user";
|
|
28
31
|
import { createUserDataRightsFeature } from "../feature";
|
|
29
32
|
import {
|
|
@@ -45,6 +48,8 @@ beforeAll(async () => {
|
|
|
45
48
|
createUserFeature(),
|
|
46
49
|
createDataRetentionFeature(),
|
|
47
50
|
createComplianceProfilesFeature(),
|
|
51
|
+
createSessionsFeature(),
|
|
52
|
+
|
|
48
53
|
createUserDataRightsFeature(),
|
|
49
54
|
],
|
|
50
55
|
});
|
|
@@ -57,18 +62,15 @@ afterAll(async () => {
|
|
|
57
62
|
});
|
|
58
63
|
|
|
59
64
|
beforeEach(async () => {
|
|
60
|
-
await
|
|
65
|
+
await resetTestTables(stack.db, [exportJobsTable]);
|
|
61
66
|
});
|
|
62
67
|
|
|
63
68
|
const NOW = () => getTemporal().Now.instant();
|
|
64
69
|
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// Constraint pinst, nicht zufaellig eine andere unique-violation
|
|
70
|
-
// (z.B. UUID-Kollision auf id-PK durch wiederholtes Test-Setup) —
|
|
71
|
-
// silent-Pass durch fremden Constraint waere falsche Bestaetigung.
|
|
70
|
+
// postgres-js wirft PostgresError direkt; Drizzle wrappt in .cause.
|
|
71
|
+
// extractPgError normalisiert beide Shapes. Constraint-Name pinnen damit
|
|
72
|
+
// der Test nur unsere Idempotency-Constraint pinst, nicht zufaellig eine
|
|
73
|
+
// andere unique-violation (z.B. UUID-Kollision auf id-PK).
|
|
72
74
|
async function expectUniqueViolation(
|
|
73
75
|
promise: Promise<unknown>,
|
|
74
76
|
expectedConstraint: string,
|
|
@@ -80,9 +82,9 @@ async function expectUniqueViolation(
|
|
|
80
82
|
caught = e;
|
|
81
83
|
}
|
|
82
84
|
expect(caught).toBeDefined();
|
|
83
|
-
const
|
|
84
|
-
expect(
|
|
85
|
-
expect(
|
|
85
|
+
const pg = extractPgError(caught);
|
|
86
|
+
expect(pg?.code).toBe("23505");
|
|
87
|
+
expect(pg?.constraint_name).toBe(expectedConstraint);
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
// Re-Export aus dem Schema — Single source of truth, Rename hier
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
// hier; der Frist-Ablauf-Cleanup folgt mit S2.U5b.
|
|
11
11
|
|
|
12
12
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
13
|
-
import {
|
|
14
|
-
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
13
|
+
import { insertOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
14
|
+
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
15
15
|
import {
|
|
16
16
|
createTestUser,
|
|
17
17
|
setupTestStack,
|
|
@@ -20,12 +20,15 @@ import {
|
|
|
20
20
|
testUserId,
|
|
21
21
|
unsafeCreateEntityTable,
|
|
22
22
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
23
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
23
24
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
24
25
|
import {
|
|
25
26
|
createComplianceProfilesFeature,
|
|
26
27
|
tenantComplianceProfileEntity,
|
|
28
|
+
tenantComplianceProfileTable,
|
|
27
29
|
} from "../../compliance-profiles";
|
|
28
30
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
31
|
+
import { createSessionsFeature } from "../../sessions";
|
|
29
32
|
import { USER_STATUS, userEntity, userTable } from "../../user";
|
|
30
33
|
import { createUserFeature } from "../../user/feature";
|
|
31
34
|
import { createUserDataRightsFeature } from "../feature";
|
|
@@ -54,6 +57,8 @@ const features = [
|
|
|
54
57
|
createUserFeature(),
|
|
55
58
|
createDataRetentionFeature(),
|
|
56
59
|
createComplianceProfilesFeature(),
|
|
60
|
+
createSessionsFeature(),
|
|
61
|
+
|
|
57
62
|
createUserDataRightsFeature(),
|
|
58
63
|
];
|
|
59
64
|
|
|
@@ -69,11 +74,7 @@ afterAll(async () => {
|
|
|
69
74
|
});
|
|
70
75
|
|
|
71
76
|
beforeEach(async () => {
|
|
72
|
-
|
|
73
|
-
// wuerde sonst row-state aus voherigen Tests einschleppen.
|
|
74
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
|
|
75
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_compliance_profiles`);
|
|
76
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM kumiko_events`);
|
|
77
|
+
await resetTestTables(stack.db, [userTable, tenantComplianceProfileTable, eventsTable]);
|
|
77
78
|
});
|
|
78
79
|
|
|
79
80
|
// gracePeriodEnd ist `instant()` (Temporal.Instant in JS). Nicht JS-Date —
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// Properties — dieser Test verifiziert sie end-to-end.
|
|
9
9
|
|
|
10
10
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
-
import {
|
|
12
|
-
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
11
|
+
import { insertOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
12
|
+
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
13
13
|
import {
|
|
14
14
|
createTestUser,
|
|
15
15
|
setupTestStack,
|
|
@@ -17,11 +17,14 @@ import {
|
|
|
17
17
|
testTenantId,
|
|
18
18
|
unsafeCreateEntityTable,
|
|
19
19
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
20
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
20
21
|
import {
|
|
21
22
|
createComplianceProfilesFeature,
|
|
22
23
|
tenantComplianceProfileEntity,
|
|
24
|
+
tenantComplianceProfileTable,
|
|
23
25
|
} from "../../compliance-profiles";
|
|
24
26
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
27
|
+
import { createSessionsFeature } from "../../sessions";
|
|
25
28
|
import { USER_STATUS, userEntity, userTable } from "../../user";
|
|
26
29
|
import { createUserFeature } from "../../user/feature";
|
|
27
30
|
import { createUserDataRightsFeature } from "../feature";
|
|
@@ -61,6 +64,8 @@ beforeAll(async () => {
|
|
|
61
64
|
createUserFeature(),
|
|
62
65
|
createDataRetentionFeature(),
|
|
63
66
|
createComplianceProfilesFeature(),
|
|
67
|
+
createSessionsFeature(),
|
|
68
|
+
|
|
64
69
|
createUserDataRightsFeature({ sendDeletionRequestedEmail }),
|
|
65
70
|
],
|
|
66
71
|
});
|
|
@@ -76,9 +81,7 @@ afterAll(async () => {
|
|
|
76
81
|
beforeEach(async () => {
|
|
77
82
|
state.calls = [];
|
|
78
83
|
state.shouldThrow = false;
|
|
79
|
-
await
|
|
80
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_compliance_profiles`);
|
|
81
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM kumiko_events`);
|
|
84
|
+
await resetTestTables(stack.db, [userTable, tenantComplianceProfileTable, eventsTable]);
|
|
82
85
|
});
|
|
83
86
|
|
|
84
87
|
async function seedAlice(email: string = "alice@example.com"): Promise<void> {
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
selectMany,
|
|
25
25
|
updateMany,
|
|
26
26
|
} from "@cosmicdrift/kumiko-framework/bun-db";
|
|
27
|
-
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
27
|
+
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
28
28
|
import {
|
|
29
29
|
createTestUser,
|
|
30
30
|
setupTestStack,
|
|
@@ -32,9 +32,11 @@ import {
|
|
|
32
32
|
testTenantId,
|
|
33
33
|
unsafeCreateEntityTable,
|
|
34
34
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
35
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
35
36
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
36
37
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
37
38
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
39
|
+
import { createSessionsFeature } from "../../sessions";
|
|
38
40
|
import { createUserFeature } from "../../user";
|
|
39
41
|
import { createUserDataRightsFeature } from "../feature";
|
|
40
42
|
import { EXPORT_JOB_STATUS, exportJobEntity, exportJobsTable } from "../schema/export-job";
|
|
@@ -56,6 +58,8 @@ beforeAll(async () => {
|
|
|
56
58
|
createUserFeature(),
|
|
57
59
|
createDataRetentionFeature(),
|
|
58
60
|
createComplianceProfilesFeature(),
|
|
61
|
+
createSessionsFeature(),
|
|
62
|
+
|
|
59
63
|
createUserDataRightsFeature(),
|
|
60
64
|
],
|
|
61
65
|
});
|
|
@@ -68,8 +72,7 @@ afterAll(async () => {
|
|
|
68
72
|
});
|
|
69
73
|
|
|
70
74
|
beforeEach(async () => {
|
|
71
|
-
await
|
|
72
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM kumiko_events`);
|
|
75
|
+
await resetTestTables(stack.db, [exportJobsTable, eventsTable]);
|
|
73
76
|
});
|
|
74
77
|
|
|
75
78
|
type RequestExportResponse = {
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
|
|
13
13
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
14
14
|
import { randomBytes } from "node:crypto";
|
|
15
|
-
import {
|
|
15
|
+
import { selectMany, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
16
16
|
import { createEncryptionProvider } from "@cosmicdrift/kumiko-framework/db";
|
|
17
17
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
18
|
-
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
18
|
+
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
19
19
|
import {
|
|
20
20
|
setupTestStack,
|
|
21
21
|
type TestStack,
|
|
@@ -24,13 +24,14 @@ import {
|
|
|
24
24
|
unsafeCreateEntityTable,
|
|
25
25
|
unsafePushTables,
|
|
26
26
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
27
|
-
import { createLateBoundHolder } from "@cosmicdrift/kumiko-framework/testing";
|
|
27
|
+
import { createLateBoundHolder, resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
28
28
|
import { AuthErrors, AuthHandlers } from "../../auth-email-password/constants";
|
|
29
29
|
import { createAuthEmailPasswordFeature } from "../../auth-email-password/feature";
|
|
30
30
|
import { hashPassword } from "../../auth-email-password/password-hashing";
|
|
31
31
|
import {
|
|
32
32
|
createComplianceProfilesFeature,
|
|
33
33
|
tenantComplianceProfileEntity,
|
|
34
|
+
tenantComplianceProfileTable,
|
|
34
35
|
} from "../../compliance-profiles";
|
|
35
36
|
import { createConfigFeature } from "../../config";
|
|
36
37
|
import { createConfigResolver } from "../../config/resolver";
|
|
@@ -97,11 +98,13 @@ afterAll(async () => {
|
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
beforeEach(async () => {
|
|
100
|
-
await
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
await resetTestTables(stack.db, [
|
|
102
|
+
userSessionTable,
|
|
103
|
+
userTable,
|
|
104
|
+
tenantMembershipsTable,
|
|
105
|
+
tenantComplianceProfileTable,
|
|
106
|
+
eventsTable,
|
|
107
|
+
]);
|
|
105
108
|
});
|
|
106
109
|
|
|
107
110
|
async function seedAliceWithMembership(
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
selectMany,
|
|
24
24
|
updateMany,
|
|
25
25
|
} from "@cosmicdrift/kumiko-framework/bun-db";
|
|
26
|
-
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
26
|
+
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
27
27
|
import {
|
|
28
28
|
createInMemoryFileProvider,
|
|
29
29
|
type FileStorageProvider,
|
|
@@ -35,12 +35,16 @@ import {
|
|
|
35
35
|
testTenantId,
|
|
36
36
|
unsafeCreateEntityTable,
|
|
37
37
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
38
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
38
39
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
39
40
|
import {
|
|
40
41
|
createComplianceProfilesFeature,
|
|
41
42
|
tenantComplianceProfileEntity,
|
|
43
|
+
tenantComplianceProfileTable,
|
|
42
44
|
} from "../../compliance-profiles";
|
|
43
45
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
46
|
+
import { createSessionsFeature } from "../../sessions";
|
|
47
|
+
import { tenantMembershipsTable } from "../../tenant";
|
|
44
48
|
import { createUserFeature, USER_STATUS, userEntity, userTable } from "../../user";
|
|
45
49
|
import { createUserDataRightsFeature } from "../feature";
|
|
46
50
|
import { runExportJobs } from "../run-export-jobs";
|
|
@@ -60,6 +64,8 @@ beforeAll(async () => {
|
|
|
60
64
|
createUserFeature(),
|
|
61
65
|
createDataRetentionFeature(),
|
|
62
66
|
createComplianceProfilesFeature(),
|
|
67
|
+
createSessionsFeature(),
|
|
68
|
+
|
|
63
69
|
createUserDataRightsFeature(),
|
|
64
70
|
],
|
|
65
71
|
});
|
|
@@ -94,12 +100,14 @@ afterAll(async () => {
|
|
|
94
100
|
});
|
|
95
101
|
|
|
96
102
|
beforeEach(async () => {
|
|
97
|
-
await
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
await resetTestTables(stack.db, [
|
|
104
|
+
exportDownloadTokensTable,
|
|
105
|
+
exportJobsTable,
|
|
106
|
+
userTable,
|
|
107
|
+
eventsTable,
|
|
108
|
+
tenantComplianceProfileTable,
|
|
109
|
+
tenantMembershipsTable,
|
|
110
|
+
]);
|
|
103
111
|
providerPerTenant = new Map();
|
|
104
112
|
|
|
105
113
|
// Atom 5: aliceUser-Row mit email seeden — Worker-Notification-Callback
|
|
@@ -734,6 +742,8 @@ describe("runExportJobs :: Atom 3c file-binaries", () => {
|
|
|
734
742
|
createUserFeature(),
|
|
735
743
|
createDataRetentionFeature(),
|
|
736
744
|
createComplianceProfilesFeature(),
|
|
745
|
+
createSessionsFeature(),
|
|
746
|
+
|
|
737
747
|
createUserDataRightsFeature(),
|
|
738
748
|
testFileExporter,
|
|
739
749
|
],
|
|
@@ -767,11 +777,13 @@ describe("runExportJobs :: Atom 3c file-binaries", () => {
|
|
|
767
777
|
|
|
768
778
|
beforeEach(async () => {
|
|
769
779
|
if (!localStack) return;
|
|
770
|
-
await
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
780
|
+
await resetTestTables(localStack.db, [
|
|
781
|
+
exportDownloadTokensTable,
|
|
782
|
+
exportJobsTable,
|
|
783
|
+
eventsTable,
|
|
784
|
+
tenantComplianceProfileTable,
|
|
785
|
+
tenantMembershipsTable,
|
|
786
|
+
]);
|
|
775
787
|
// Reset zu safe Default damit kein Test den State an den naechsten leakt.
|
|
776
788
|
currentTestFileName = "report.pdf";
|
|
777
789
|
});
|
|
@@ -1033,7 +1045,7 @@ describe("runExportJobs :: Atom 5 notification-callbacks", () => {
|
|
|
1033
1045
|
test("user ohne email → Callback skipped + console.warn (kein Throw)", async () => {
|
|
1034
1046
|
// User-Row mit email=null seeden (override). Worker logged warn,
|
|
1035
1047
|
// Callback wird NICHT gerufen, Worker-Run bleibt successful.
|
|
1036
|
-
await
|
|
1048
|
+
await resetTestTables(stack.db, [userTable]);
|
|
1037
1049
|
await insertOne(stack.db, userTable, {
|
|
1038
1050
|
id: String(aliceUser.id),
|
|
1039
1051
|
tenantId: tenantA,
|
|
@@ -17,15 +17,18 @@
|
|
|
17
17
|
|
|
18
18
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
19
19
|
import { asRawClient, insertOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
20
|
+
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
20
21
|
import {
|
|
21
22
|
setupTestStack,
|
|
22
23
|
type TestStack,
|
|
23
24
|
unsafeCreateEntityTable,
|
|
24
25
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
26
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
25
27
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
26
28
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
27
29
|
import { createDataRetentionFeature, tenantRetentionOverrideEntity } from "../../data-retention";
|
|
28
30
|
import { createFilesFeature } from "../../files";
|
|
31
|
+
import { createSessionsFeature } from "../../sessions";
|
|
29
32
|
import {
|
|
30
33
|
createUserFeature,
|
|
31
34
|
USER_ANONYMIZED_DISPLAY_NAME,
|
|
@@ -61,6 +64,8 @@ beforeAll(async () => {
|
|
|
61
64
|
createFilesFeature(),
|
|
62
65
|
createDataRetentionFeature(),
|
|
63
66
|
createComplianceProfilesFeature(),
|
|
67
|
+
createSessionsFeature(),
|
|
68
|
+
|
|
64
69
|
createUserDataRightsFeature(),
|
|
65
70
|
createUserDataRightsDefaultsFeature(),
|
|
66
71
|
],
|
|
@@ -109,9 +114,7 @@ afterAll(async () => {
|
|
|
109
114
|
});
|
|
110
115
|
|
|
111
116
|
beforeEach(async () => {
|
|
112
|
-
await
|
|
113
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_memberships`);
|
|
114
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM file_refs`);
|
|
117
|
+
await resetTestTables(stack.db, [userTable, "read_tenant_memberships", fileRefsTable]);
|
|
115
118
|
});
|
|
116
119
|
|
|
117
120
|
type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
@@ -16,15 +16,18 @@
|
|
|
16
16
|
|
|
17
17
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
18
18
|
import { asRawClient, insertOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
19
|
+
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
19
20
|
import {
|
|
20
21
|
setupTestStack,
|
|
21
22
|
type TestStack,
|
|
22
23
|
unsafeCreateEntityTable,
|
|
23
24
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
25
|
+
import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
24
26
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
25
27
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
26
28
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
27
29
|
import { createFilesFeature } from "../../files";
|
|
30
|
+
import { createSessionsFeature } from "../../sessions";
|
|
28
31
|
import { createUserFeature, USER_STATUS, userEntity, userTable } from "../../user";
|
|
29
32
|
import { createUserDataRightsDefaultsFeature } from "../../user-data-rights-defaults";
|
|
30
33
|
import { createUserDataRightsFeature } from "../feature";
|
|
@@ -51,6 +54,8 @@ beforeAll(async () => {
|
|
|
51
54
|
createFilesFeature(),
|
|
52
55
|
createDataRetentionFeature(),
|
|
53
56
|
createComplianceProfilesFeature(),
|
|
57
|
+
createSessionsFeature(),
|
|
58
|
+
|
|
54
59
|
createUserDataRightsFeature(),
|
|
55
60
|
createUserDataRightsDefaultsFeature(),
|
|
56
61
|
],
|
|
@@ -96,9 +101,7 @@ afterAll(async () => {
|
|
|
96
101
|
});
|
|
97
102
|
|
|
98
103
|
beforeEach(async () => {
|
|
99
|
-
await
|
|
100
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_memberships`);
|
|
101
|
-
await asRawClient(stack.db).unsafe(`DELETE FROM file_refs`);
|
|
104
|
+
await resetTestTables(stack.db, [userTable, "read_tenant_memberships", fileRefsTable]);
|
|
102
105
|
});
|
|
103
106
|
|
|
104
107
|
const NOW = () => getTemporal().Now.instant();
|
|
@@ -10,6 +10,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
|
10
10
|
import { setupTestStack, type TestStack } from "@cosmicdrift/kumiko-framework/stack";
|
|
11
11
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
12
12
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
13
|
+
import { createSessionsFeature } from "../../sessions";
|
|
13
14
|
import { createUserFeature } from "../../user";
|
|
14
15
|
import { createUserDataRightsFeature } from "../feature";
|
|
15
16
|
|
|
@@ -18,11 +19,12 @@ let stack: TestStack;
|
|
|
18
19
|
const userFeature = createUserFeature();
|
|
19
20
|
const dataRetention = createDataRetentionFeature();
|
|
20
21
|
const complianceProfiles = createComplianceProfilesFeature();
|
|
22
|
+
const sessionsFeature = createSessionsFeature();
|
|
21
23
|
const userDataRights = createUserDataRightsFeature();
|
|
22
24
|
|
|
23
25
|
beforeAll(async () => {
|
|
24
26
|
stack = await setupTestStack({
|
|
25
|
-
features: [userFeature, dataRetention, complianceProfiles, userDataRights],
|
|
27
|
+
features: [userFeature, sessionsFeature, dataRetention, complianceProfiles, userDataRights],
|
|
26
28
|
});
|
|
27
29
|
});
|
|
28
30
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import { exportJobsTable } from "../../schema/export-job";
|
|
4
5
|
|
|
5
6
|
export type ExportJobCleanupCandidate = {
|
|
6
7
|
readonly id: string;
|
|
@@ -16,8 +17,8 @@ export async function selectExportJobsForStorageCleanup(
|
|
|
16
17
|
doneStatus: string,
|
|
17
18
|
failedStatus: string,
|
|
18
19
|
): Promise<readonly ExportJobCleanupCandidate[]> {
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
);
|
|
20
|
+
return selectMany<ExportJobCleanupCandidate>(db, exportJobsTable, {
|
|
21
|
+
status: { in: [doneStatus, failedStatus] },
|
|
22
|
+
downloadStorageKey: { ne: null },
|
|
23
|
+
});
|
|
23
24
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
+
import { userTable } from "../../../user";
|
|
3
4
|
|
|
4
5
|
export async function selectUsersDueForForgetCleanup(
|
|
5
6
|
db: DbConnection,
|
|
6
7
|
status: string,
|
|
7
|
-
|
|
8
|
+
gracePeriodEndCutoff: Temporal.Instant | string,
|
|
8
9
|
): Promise<readonly { id: string }[]> {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const cutoff =
|
|
11
|
+
typeof gracePeriodEndCutoff === "string"
|
|
12
|
+
? Temporal.Instant.from(gracePeriodEndCutoff)
|
|
13
|
+
: gracePeriodEndCutoff;
|
|
14
|
+
return selectMany<{ id: string }>(db, userTable, {
|
|
15
|
+
status,
|
|
16
|
+
gracePeriodEnd: { lte: cutoff },
|
|
17
|
+
});
|
|
13
18
|
}
|
|
@@ -23,7 +23,7 @@ export const cancelDeletionWrite = defineWriteHandler({
|
|
|
23
23
|
// ctx.db.raw (kein TenantDb-Wrapper) weil User-Entity tenant-agnostisch
|
|
24
24
|
// ist — siehe request-deletion.write.ts fuer die Begruendung. Cancel
|
|
25
25
|
// muss aus jedem Tenant-Mode den User finden + zuruecksetzen koennen.
|
|
26
|
-
const row = await fetchOne<{ status: string;
|
|
26
|
+
const row = await fetchOne<{ status: string; gracePeriodEnd: Temporal.Instant | null }>(
|
|
27
27
|
ctx.db.raw,
|
|
28
28
|
userTable,
|
|
29
29
|
{ id: event.user.id },
|
|
@@ -37,26 +37,21 @@ export const cancelDeletionWrite = defineWriteHandler({
|
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
if (row
|
|
40
|
+
if (row.status !== USER_STATUS.DeletionRequested) {
|
|
41
41
|
return writeFailure(
|
|
42
42
|
new UnprocessableError("no_pending_deletion", {
|
|
43
43
|
details: {
|
|
44
44
|
reason: "no_pending_deletion",
|
|
45
|
-
currentStatus: row
|
|
45
|
+
currentStatus: row.status,
|
|
46
46
|
},
|
|
47
47
|
}),
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
// from bun-db boundary) against current server clock.
|
|
53
|
-
const gracePeriodEnd = row["grace_period_end"];
|
|
51
|
+
const gracePeriodEnd = row.gracePeriodEnd;
|
|
54
52
|
const inGrace =
|
|
55
53
|
gracePeriodEnd != null &&
|
|
56
|
-
Temporal.Instant.compare(
|
|
57
|
-
gracePeriodEnd as unknown as Temporal.Instant,
|
|
58
|
-
Temporal.Now.instant(),
|
|
59
|
-
) > 0;
|
|
54
|
+
Temporal.Instant.compare(gracePeriodEnd, Temporal.Now.instant()) > 0;
|
|
60
55
|
|
|
61
56
|
if (!inGrace) {
|
|
62
57
|
return writeFailure(
|
|
@@ -22,11 +22,11 @@ type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
|
22
22
|
type ExportJobRow = {
|
|
23
23
|
readonly id: string;
|
|
24
24
|
readonly status: string;
|
|
25
|
-
readonly
|
|
26
|
-
readonly
|
|
27
|
-
readonly
|
|
28
|
-
readonly
|
|
29
|
-
readonly
|
|
25
|
+
readonly requestedAt: Instant;
|
|
26
|
+
readonly completedAt: Instant | null;
|
|
27
|
+
readonly expiresAt: Instant | null;
|
|
28
|
+
readonly errorMessage: string | null;
|
|
29
|
+
readonly bytesWritten: number | null;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export const exportStatusQuery = defineQueryHandler({
|
|
@@ -49,13 +49,13 @@ export const exportStatusQuery = defineQueryHandler({
|
|
|
49
49
|
return {
|
|
50
50
|
hasJob: true as const,
|
|
51
51
|
job: {
|
|
52
|
-
id: latest
|
|
53
|
-
status: latest
|
|
54
|
-
requestedAt: latest
|
|
55
|
-
completedAt: latest
|
|
56
|
-
expiresAt: latest
|
|
57
|
-
errorMessage: latest
|
|
58
|
-
bytesWritten: latest
|
|
52
|
+
id: latest.id,
|
|
53
|
+
status: latest.status,
|
|
54
|
+
requestedAt: latest.requestedAt.toString(),
|
|
55
|
+
completedAt: latest.completedAt?.toString() ?? null,
|
|
56
|
+
expiresAt: latest.expiresAt?.toString() ?? null,
|
|
57
|
+
errorMessage: latest.errorMessage,
|
|
58
|
+
bytesWritten: latest.bytesWritten,
|
|
59
59
|
},
|
|
60
60
|
};
|
|
61
61
|
},
|
|
@@ -595,11 +595,8 @@ async function storageCleanupPass(args: {
|
|
|
595
595
|
// → Trade-off zugunsten DSGVO entschieden. Wenn ein Operator forensik
|
|
596
596
|
// braucht, muss er das vor dem Cleanup-Pass capturen (out-of-band).
|
|
597
597
|
//
|
|
598
|
-
// **SQL-Filter:**
|
|
599
|
-
//
|
|
600
|
-
// done-jobs nach 30 Tagen) reduziert das den Worker-Roundtrip drastisch.
|
|
601
|
-
//
|
|
602
|
-
// or() + isNotNull(): no bun-db helper covers this combination — raw SQL.
|
|
598
|
+
// **SQL-Filter:** status IN (done, failed) + downloadStorageKey IS NOT NULL
|
|
599
|
+
// via selectMany (db/queries/export-jobs.ts).
|
|
603
600
|
const candidates = await selectExportJobsForStorageCleanup(
|
|
604
601
|
db,
|
|
605
602
|
EXPORT_JOB_STATUS.Done,
|
|
@@ -112,7 +112,6 @@ export async function runForgetCleanup(
|
|
|
112
112
|
const { db, registry, now, sendDeletionExecutedEmail } = args;
|
|
113
113
|
|
|
114
114
|
// Step 1: Find users with expired grace period.
|
|
115
|
-
// lte with Instant: no bun-db operator covers this — raw SQL.
|
|
116
115
|
const dueUsers = await selectUsersDueForForgetCleanup(
|
|
117
116
|
db,
|
|
118
117
|
USER_STATUS.DeletionRequested,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
21
21
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
22
22
|
import { createFilesFeature } from "../../files";
|
|
23
|
+
import { createSessionsFeature } from "../../sessions";
|
|
23
24
|
import {
|
|
24
25
|
createUserFeature,
|
|
25
26
|
USER_ANONYMIZED_DISPLAY_NAME,
|
|
@@ -39,6 +40,7 @@ const features = [
|
|
|
39
40
|
createFilesFeature(),
|
|
40
41
|
createDataRetentionFeature(),
|
|
41
42
|
createComplianceProfilesFeature(),
|
|
43
|
+
createSessionsFeature(),
|
|
42
44
|
createUserDataRightsFeature(),
|
|
43
45
|
createUserDataRightsDefaultsFeature(),
|
|
44
46
|
];
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|