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 currentProject,
11 runLifecycle,
12} from "./common.ts";
13
14const { test } = Test.make({ providers: GCP.providers() });
15
16const waitUntilGone = (parent: string, name: string) =>
17 analyticshub
18 .listProjectsLocationsDataExchangesListings({ parent, pageSize: 100 })
19 .pipe(
20 Effect.map((page) =>
21 (page.listings ?? []).some((item) => item.name === name)
22 ? ("found" as const)
23 : ("gone" as const),
24 ),
25 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
26 Effect.catchTag("InternalServerError", () =>
27 Effect.succeed("found" as const),
28 ),
29 Effect.repeat({
30 schedule: Schedule.spaced("2 seconds"),
31 until: (status) => status === "gone",
32 times: 20,
33 }),
34 );
35
36test.provider(
37 "getProjectsLocationsDataExchangesListings on a missing listing fails with a typed tag",
38 (stack) =>
39 Effect.gen(function* () {
40 const project = yield* currentProject;
41
42 yield* stack.destroy();
43
44 const error = yield* Effect.flip(
45 analyticshub.getProjectsLocationsDataExchangesListings({
46 name: `projects/${project}/locations/${hubLocation}/dataExchanges/missing/listings/alchemy_missing_listing`,
47 }),
48 );
49 expect(error._tag).toEqual("NotFound");
50
51 yield* stack.destroy();
52 }).pipe(logLevel),
53 {
54 tags: ["provider:gcp", "provider:gcp:analyticshub", "live"],
55 timeout: 90_000,
56 },
57);
58
59test.provider.skipIf(!runLifecycle)(
60 "listing lifecycle",
61 (stack) =>
62 Effect.gen(function* () {
63 yield* stack.destroy();
64
65 const created = yield* stack.deploy(
66 Effect.gen(function* () {
67 const dataset = yield* GCP.BigQuery.Dataset("Source", {
68 location: hubLocation,
69 forceDestroy: true,
70 });
71 const exchange = yield* GCP.AnalyticsHub.DataExchange("Hub", {
72 location: hubLocation,
73 displayName: "Hub",
74 });
75 const listing = yield* GCP.AnalyticsHub.DataExchangesListing(
76 "Orders",
77 {
78 dataExchange: exchange.name,
79 location: hubLocation,
80 displayName: "Orders",
81 description: "order events",
82 bigqueryDataset: { dataset: dataset.name },
83 },
84 );
85 return { dataset, exchange, listing };
86 }),
87 );
88
89 expect(created.listing.name).toContain("/listings/");
90 expect(created.listing.dataExchange).toEqual(created.exchange.name);
91 expect(created.listing.displayName).toEqual("Orders");
92 expect(created.listing.description).toEqual("order events");
93 expect(created.listing.bigqueryDataset?.dataset).toContain(
94 created.dataset.datasetId,
95 );
96
97 const fetched =
98 yield* analyticshub.getProjectsLocationsDataExchangesListings({
99 name: created.listing.name,
100 });
101 expect(fetched.name).toEqual(created.listing.name);
102 expect(fetched.displayName).toEqual("Orders");
103 expect(fetched.description).toContain("alchemy-id=");
104 expect(fetched.description).toContain("order events");
105 expect(fetched.bigqueryDataset?.dataset).toContain(
106 created.dataset.datasetId,
107 );
108
109 const updated = yield* stack.deploy(
110 Effect.gen(function* () {
111 const dataset = yield* GCP.BigQuery.Dataset("Source", {
112 datasetId: created.dataset.datasetId,
113 location: hubLocation,
114 forceDestroy: true,
115 });
116 const exchange = yield* GCP.AnalyticsHub.DataExchange("Hub", {
117 dataExchangeId: created.exchange.dataExchangeId,
118 location: hubLocation,
119 displayName: "Hub",
120 });
121 const listing = yield* GCP.AnalyticsHub.DataExchangesListing(
122 "Orders",
123 {
124 dataExchange: exchange.name,
125 listingId: created.listing.listingId,
126 location: hubLocation,
127 displayName: "Orders v2",
128 description: "order facts",
129 primaryContact: "orders@example.com",
130 bigqueryDataset: { dataset: dataset.name },
131 },
132 );
133 return { dataset, exchange, listing };
134 }),
135 );
136
137 expect(updated.listing.name).toEqual(created.listing.name);
138 expect(updated.listing.displayName).toEqual("Orders v2");
139 expect(updated.listing.description).toEqual("order facts");
140 expect(updated.listing.primaryContact).toEqual("orders@example.com");
141
142 const fetchedUpdate =
143 yield* analyticshub.getProjectsLocationsDataExchangesListings({
144 name: created.listing.name,
145 });
146 expect(fetchedUpdate.displayName).toEqual("Orders v2");
147 expect(fetchedUpdate.description).toContain("order facts");
148 expect(fetchedUpdate.primaryContact).toEqual("orders@example.com");
149
150 yield* stack.destroy();
151 const gone = yield* waitUntilGone(
152 created.exchange.name,
153 created.listing.name,
154 );
155 expect(gone).toEqual("gone");
156 }).pipe(logLevel),
157 {
158 tags: ["provider:gcp", "provider:gcp:analyticshub", "live"],
159 timeout: 120_000,
160 },
161);