1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as dataplex from "@distilled.cloud/gcp/dataplex_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import { withDataplexSlot } from "./quota.ts";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17const waitUntilGone = (name: string) =>
18 dataplex.getProjectsLocationsEntryGroupsEntries({ name }).pipe(
19 Effect.as("found" as const),
20 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
21 Effect.repeat({
22 schedule: Schedule.spaced("1 second"),
23 until: (status) => status === "gone",
24 times: 10,
25 }),
26 );
27
28test.provider.skipIf(!!process.env.FAST)(
29 "create, update, and delete an entry",
30 (stack) =>
31 Effect.gen(function* () {
32 yield* stack.destroy();
33
34 const created = yield* stack.deploy(
35 Effect.gen(function* () {
36 const type = yield* GCP.Dataplex.EntryType("Kind", {
37 location: "us-central1",
38 displayName: "kind",
39 });
40 const group = yield* GCP.Dataplex.EntryGroup("Catalog", {
41 location: "us-central1",
42 displayName: "catalog",
43 labels: { env: "test" },
44 });
45 const entry = yield* GCP.Dataplex.EntryGroupsEntry("Orders", {
46 entryGroup: group.name,
47 entryType: type.name,
48 fullyQualifiedName: "custom:orders",
49 entrySource: {
50 system: "custom",
51 platform: "alchemy",
52 displayName: "orders a",
53 },
54 labels: { env: "test" },
55 });
56 return { type, group, entry };
57 }),
58 );
59
60 expect(created.entry.name).toContain("/entries/");
61 expect(created.entry.entryGroup).toEqual(created.group.name);
62 expect(created.entry.entryType).toContain(created.type.entryTypeId);
63 expect(created.entry.fullyQualifiedName).toEqual("custom:orders");
64 expect(created.entry.labels).toMatchObject({ env: "test" });
65
66 const fetched = yield* dataplex.getProjectsLocationsEntryGroupsEntries({
67 name: created.entry.name,
68 view: "ALL",
69 });
70 expect(fetched.name).toEqual(created.entry.name);
71 expect(fetched.entryType).toContain(created.type.entryTypeId);
72 expect(fetched.entrySource?.labels?.env).toEqual("test");
73 expect(
74 Object.keys(fetched.entrySource?.labels ?? {}).some((key) =>
75 key.startsWith("alchemy-"),
76 ),
77 ).toEqual(true);
78
79 const updated = yield* stack.deploy(
80 Effect.gen(function* () {
81 const type = yield* GCP.Dataplex.EntryType("Kind", {
82 entryTypeId: created.type.entryTypeId,
83 location: "us-central1",
84 displayName: "kind",
85 });
86 const group = yield* GCP.Dataplex.EntryGroup("Catalog", {
87 entryGroupId: created.group.entryGroupId,
88 location: "us-central1",
89 displayName: "catalog",
90 labels: { env: "test" },
91 });
92 const entry = yield* GCP.Dataplex.EntryGroupsEntry("Orders", {
93 entryGroup: group.name,
94 entryId: created.entry.entryId,
95 entryType: type.name,
96 fullyQualifiedName: "custom:orders.v2",
97 entrySource: {
98 system: "custom",
99 platform: "alchemy",
100 displayName: "orders b",
101 },
102 labels: { env: "prod", team: "data" },
103 });
104 return { type, group, entry };
105 }),
106 );
107
108 expect(updated.entry.name).toEqual(created.entry.name);
109 expect(updated.entry.fullyQualifiedName).toEqual("custom:orders.v2");
110 expect(updated.entry.labels).toMatchObject({ env: "prod", team: "data" });
111
112 yield* stack.destroy();
113 const gone = yield* waitUntilGone(created.entry.name);
114 expect(gone).toEqual("gone");
115 }).pipe(logLevel, withDataplexSlot),
116 { tags: ["provider:gcp", "provider:gcp:dataplex", "live"], timeout: 900_000 },
117);