1import * as discoveryengine from "@distilled.cloud/gcp/discoveryengine_v1";
2import * as Effect from "effect/Effect";
3import { waitForOperation as waitForGcpOperation } from "../Operation.ts";
4
5export const resourceNameFromOperation = (
6 operation: discoveryengine.GoogleLongrunningOperation,
7): string | undefined => {
8 const response = operation.response;
9 if (response && typeof response === "object" && "name" in response) {
10 const name = (response as { name?: unknown }).name;
11 if (typeof name === "string" && name.length > 0) return name;
12 }
13 const metadata = operation.metadata;
14 if (metadata && typeof metadata === "object") {
15 const record = metadata as { target?: unknown; name?: unknown };
16 if (typeof record.target === "string" && record.target.length > 0) {
17 return record.target;
18 }
19 if (typeof record.name === "string" && record.name.length > 0) {
20 return record.name;
21 }
22 }
23 return undefined;
24};
25
26/**
27 * Wait for a Discovery Engine operation; data store and engine creation
28 * take a few minutes. ALREADY_EXISTS (code 6) counts as success (create
29 * race); with `notFoundOk`, so does NOT_FOUND (code 5, delete race) and an
30 * operation that is already gone.
31 */
32export const waitForOperation = (
33 operation: discoveryengine.GoogleLongrunningOperation,
34 options?: { notFoundOk?: boolean },
35) =>
36 waitForGcpOperation(
37 operation,
38 (name) => discoveryengine.getProjectsLocationsOperations({ name }),
39 { budget: "10 minutes" },
40 ).pipe(
41 Effect.catchIf(
42 (error) =>
43 (error._tag === "GCP.OperationFailed" &&
44 (error.code === 6 ||
45 (options?.notFoundOk === true && error.code === 5))) ||
46 (options?.notFoundOk === true && error._tag === "NotFound"),
47 () => Effect.succeed(operation),
48 ),
49 );