|
| 1 | +import { loadTable } from '../catalog/loadTable.js' |
| 2 | +import { restCatalogConnect } from '../catalog/rest.js' |
| 3 | +import { s3SignedResolver } from '../s3.js' |
| 4 | +import { createSigV4SignRequest } from '../sigv4.js' |
| 5 | +import { resolveAwsCredentials } from './credentials.js' |
| 6 | + |
| 7 | +/** |
| 8 | + * @import {Resolver, RestCatalogContext} from '../types.js' |
| 9 | + * @import {ResolvedAwsCredentials} from './credentials.js' |
| 10 | + */ |
| 11 | + |
| 12 | +/** @typedef {RestCatalogContext & { s3TablesCreds?: ResolvedAwsCredentials }} S3TablesCatalogContext */ |
| 13 | + |
| 14 | +/** |
| 15 | + * @typedef {object} S3TablesConnectOptions |
| 16 | + * @property {string} region - AWS region, e.g. `us-east-1` |
| 17 | + * @property {string} tableBucketArn - e.g. `arn:aws:s3tables:us-east-1:111122223333:bucket/my-bucket` |
| 18 | + * @property {string} [accessKeyId] - Omit to use the default AWS credential chain |
| 19 | + * @property {string} [secretAccessKey] |
| 20 | + * @property {string} [sessionToken] |
| 21 | + */ |
| 22 | + |
| 23 | +/** |
| 24 | + * Iceberg REST endpoint URL for Amazon S3 Tables in a region. |
| 25 | + * |
| 26 | + * @param {string} region |
| 27 | + * @returns {string} |
| 28 | + */ |
| 29 | +export function s3TablesEndpoint(region) { |
| 30 | + return `https://s3tables.${region}.amazonaws.com/iceberg` |
| 31 | +} |
| 32 | + |
| 33 | +/** |
| 34 | + * Connect to the Amazon S3 Tables Iceberg REST catalog for a table bucket. |
| 35 | + * |
| 36 | + * When credentials are resolved from the default chain, the optional peer |
| 37 | + * dependency `@aws-sdk/credential-providers` is required (pass explicit keys to |
| 38 | + * avoid it). Catalog requests are SigV4-signed with service name `s3tables`. |
| 39 | + * Use {@link s3TablesResolver} with the same credentials to read table data |
| 40 | + * files (SigV4 with service name `s3`). |
| 41 | + * |
| 42 | + * @param {S3TablesConnectOptions} options |
| 43 | + * @returns {Promise<S3TablesCatalogContext>} |
| 44 | + */ |
| 45 | +export async function s3TablesCatalogConnect({ |
| 46 | + region, tableBucketArn, accessKeyId, secretAccessKey, sessionToken, |
| 47 | +}) { |
| 48 | + const creds = await resolveAwsCredentials({ region, accessKeyId, secretAccessKey, sessionToken }) |
| 49 | + const ctx = await restCatalogConnect({ |
| 50 | + url: s3TablesEndpoint(region), |
| 51 | + warehouse: tableBucketArn, |
| 52 | + signRequest: createSigV4SignRequest({ |
| 53 | + accessKeyId: creds.accessKeyId, |
| 54 | + secretAccessKey: creds.secretAccessKey, |
| 55 | + sessionToken: creds.sessionToken, |
| 56 | + region, |
| 57 | + service: 's3tables', |
| 58 | + }), |
| 59 | + }) |
| 60 | + return Object.freeze({ ...ctx, s3TablesCreds: creds }) |
| 61 | +} |
| 62 | + |
| 63 | +/** |
| 64 | + * Connect using the default AWS credential chain (env vars, shared config, IAM role). |
| 65 | + * |
| 66 | + * @param {object} options |
| 67 | + * @param {string} options.region |
| 68 | + * @param {string} options.tableBucketArn |
| 69 | + * @returns {Promise<S3TablesCatalogContext>} |
| 70 | + */ |
| 71 | +export function s3TablesCatalogConnectFromEnv({ region, tableBucketArn }) { |
| 72 | + return s3TablesCatalogConnect({ region, tableBucketArn }) |
| 73 | +} |
| 74 | + |
| 75 | +/** |
| 76 | + * Build a SigV4 `Resolver` for reading S3 Tables data files (`s3://…--table-s3/…`). |
| 77 | + * |
| 78 | + * @param {S3TablesConnectOptions} options |
| 79 | + * @returns {Promise<Resolver>} |
| 80 | + */ |
| 81 | +export async function s3TablesResolver({ region, accessKeyId, secretAccessKey, sessionToken }) { |
| 82 | + const creds = await resolveAwsCredentials({ region, accessKeyId, secretAccessKey, sessionToken }) |
| 83 | + return s3SignedResolver(creds) |
| 84 | +} |
| 85 | + |
| 86 | +/** |
| 87 | + * Load a table from an S3 Tables catalog context, wiring a resolver from stored |
| 88 | + * credentials when none is supplied. |
| 89 | + * |
| 90 | + * @param {object} options |
| 91 | + * @param {S3TablesCatalogContext} options.catalog |
| 92 | + * @param {string | string[]} options.namespace |
| 93 | + * @param {string} options.table |
| 94 | + * @param {Resolver} [options.resolver] |
| 95 | + * @returns {ReturnType<typeof loadTable>} |
| 96 | + */ |
| 97 | +export function loadS3TablesTable({ catalog, namespace, table, resolver }) { |
| 98 | + const eff = resolver ?? (catalog.s3TablesCreds |
| 99 | + ? s3SignedResolver(catalog.s3TablesCreds) |
| 100 | + : undefined) |
| 101 | + return loadTable({ catalog, namespace, table, resolver: eff }) |
| 102 | +} |
0 commit comments