authui-container/server/api/cloud-storage-handler.ts (120 lines of code) (raw):

/* * Copyright 2020 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ import {AuthenticatedRequestHandler} from './authenticated-request-handler'; import {AccessTokenManager} from './token-manager'; import {ApplicationData} from './metadata-server'; /** Interface defining the bucket related metadata as returned by server. */ interface Bucket { kind: string; selfLink: string; id: string; name: string; projectNumber: string; metageneration: string; location: string; storageClass: string; locationType: string; timeCreated: string; updated: string; iamConfiguration?: any; } /** Interface defining the server response when listing GCS buckets. */ interface BucketList { kind: string; items: Bucket[]; } /** GCS create bucket endpoint. */ const GCS_CREATE_BUCKET_URL = 'https://storage.googleapis.com/storage/v1/b?project={projectId}'; /** GCS read file endpoint. */ const GCS_READ_FILE_URL = 'https://storage.googleapis.com/storage/v1/b/{bucketName}/o/{fileName}?alt=media'; /** GCS write file endpoint. */ const GCS_WRITE_FILE_URL = 'https://storage.googleapis.com/upload/storage/v1/b/{bucketName}/o?uploadType=media&name={fileName}'; /** GCS list buckets permissions endpoint. */ const GCS_LIST_BUCKETS_URL = GCS_CREATE_BUCKET_URL; /** Default error message to show when a GCS bucket cannot be created. */ export const DEFAULT_ERROR_MESSAGE_CREATE_BUCKET = 'Unable to create GCS bucket.'; /** Default error message to show when a GCS bucket file cannot be read. */ export const DEFAULT_ERROR_MESSAGE_READ_FILE = 'Unable to read GCS file.'; /** Default error message to show when a GCS bucket file cannot be written to. */ export const DEFAULT_ERROR_MESSAGE_WRITE_FILE = 'Unable to write to GCS file.'; /** Default error message to show when GCS buckets cannot be listed. */ export const DEFAULT_ERROR_MESSAGE_LIST_BUCKETS = 'Unable to list GCS buckets.'; /** Network request timeout duration. */ const TIMEOUT_DURATION = 10000; /** * Utility used to make API calls to GCS. This supports the ability to create * storage backets, read and write to them. * OAuth scope required: https://www.googleapis.com/auth/devstorage.read_write */ export class CloudStorageHandler { private createBucketHandler: AuthenticatedRequestHandler; private readFileHandler: AuthenticatedRequestHandler; private writeFileHandler: AuthenticatedRequestHandler; private listBucketsHandler: AuthenticatedRequestHandler; /** * Instantiates a Cloud Storage API handler. * @param app The application data. * @param accessTokenManager The access token manager. */ constructor( private readonly app: ApplicationData, private readonly accessTokenManager: AccessTokenManager) { this.createBucketHandler = new AuthenticatedRequestHandler({ method: 'POST', url: GCS_CREATE_BUCKET_URL, timeout: TIMEOUT_DURATION, }, this.accessTokenManager, app.log.bind(app)); this.readFileHandler = new AuthenticatedRequestHandler({ method: 'GET', url: GCS_READ_FILE_URL, timeout: TIMEOUT_DURATION, }, this.accessTokenManager, app.log.bind(app)); this.writeFileHandler = new AuthenticatedRequestHandler({ method: 'POST', url: GCS_WRITE_FILE_URL, timeout: TIMEOUT_DURATION, }, this.accessTokenManager, app.log.bind(app)); this.listBucketsHandler = new AuthenticatedRequestHandler({ method: 'GET', url: GCS_LIST_BUCKETS_URL, timeout: TIMEOUT_DURATION, }, this.accessTokenManager, app.log.bind(app)); } /** * Creates a new bucket using the name provided. * @param bucketName The name of the bucket to be created. * @return A promise that resolves on successful creation. */ createBucket(bucketName: string): Promise<void> { let urlParams: any; return this.app.getProjectId() .then((projectId) => { urlParams = { projectId, } return this.app.getZone(); }) .then((zone) => { return this.createBucketHandler.send({ urlParams, body: { name: bucketName, location: zone.toUpperCase(), storageClass: 'STANDARD', }, }, DEFAULT_ERROR_MESSAGE_CREATE_BUCKET); }) .then((httpResponse) => { // Return nothing. }); } /** * Reads a Cloud Storage bucket for a specified JSON file contents. * @param bucketName The name of the bucket where the file is located. * @param fileName The name of the file to be read. * @return A promise that resolves with the content of the file. */ readFile(bucketName: string, fileName: string): Promise<any> { return this.readFileHandler.send({ urlParams: { bucketName, fileName, }, }, DEFAULT_ERROR_MESSAGE_READ_FILE).then((httpResponse) => { return typeof httpResponse.body === 'object' ? httpResponse.body : JSON.parse(httpResponse.body); }); } /** * Writes to a Cloud Storage bucket file. * @param bucketName The name of the bucket where the file is located. * @param fileName The name of the file to write to. * @return A promise that resolves on successful write. */ writeFile(bucketName: string, fileName: string, content: any): Promise<void> { return this.writeFileHandler.send({ urlParams: { bucketName, fileName, }, body: content, }, DEFAULT_ERROR_MESSAGE_WRITE_FILE).then((httpResponse) => { // Return nothing. }); } /** * Returns a list of all GCS buckets. * When a user does not have permissions to list the buckets, the error is returned: * { * error: { * code: 403, * message: 'EMAIL does not have storage.buckets.list access to project PROJECT_NUMBER.', * errors: [ * { * message: 'EMAIL does not have storage.buckets.list access to project PROJECT_NUMBER.', * domain: 'global', * reason: 'forbidden' * } * ] * } * } * @return A promise that resolves with the listed buckets. */ listBuckets(): Promise<BucketList> { return this.app.getProjectId() .then((projectId) => { return this.listBucketsHandler.send({ urlParams: { projectId, }, }, DEFAULT_ERROR_MESSAGE_LIST_BUCKETS); }) .then((httpResponse) => { return typeof httpResponse.body === 'object' ? httpResponse.body : JSON.parse(httpResponse.body); }); } }