Bucket.js

const { Upload } = require('@aws-sdk/lib-storage');
const {
  S3Client,
  ListBucketsCommand,
  CreateBucketCommand,
  DeleteBucketCommand,
  ListObjectsV2Command,
  DeleteObjectCommand,
  GetObjectCommand,
} = require('@aws-sdk/client-s3');

/** @namespace buckets */
class Bucket {
  constructor(userClass, config) {
    this._userClass = userClass;
    this._config = config;
    this._s3client = null;
  }
  /* Related doc
   * https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/
   */
  async getS3Client() {
    if (this._s3client === null) {
      // It handles two ways of setting up client to be backward compatible with old version of this sdk
      if (this._config?.storage?.accessKeyId) {
        this._s3client = new S3Client({
          region: 'default',
          endpoint: this._config.storageUrl,
          credentials: {
            accessKeyId: this._config.storage.accessKeyId,
            secretAccessKey: this._config.storage?.secretAccessKey ? this._config.storage.secretAccessKey : this._config.auth,
          },
          s3ForcePathStyle: this._config.s3ForcePathStyle,
        });
      } else {
        const userInfo = await this._userClass.get();
        this._s3client = new S3Client({
          region: 'default',
          endpoint: this._config.storageUrl,
          credentials: {
            accessKeyId: userInfo.email,
            secretAccessKey: this._config.auth,
          },
          s3ForcePathStyle: this._config.s3ForcePathStyle,
        });
      }
    }
    return this._s3client;
  }

  async sendCommand(command) {
    const s3client = await this.getS3Client();

    return s3client.send(command);
  }

  /** List all buckets<br>
   * see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/listbucketscommand.html}
   * @memberof buckets
   * @name listBuckets
   * @function
   * @example <caption>Usage</caption>
   * const buckets = await Qarnot.buckets.listBuckets();
   * console.log(buckets);
   * @example <caption>Output</caption>
   * {
   *    Buckets: [
   *      { Name: 'my-bucket', CreationDate: 2019-06-11T12:59:43.743Z }
   *    ],
   *    Owner: { DisplayName: 'john.doe', ID: 'xx$xx' }
   * }
   * @returns {Object} List of all buckets
   */
  async listBuckets() {
    const command = new ListBucketsCommand({});

    return this.sendCommand(command);
  }

  /** Create a new bucket<br>
   * see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/createbucketcommand.html}
   * @memberof buckets
   * @name createBucket
   * @function
   * @param {String} bucketName name of the bucket to create
   * @example <caption>Usage</caption>
   * await Qarnot.buckets.createBucket('my-bucket');
   * @returns {Object}
   */
  async createBucket(bucketName) {
    const command = new CreateBucketCommand({
      Bucket: bucketName,
      CreateBucketConfiguration: {
        LocationConstraint: '',
      },
    });
    return this.sendCommand(command);
  }

  /** Delete a bucket<br>
   * see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/deletebucketcommand.html}
   * @memberof buckets
   * @name deleteBucket
   * @function
   * @param {String} bucketName name of the bucket to delete
   * @example <caption>Usage</caption>
   * await Qarnot.buckets.deleteBucket('my-bucket-to-delete');
   * @returns {Promise}
   */
  async deleteBucket(bucketName) {
    const command = new DeleteBucketCommand({
      Bucket: bucketName,
    });
    return this.sendCommand(command);
  }

  /** Upload data in a bucket <br>
   * see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_lib_storage.html}
   * @memberof buckets
   * @name upload
   * @function
   * @param {String} bucketName name of the bucket
   * @param {String} fileName name of the file to create
   * @param {Buffer|ReadableStream|String} data data to upload
   * @example <caption>Usage</caption>
   * const file = await Qarnot.buckets.upload('my-bucket', 'file.txt', data, mimetype);
   * console.log(file);
   * @example <caption>Output</caption>
   *  {
   *   ETag: '"f7482490035aa20e0bed1375d44e97bf"',
   *   Bucket: 'test-suite-nodejs',
   *   Key: 'test.txt',
   *   Location: 'https://test-suite-nodejs.storage.qarnot.com/test.txt'
   * }
   * @returns {Promise<Object>} Object describing the created file
   */
  async upload(bucketName, fileName, data, mimetype) {
    const upload = new Upload({
      client: await this.getS3Client(),
      params: {
        Bucket: bucketName,
        Key: fileName,
        Body: data,
        ContentType: mimetype,
      },
    });
    return upload.done();
  }

  /** Download content of a file in a bucket<br>
   *  see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/getobjectcommand.html}
   * @name download
   * @memberof buckets
   * @function
   * @param {String} bucketName name of the bucket
   * @param {String} fileName name of the file to create
   * @example <caption>Usage</caption>
   * const data = await Qarnot.buckets.download('my-bucket', 'file.txt', params);
   * console.log(data)
   * console.log(data.Body.toString())
   * @example <caption>Output</caption>
   * {
   *    AcceptRanges: 'bytes',
   *    LastModified: 2019-06-11T20:13:17.000Z,
   *    ContentLength: 11,
   *    ETag: '"5eb63bbbe01eeed093cb22bb8f5acdc3"',
   *    ContentType: 'application/octet-stream',
   *    Metadata: {},
   *    Body: <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
   * }
   * hello world
   * @returns {Promise<Buffer>} File info and content <br>
   * More info see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/getobjectcommandoutput.html}
   */
  async download(bucketName, fileName, params = {}) {
    const s3params = Object.assign(params, {
      Bucket: bucketName,
      Key: fileName,
    });
    const command = new GetObjectCommand(s3params);

    return this.sendCommand(command);
  }

  /** List all files in a bucket<br>
   * see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/listobjectsv2command.html}
   * @name listFiles
   * @memberof buckets
   * @function
   * @param {String} bucketName name of the bucket
   * @param {Object} [params] custom params object for the storage API (see code and aws s3 sdk documentation)
   * @example <caption>Usage</caption>
   * const files = await Qarnot.buckets.listFiles('my-bucket');
   * console.log(files);
   * @example <caption>Output</caption>
   * {
   *   Contents: [
   *     {
   *       Key: 'test.txt',
   *       LastModified: 2023-05-30T09:30:53.873Z,
   *       ETag: '"f7482490035aa20e0bed1375d44e97bf"',
   *       Size: 17,
   *       StorageClass: 'STANDARD'
   *     }
   *   ],
   *   IsTruncated: false,
   *   KeyCount: 1,
   *   MaxKeys: 1000,
   *   Name: 'test-suite-nodejs',
   *   Prefix: ''
   * }
   * @returns {Promise<Object>} Files in your bucket
   */
  async listFiles(bucketName, params = {}) {
    const s3Params = Object.assign(params, { Bucket: bucketName });
    const command = new ListObjectsV2Command(s3Params);

    return this.sendCommand(command);
  }

  /** Delete a file in a bucket
   * see: {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/deleteobjectcommand.html}
   * @name deleteFile
   * @memberof buckets
   * @function
   * @example
   * await Qarnot.buckets.deleteFile('my-bucket', 'file.txt');
   * @returns {Promise}
   */
  async deleteFile(bucketName, fileName) {
    const command = new DeleteObjectCommand({ Bucket: bucketName, Key: fileName });
    return this.sendCommand(command);
  }
}

module.exports = Bucket;