GioCirque/DynamoDb-LevelDown

View on GitHub
src/lib/s3Async.ts

Summary

Maintainability
C
7 hrs
Test Coverage
A
100%
import { S3 } from 'aws-sdk';
import { promisify } from 'util';
import { Attachment } from './types';

/* @internal */
export class S3Async {
  private headBucketAsync?: (params: S3.Types.HeadBucketRequest) => Promise<any>;
  private createBucketAsync?: (params: S3.Types.CreateBucketRequest) => Promise<S3.Types.CreateBucketOutput>;
  private putObjectAsync?: (params: S3.Types.PutObjectRequest) => Promise<S3.Types.PutObjectOutput>;
  private getObjectAsync?: (params: S3.Types.GetObjectRequest) => Promise<S3.Types.GetObjectOutput>;
  private deleteObjectAsync?: (params: S3.Types.DeleteObjectRequest) => Promise<S3.Types.DeleteObjectOutput>;
  private deleteBucketAsync?: (params: S3.Types.DeleteBucketRequest) => Promise<any>;

  constructor(private s3: S3, private bucketName: string) {
    if (!!s3) {
      this.putObjectAsync = promisify(this.s3.putObject).bind(this.s3);
      this.getObjectAsync = promisify(this.s3.getObject).bind(this.s3);
      this.headBucketAsync = promisify(this.s3.headBucket).bind(this.s3);
      this.createBucketAsync = promisify(this.s3.createBucket).bind(this.s3);
      this.deleteObjectAsync = promisify(this.s3.deleteObject).bind(this.s3);
      this.deleteBucketAsync = promisify(this.s3.deleteBucket).bind(this.s3);
    }
  }

  async bucketExists() {
    return await this.useCallSuccess(this.headBucketAsync);
  }

  async createBucket() {
    return await this.useCallSuccess(this.createBucketAsync);
  }

  async deleteBucket() {
    return await this.useCallSuccess(this.deleteBucketAsync);
  }

  async putObject(key: string, data: any, contentType?: string, acl: S3.ObjectCannedACL = 'public-read') {
    if (!this.putObjectAsync) return {};
    const putResult = await this.putObjectAsync({
      Key: key,
      Bucket: this.bucketName,
      Body: data,
      ContentType: contentType,
      ACL: acl
    });
    return putResult;
  }

  async putObjectBatch(...attachments: Attachment[]): Promise<{ [key: string]: S3.PutObjectOutput }> {
    if (!this.putObjectAsync) return {};
    return await Promise.all(
      attachments.map(a => this.putObject(a.key, a.data, a.contentType).then(result => ({ key: a.key, result })))
    ).then(all => all.reduce((p, c) => ({ ...p, [c.key]: c.result }), {}));
  }

  async getObject(key: string) {
    if (!this.getObjectAsync) return {};
    return await this.getObjectAsync({ Bucket: this.bucketName, Key: key });
  }

  async getObjectBatch(...keys: string[]): Promise<{ [key: string]: S3.GetObjectOutput }> {
    if (!this.getObjectAsync) return {};
    return await this.simpleBatch(keys, key => this.getObject(key));
  }

  async deleteObject(key: string) {
    if (!this.deleteObjectAsync) return {};
    return await this.deleteObjectAsync({ Bucket: this.bucketName, Key: key });
  }

  async deleteObjectBatch(...keys: string[]): Promise<{ [key: string]: S3.DeleteObjectOutput }> {
    if (!this.deleteObjectAsync) return {};
    return await this.simpleBatch(keys, key => this.deleteObject(key));
  }

  private simpleBatch<T>(keys: string[], func: (key: string) => Promise<T>) {
    return Promise.all(keys.map(key => func(key).then(result => ({ key, result })))).then(all =>
      all.reduce((p, c) => ({ ...p, [c.key]: c.result }), {})
    );
  }

  private async useCallSuccess<T>(func?: (params: { Bucket: string }) => Promise<T>): Promise<boolean> {
    if (!func) return true;
    try {
      await func({ Bucket: this.bucketName });
    } catch (e) {
      return false;
    }
    return true;
  }
}