1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as analyticshub from "@distilled.cloud/gcp/analyticshub_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 hubLocation,
9 logLevel,
10 primaryContactOf,
11 currentProject,
12 runLifecycle,
13} from "./common.ts";
14
15const { test } = Test.make({ providers: GCP.providers() });
16
17const waitUntilGone = (parent: string, name: string) =>
18 analyticshub
19 .listProjectsLocationsDataExchangesQueryTemplates({
20 parent,
21 pageSize: 100,
22 })
23 .pipe(
24 Effect.map((page) =>
25 (page.queryTemplates ?? []).some((item) => item.name === name)
26 ? ("found" as const)
27 : ("gone" as const),
28 ),
29 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
30 Effect.repeat({
31 schedule: Schedule.spaced("1 second"),
32 until: (status) => status === "gone",
33 times: 10,
34 }),
35 );
36
37test.provider(
38 "getProjectsLocationsDataExchangesQueryTemplates on a missing template fails with a typed tag",
39 (stack) =>
40 Effect.gen(function* () {
41 const project = yield* currentProject;
42
43 yield* stack.destroy();
44
45 const error = yield* Effect.flip(
46 analyticshub.getProjectsLocationsDataExchangesQueryTemplates({
47 name: `projects/${project}/locations/${hubLocation}/dataExchanges/missing/queryTemplates/alchemy_missing_template`,
48 }),
49 );
50 expect(error._tag).toEqual("NotFound");
51
52 yield* stack.destroy();
53 }).pipe(logLevel),
54 {
55 tags: ["provider:gcp", "provider:gcp:analyticshub", "live"],
56 timeout: 90_000,
57 },
58);
59
60test.provider.skipIf(!runLifecycle)(
61 "query template lifecycle",
62 (stack) =>
63 Effect.gen(function* () {
64 const project = yield* currentProject;
65 const primaryContact = primaryContactOf(project);
66
67 yield* stack.destroy();
68
69 const created = yield* stack.deploy(
70 Effect.gen(function* () {
71 const exchange = yield* GCP.AnalyticsHub.DataExchange("CleanRoom", {
72 location: hubLocation,
73 displayName: "Clean Room",
74 sharingEnvironmentConfig: { dcrExchangeConfig: {} },
75 });
76 const template = yield* GCP.AnalyticsHub.DataExchangesQueryTemplate(
77 "Counts",
78 {
79 dataExchange: exchange.name,
80 location: hubLocation,
81 displayName: "Counts",
82 description: "row counts",
83 primaryContact,
84 routine: {
85 routineType: "TABLE_VALUED_FUNCTION",
86 definitionBody: "SELECT 1 AS n",
87 },
88 },
89 );
90 return { exchange, template };
91 }),
92 );
93
94 expect(created.template.name).toContain("/queryTemplates/");
95 expect(created.template.dataExchange).toEqual(created.exchange.name);
96 expect(created.template.displayName).toEqual("Counts");
97 expect(created.template.description).toEqual("row counts");
98 expect(created.template.routine?.routineType).toEqual(
99 "TABLE_VALUED_FUNCTION",
100 );
101
102 const fetched =
103 yield* analyticshub.getProjectsLocationsDataExchangesQueryTemplates({
104 name: created.template.name,
105 });
106 expect(fetched.name).toEqual(created.template.name);
107 expect(fetched.displayName).toEqual("Counts");
108 expect(fetched.description).toContain("alchemy-id=");
109 expect(fetched.description).toContain("row counts");
110
111 const updated = yield* stack.deploy(
112 Effect.gen(function* () {
113 const exchange = yield* GCP.AnalyticsHub.DataExchange("CleanRoom", {
114 dataExchangeId: created.exchange.dataExchangeId,
115 location: hubLocation,
116 displayName: "Clean Room",
117 sharingEnvironmentConfig: { dcrExchangeConfig: {} },
118 });
119 const template = yield* GCP.AnalyticsHub.DataExchangesQueryTemplate(
120 "Counts",
121 {
122 dataExchange: exchange.name,
123 queryTemplateId: created.template.queryTemplateId,
124 location: hubLocation,
125 displayName: "Counts",
126 description: "updated counts",
127 primaryContact,
128 routine: {
129 routineType: "TABLE_VALUED_FUNCTION",
130 definitionBody: "SELECT 2 AS n",
131 },
132 },
133 );
134 return { exchange, template };
135 }),
136 );
137
138 expect(updated.template.name).toEqual(created.template.name);
139 expect(updated.template.displayName).toEqual("Counts");
140 expect(updated.template.description).toEqual("updated counts");
141
142 const fetchedUpdate =
143 yield* analyticshub.getProjectsLocationsDataExchangesQueryTemplates({
144 name: created.template.name,
145 });
146 expect(fetchedUpdate.displayName).toEqual("Counts");
147 expect(fetchedUpdate.description).toContain("updated counts");
148
149 yield* stack.destroy();
150 const gone = yield* waitUntilGone(
151 created.exchange.name,
152 created.template.name,
153 );
154 expect(gone).toEqual("gone");
155 }).pipe(logLevel),
156 {
157 tags: ["provider:gcp", "provider:gcp:analyticshub", "live"],
158 timeout: 120_000,
159 },
160);