UNPKG

@itwin/object-storage-s3

Version:

Object storage implementation base for S3 compatible providers

193 lines 8.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.S3ClientWrapper = void 0; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ const stream_1 = require("stream"); const client_s3_1 = require("@aws-sdk/client-s3"); const lib_storage_1 = require("@aws-sdk/lib-storage"); const internal_1 = require("@itwin/object-storage-core/lib/common/internal"); const internal_2 = require("@itwin/object-storage-core/lib/server/internal"); class S3ClientWrapper { _client; _bucket; constructor(_client, _bucket) { this._client = _client; this._bucket = _bucket; } async download(reference, options) { /* eslint-disable @typescript-eslint/naming-convention */ const { Body } = await this._client.send(new client_s3_1.GetObjectCommand({ Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(reference), }), options); /* eslint-enable @typescript-eslint/naming-convention */ if (Body instanceof stream_1.Readable) return Body; throw new Error("Unexpected body type"); } async upload(reference, data, metadata, headers) { /* eslint-disable @typescript-eslint/naming-convention */ const input = { Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(reference), Metadata: metadata, }; /* eslint-enable @typescript-eslint/naming-convention */ if (data) input.Body = data instanceof stream_1.Readable ? await (0, internal_2.streamToBuffer)(data) : data; else input.ContentLength = 0; input.ContentEncoding = headers?.contentEncoding; input.CacheControl = headers?.cacheControl; input.ContentType = headers?.contentType; await this._client.send(new client_s3_1.PutObjectCommand(input)); } async uploadInMultipleParts(reference, data, options, headers) { const { queueSize, partSize, metadata } = options ?? {}; /* eslint-disable @typescript-eslint/naming-convention */ const upload = new lib_storage_1.Upload({ client: this._client, queueSize, partSize, leavePartsOnError: false, params: { Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(reference), Body: data, Metadata: metadata, ContentEncoding: headers?.contentEncoding, CacheControl: headers?.cacheControl, ContentType: headers?.contentType, }, }); /* eslint-enable @typescript-eslint/naming-convention */ await upload.done(); } async getDirectoriesNextPage(options) { /* eslint-disable @typescript-eslint/naming-convention */ const response = await this._client.send(new client_s3_1.ListObjectsV2Command({ Bucket: this._bucket, ContinuationToken: options.continuationToken, // add delimiter to get list of directories in the response. // See https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-prefixes.html Delimiter: "/", MaxKeys: options.maxPageSize, })); const directories = response.CommonPrefixes?.map((entry) => // removing last character which is a slash to have directory name ({ baseDirectory: entry.Prefix?.slice(0, -1) })) ?? []; const continuationToken = response.NextContinuationToken; const uniqueBaseDirectories = Array.from(new Set(directories).values()); const page = { entities: uniqueBaseDirectories, next: continuationToken == undefined ? undefined : () => this.getDirectoriesNextPage({ maxPageSize: options.maxPageSize, continuationToken: continuationToken, }), }; return page; } async getObjectsNextPage(directory, options) { /* eslint-disable @typescript-eslint/naming-convention */ const response = await this._client.send(new client_s3_1.ListObjectsV2Command({ Bucket: this._bucket, Prefix: directory.baseDirectory, ContinuationToken: options.continuationToken, MaxKeys: options.maxPageSize, })); /* eslint-disable @typescript-eslint/naming-convention */ let references = response.Contents?.map((object) => (0, internal_1.buildObjectReference)(object.Key)) ?? []; const continuationToken = response.NextContinuationToken; if (!options?.includeEmptyFiles) { references = references.filter((ref) => !!ref.objectName); } const page = { entities: references, next: continuationToken == undefined ? undefined : () => this.getObjectsNextPage(directory, { maxPageSize: options.maxPageSize, continuationToken: continuationToken, includeEmptyFiles: options.includeEmptyFiles, }), }; return page; } async deleteObject(reference) { /* eslint-disable @typescript-eslint/naming-convention */ await this._client.send(new client_s3_1.DeleteObjectCommand({ Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(reference), })); /* eslint-enable @typescript-eslint/naming-convention */ } async copyObject(bucket, sourceReference, targetReference) { /* eslint-disable @typescript-eslint/naming-convention */ await this._client.send(new client_s3_1.CopyObjectCommand({ CopySource: `${bucket}/${(0, internal_1.buildObjectKey)(sourceReference)}`, Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(targetReference), })); /* eslint-enable @typescript-eslint/naming-convention */ } async updateMetadata(reference, metadata) { /* eslint-disable @typescript-eslint/naming-convention */ await this._client.send(new client_s3_1.CopyObjectCommand({ Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(reference), CopySource: `${this._bucket}/${(0, internal_1.buildObjectKey)(reference)}`, Metadata: metadata, MetadataDirective: "REPLACE", })); /* eslint-enable @typescript-eslint/naming-convention */ } async getObjectProperties(reference) { /* eslint-disable @typescript-eslint/naming-convention */ const { LastModified, ContentLength, Metadata: metadata, ContentEncoding: contentEncoding, ContentType: contentType, CacheControl: cacheControl, } = await this._client.send(new client_s3_1.HeadObjectCommand({ Bucket: this._bucket, Key: (0, internal_1.buildObjectKey)(reference), })); /* eslint-enable @typescript-eslint/naming-convention */ return { reference, lastModified: LastModified, size: ContentLength, metadata, contentEncoding, contentType, cacheControl, }; } async objectExists(reference) { try { return !!(await this.getObjectProperties(reference)); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error) { if (error.name === "NotFound") return false; throw error; } } async prefixExists(directory) { const filesWithPrefix = await this.getObjectsNextPage(directory, { maxPageSize: 1, includeEmptyFiles: true, }); return filesWithPrefix.entities.length !== 0; } get bucketName() { return this._bucket; } releaseResources() { this._client.destroy(); } } exports.S3ClientWrapper = S3ClientWrapper; //# sourceMappingURL=S3ClientWrapper.js.map