1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as firebasedataconnect from "@distilled.cloud/gcp/firebasedataconnect_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 location,
9 logLevel,
10 missingSchema,
11 currentProject,
12 schemaSource,
13 unlinkedDatasources,
14} from "./common.ts";
15
16const { test } = Test.make({ providers: GCP.providers() });
17
18const waitUntilGone = (name: string) =>
19 firebasedataconnect.getProjectsLocationsServicesSchemas({ name }).pipe(
20 Effect.as("found" as const),
21 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
22 Effect.repeat({
23 schedule: Schedule.spaced("3 seconds"),
24 until: (status) => status === "gone",
25 times: 10,
26 }),
27 );
28
29test.provider(
30 "getProjectsLocationsServicesSchemas on a missing schema fails with a typed tag",
31 (stack) =>
32 Effect.gen(function* () {
33 const project = yield* currentProject;
34
35 yield* stack.destroy();
36
37 const missing = yield* missingSchema();
38 const error = yield* Effect.flip(
39 firebasedataconnect.getProjectsLocationsServicesSchemas({
40 name: missing,
41 }),
42 );
43 expect(error._tag).toEqual("NotFound");
44
45 const page =
46 yield* firebasedataconnect.listProjectsLocationsServicesSchemas({
47 parent: `projects/${project}/locations/${location}/services/-`,
48 pageSize: 10,
49 });
50 expect((page.schemas ?? []).map((item) => item.name)).not.toContain(
51 missing,
52 );
53
54 yield* stack.destroy();
55 }).pipe(logLevel),
56 {
57 tags: ["provider:gcp", "provider:gcp:firebasedataconnect", "live"],
58 timeout: 90_000,
59 },
60);
61
62test.provider(
63 "create, update, and delete a Data Connect schema",
64 (stack) =>
65 Effect.gen(function* () {
66 yield* stack.destroy();
67
68 const created = yield* stack.deploy(
69 Effect.gen(function* () {
70 const service = yield* GCP.FirebaseDataConnect.Service("App", {
71 displayName: "alchemy-test-dataconnect",
72 labels: { env: "test" },
73 });
74 const schema = yield* GCP.FirebaseDataConnect.ServicesSchema("Main", {
75 service: service.name,
76 source: schemaSource(),
77 datasources: unlinkedDatasources,
78 displayName: "alchemy-test-schema",
79 labels: { env: "test" },
80 });
81 return { service, schema };
82 }),
83 );
84
85 expect(created.schema.name).toContain("/schemas/main");
86 expect(created.schema.schemaId).toEqual("main");
87 expect(created.schema.location).toEqual(location);
88 expect(created.schema.displayName).toEqual("alchemy-test-schema");
89 expect(created.schema.labels).toMatchObject({ env: "test" });
90 expect(created.schema.service).toEqual(created.service.name);
91 expect(created.schema.reconciling).toEqual(false);
92
93 const fetched =
94 yield* firebasedataconnect.getProjectsLocationsServicesSchemas({
95 name: created.schema.name,
96 });
97 expect(fetched.name).toEqual(created.schema.name);
98 expect(fetched.displayName).toEqual("alchemy-test-schema");
99 expect(fetched.labels?.env).toEqual("test");
100 expect(fetched.source?.files?.[0]?.path).toEqual("schema.gql");
101
102 const updated = yield* stack.deploy(
103 Effect.gen(function* () {
104 const service = yield* GCP.FirebaseDataConnect.Service("App", {
105 serviceId: created.service.serviceId,
106 displayName: "alchemy-prod-dataconnect",
107 labels: { env: "prod" },
108 });
109 const schema = yield* GCP.FirebaseDataConnect.ServicesSchema("Main", {
110 service: service.name,
111 source: schemaSource("body: String"),
112 datasources: unlinkedDatasources,
113 displayName: "alchemy-prod-schema",
114 labels: { env: "prod", role: "schema" },
115 });
116 return { service, schema };
117 }),
118 );
119
120 expect(updated.schema.name).toEqual(created.schema.name);
121 expect(updated.schema.displayName).toEqual("alchemy-prod-schema");
122 expect(updated.schema.labels).toMatchObject({
123 env: "prod",
124 role: "schema",
125 });
126
127 const refetched =
128 yield* firebasedataconnect.getProjectsLocationsServicesSchemas({
129 name: created.schema.name,
130 });
131 expect(refetched.displayName).toEqual("alchemy-prod-schema");
132 expect(refetched.labels?.env).toEqual("prod");
133 expect(refetched.source?.files?.[0]?.content).toContain("body: String");
134
135 yield* stack.destroy();
136
137 const gone = yield* waitUntilGone(created.schema.name);
138 expect(gone).toEqual("gone");
139 }).pipe(logLevel),
140 {
141 tags: ["provider:gcp", "provider:gcp:firebasedataconnect", "live"],
142 timeout: 120_000,
143 },
144);