| 1 | import type * as firestore from "@distilled.cloud/gcp/firestore_v1"; |
| 2 | import type * as Effect from "effect/Effect"; |
| 3 | import * as Binding from "../../Binding.ts"; |
| 4 | import type { RuntimeContext } from "../../RuntimeContext.ts"; |
| 5 | import type { Database } from "./Database.ts"; |
| 6 | |
| 7 | /** |
| 8 | * A Firestore document read through a {@link ReadDatabase} client. `fields` |
| 9 | * are decoded from Firestore `Value`s to plain JavaScript: integers → |
| 10 | * `number` (`bigint` beyond the safe range), doubles → `number`, |
| 11 | * timestamps → `Date`, bytes → `Uint8Array`, arrays and maps recursively, |
| 12 | * geo points → `{ latitude, longitude }`, references → resource name. |
| 13 | */ |
| 14 | export interface DocumentSnapshot { |
| 15 | /** Full resource name `projects/{p}/databases/{d}/documents/{path}`. */ |
| 16 | name: string; |
| 17 | /** Decoded document fields. */ |
| 18 | fields: Record<string, unknown>; |
| 19 | /** RFC 3339 creation time. */ |
| 20 | createTime: string | undefined; |
| 21 | /** RFC 3339 last-update time. */ |
| 22 | updateTime: string | undefined; |
| 23 | } |
| 24 | |
| 25 | export interface ListDocumentsOptions { |
| 26 | /** Maximum documents per page. */ |
| 27 | pageSize?: number; |
| 28 | /** Page token from a previous `list`. */ |
| 29 | pageToken?: string; |
| 30 | } |
| 31 | |
| 32 | export interface ListDocumentsResult { |
| 33 | documents: DocumentSnapshot[]; |
| 34 | /** Pass to the next `list` call; `undefined` on the last page. */ |
| 35 | nextPageToken: string | undefined; |
| 36 | } |
| 37 | |
| 38 | /** Read-only client for one Firestore database. */ |
| 39 | export interface ReadDatabaseClient { |
| 40 | /** |
| 41 | * Get a document by path relative to the database (`"users/alice"`), or |
| 42 | * `undefined` when it does not exist. |
| 43 | */ |
| 44 | get( |
| 45 | path: string, |
| 46 | ): Effect.Effect< |
| 47 | DocumentSnapshot | undefined, |
| 48 | firestore.GetProjectsDatabasesDocumentsError, |
| 49 | RuntimeContext |
| 50 | >; |
| 51 | /** |
| 52 | * One page of documents in a collection (`"users"`, or a subcollection |
| 53 | * such as `"users/alice/posts"`). |
| 54 | */ |
| 55 | list( |
| 56 | collection: string, |
| 57 | options?: ListDocumentsOptions, |
| 58 | ): Effect.Effect< |
| 59 | ListDocumentsResult, |
| 60 | firestore.ListProjectsDatabasesDocumentsError, |
| 61 | RuntimeContext |
| 62 | >; |
| 63 | /** |
| 64 | * Run a Firestore `StructuredQuery` against the database root. Filter |
| 65 | * values use the Firestore REST `Value` shape |
| 66 | * (`{ integerValue: "2" }`, `{ stringValue: "a" }`, …). |
| 67 | */ |
| 68 | query( |
| 69 | structuredQuery: firestore.StructuredQuery, |
| 70 | ): Effect.Effect< |
| 71 | DocumentSnapshot[], |
| 72 | firestore.RunQueryProjectsDatabasesDocumentsError, |
| 73 | RuntimeContext |
| 74 | >; |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Read access to a Firestore {@link Database}: `get`, `list`, `query`. |
| 79 | * Grants `roles/datastore.viewer` on the project under an IAM Condition |
| 80 | * naming this database (Firestore databases have no resource-level IAM |
| 81 | * policy). |
| 82 | * |
| 83 | * ### Reading documents |
| 84 | * **Example:** Get a document and list a collection |
| 85 | * ```typescript |
| 86 | * const db = yield* GCP.Firestore.ReadDatabase(database); |
| 87 | * const alice = yield* db.get("users/alice"); |
| 88 | * const { documents } = yield* db.list("users", { pageSize: 20 }); |
| 89 | * // …provided with Effect.provide(GCP.Firestore.ReadDatabaseHttp) |
| 90 | * ``` |
| 91 | * |
| 92 | * ### Querying |
| 93 | * **Example:** Filter a collection |
| 94 | * ```typescript |
| 95 | * const db = yield* GCP.Firestore.ReadDatabase(database); |
| 96 | * const admins = yield* db.query({ |
| 97 | * from: [{ collectionId: "users" }], |
| 98 | * where: { |
| 99 | * fieldFilter: { |
| 100 | * field: { fieldPath: "role" }, |
| 101 | * op: "EQUAL", |
| 102 | * value: { stringValue: "admin" }, |
| 103 | * }, |
| 104 | * }, |
| 105 | * }); |
| 106 | * ``` |
| 107 | * |
| 108 | * @binding |
| 109 | * @category Firestore |
| 110 | */ |
| 111 | export interface ReadDatabase extends Binding.Service< |
| 112 | ReadDatabase, |
| 113 | "GCP.Firestore.ReadDatabase", |
| 114 | (database: Database) => Effect.Effect<ReadDatabaseClient> |
| 115 | > {} |
| 116 | |
| 117 | export const ReadDatabase = Binding.Service<ReadDatabase>( |
| 118 | "GCP.Firestore.ReadDatabase", |
| 119 | ); |