UNPKG

snowflake-sdk

Version:
215 lines (214 loc) 9.91 kB
export = GCSUtil; /** * How GCS PUT/GET works (read this first) * ======================================= * A transfer happens in two phases: * 1. Resolve - the driver sends the PUT/GET SQL to the Snowflake server (GS), * which decides where the bytes go and how the client may write * there, then returns a `stageInfo` (bucket, path prefix, * credentials, and sometimes a presigned URL). * 2. Transfer - the driver streams bytes straight to the GCS bucket using what * GS returned. The driver never uploads to Snowflake directly. * * For GCS, GS returns exactly ONE of two credential styles per session (never a * mix): * (A) Downscoped access token (`stageInfo.creds.GCS_ACCESS_TOKEN`) - PREFERRED. * "Downscoped" means folder/prefix-scoped, NOT object-scoped: one token * authorizes every object under the staging path, so a single token covers * all files in the PUT. * (B) Presigned URL - FALLBACK/legacy. A signed URL authorizing one operation * on exactly one object, so you need one presigned URL per file. These are * minted by `updateFileMetasWithPresignedUrl` in file_transfer_agent.js, * which re-resolves the PUT once per file (see the put-rank warning there). * * How each style authorizes a request at transfer time: * - Token mode: PUT/GET `generateFileURL(stageInfo)` + file name, with an * `Authorization: Bearer <GCS_ACCESS_TOKEN>` header. * - Presigned mode: PUT/GET the presigned URL itself; no Authorization header. * In token mode the URL and the token MUST come from the same resolution. * * Landmarks in this file: `createClient` is the mode switch; `uploadFile` picks * the upload path; `applyGcsUploadError` maps HTTP failures onto the retry/renew * states the outer loop understands (401 -> renew token, 400 -> renew presigned * URL). */ /** * Creates an GCS utility object. * @param {module} connectionConfig * @param {module} httpClient * @param {module} fileStream * * @returns {Object} * @constructor */ declare function GCSUtil(connectionConfig: any, httpClient: any): Object; declare class GCSUtil { /** * How GCS PUT/GET works (read this first) * ======================================= * A transfer happens in two phases: * 1. Resolve - the driver sends the PUT/GET SQL to the Snowflake server (GS), * which decides where the bytes go and how the client may write * there, then returns a `stageInfo` (bucket, path prefix, * credentials, and sometimes a presigned URL). * 2. Transfer - the driver streams bytes straight to the GCS bucket using what * GS returned. The driver never uploads to Snowflake directly. * * For GCS, GS returns exactly ONE of two credential styles per session (never a * mix): * (A) Downscoped access token (`stageInfo.creds.GCS_ACCESS_TOKEN`) - PREFERRED. * "Downscoped" means folder/prefix-scoped, NOT object-scoped: one token * authorizes every object under the staging path, so a single token covers * all files in the PUT. * (B) Presigned URL - FALLBACK/legacy. A signed URL authorizing one operation * on exactly one object, so you need one presigned URL per file. These are * minted by `updateFileMetasWithPresignedUrl` in file_transfer_agent.js, * which re-resolves the PUT once per file (see the put-rank warning there). * * How each style authorizes a request at transfer time: * - Token mode: PUT/GET `generateFileURL(stageInfo)` + file name, with an * `Authorization: Bearer <GCS_ACCESS_TOKEN>` header. * - Presigned mode: PUT/GET the presigned URL itself; no Authorization header. * In token mode the URL and the token MUST come from the same resolution. * * Landmarks in this file: `createClient` is the mode switch; `uploadFile` picks * the upload path; `applyGcsUploadError` maps HTTP failures onto the retry/renew * states the outer loop understands (401 -> renew token, 400 -> renew presigned * URL). */ /** * Creates an GCS utility object. * @param {module} connectionConfig * @param {module} httpClient * @param {module} fileStream * * @returns {Object} * @constructor */ constructor(connectionConfig: any, httpClient: any); /** * Build the per-session GCS client, which is really just the credential-style * switch (see the module overview at the top of this file): * - token mode: returns `{ gcsToken }` from `stageInfo.creds.GCS_ACCESS_TOKEN` * - presigned mode: returns `null` (no token; each meta carries its own * presigned URL instead) * Also configures the HTTP client for the resolved GCS endpoint. * * @param {Object} stageInfo * * @returns {?{gcsToken: string}} token-bearing client, or null in presigned mode */ createClient: (stageInfo: Object) => { gcsToken: string; } | null; /** * Extract the bucket name and path from the metadata's stage location. * * @param {String} stageLocation * * @returns {GCSLocation} */ extractBucketNameAndPath: (stageLocation: string) => GCSLocation; /** * Create file header based on file being uploaded or not. * * @param {Object} meta * @param {String} filename * * @returns {Object} */ getFileHeader: (meta: Object, filename: string) => Object; /** * Read the file's stat, then dispatch to one of two upload paths. Which path * applies depends on the credential style (token vs presigned; see the module * overview) and the file size: * * 1. **Single Buffer-bodied PUT** — when the session uses a legacy * presigned URL or the file is no larger than * `MULTIPART_THRESHOLD_BYTES`. Workspace stage presigned URLs are * signed for one specific PUT and cannot initiate a resumable upload. * So when GS hands back a `presignedUrl` (the legacy path; * pre-CB_2023_06 deployments or driver versions before 1.6.21), large * files fall back to a single Buffer PUT even though the access-token * path would split into chunks. * 2. **XML API resumable upload session** — when an access token is * available and the file exceeds `MULTIPART_THRESHOLD_BYTES`. Bounds * in-flight memory by `MULTIPART_PART_SIZE_BYTES` and gives per-chunk * retry granularity. See `uploadFileResumable` for protocol * details. * * @param {String} dataFile * @param {Object} meta * @param {Object} encryptionMetadata * @param {Number} maxConcurrency */ uploadFile: (dataFile: string, meta: Object, encryptionMetadata: Object, maxConcurrency: number) => Promise<void>; /** * GCS resumable upload session for `dataFile` (access-token mode only; * presigned URLs cannot initiate a resumable session). Reads * `MULTIPART_PART_SIZE_BYTES` bytes per chunk into a fresh Buffer and PUTs * each as a `Content-Range`-tagged chunk against the session URL minted by * the initiation POST. On transient chunk failure, queries the session for * the committed offset and resumes from there. On terminal failure, * best-effort DELETE the session so the upload doesn't linger as a * half-staged blob. * * Uses GCS's XML API resumable variant (initiate POST against the same * bucket-path URL the single-PUT uses, with `x-goog-resumable: start`) * rather than JSON API resumable, since GS hands the connector that XML * shape; honoring it avoids inventing a path prefix that the server * never returned and keeps strict-allowlisting environments working. * * @param {String} dataFile * @param {Number} fileSize * @param {Object} meta * @param {Object} encryptionMetadata */ uploadFileResumable: (dataFile: string, fileSize: number, meta: Object, encryptionMetadata: Object) => Promise<void>; /** * Single-PUT upload used by both credential styles (and the small-file / * legacy fallback when a resumable session isn't used). Picks the destination * the same way every GCS request does: * - presigned mode: PUT to `meta.presignedUrl` (no Authorization header) * - token mode: PUT to `generateFileURL(meta.stageInfo)` with a Bearer * token from `meta.client.gcsToken` * In token mode the URL (from `meta.stageInfo`) and the token (from * `meta.client`) must originate from the same resolution; see the put-rank * warning in `updateFileMetasWithPresignedUrl` for why mixing resolutions * yields a 403. * * @param {Buffer|string|stream.Readable} fileStream * @param {Object} meta * @param {Object} encryptionMetadata * * @returns {null} */ uploadFileStream: (fileStream: Buffer | string | stream.Readable, meta: Object, encryptionMetadata: Object) => null; /** * Download the file. * * @param {Object} meta * @param fullDstPath * * @returns {null} */ nativeDownloadFile: (meta: Object, fullDstPath: any) => null; /** * Generate file URL based on bucket. * * @param {Object} stageInfo * @param {String} filename * * @returns {String} */ generateFileURL: (stageInfo: Object, filename: string) => string; getGCSCustomEndPoint: (stageInfo: any) => any; setupHttpClient: (endPoint: any) => void; } declare namespace GCSUtil { export { GCSLocation }; } type GCSLocation = { bucketName: string; path: string; };