s3Helper.ts

mail@pastecode.io avatar
unknown
typescript
7 months ago
5.3 kB
2
Indexable
Never
import {API, graphqlOperation} from 'aws-amplify';
import {
  endSimpleTrainConsistTrip,
  getPresignedPostUrl,
} from '@graphql/queriesFn';

// Utils
import {
  deleteGzipFile,
  readAndDecompressGzipFile,
  readGzipDirectory,
} from '@Utils/fileHelper';

// Types
import {File, S3PathKey} from '@Types';

// Apollo
import apolloClient from '@ApolloClient/apolloClient';

/**
 * Requests an S3 pre-signed URL that can be used to upload files.
 * @param {string} s3PathKey - An enum value specifying the data type (e.g GPS, MODELS, etc.).
 * @returns {Promise<string>} - An S3 pre-signed URL.
 */
const getS3PreSignedUrl = async ({s3PathKey}: {s3PathKey: string}) => {
  try {
    const client = await apolloClient();
    const response = await client.query({
      query: getPresignedPostUrl({
        s3_path_key: s3PathKey,
      }),
    });

    console.log('response', response, s3PathKey);
    return response;
  } catch (error) {
    console.log('error', error);
    return error;
  }
};

/**
 * This is an asynchronous function that ends a simple train consist trip by calling an API with the
 * necessary parameters.
 * @param  - The function `endTrip` takes in an object with three properties as its parameter:
 */
const endTrip = async (
  createdBy: string,
  endDateUtc: string,
  simpleTrainConsistId: string,
) => {
  try {
    const client = await apolloClient();
    await client.mutate({
      mutation: endSimpleTrainConsistTrip({
        createdBy,
        endDateUtc,
        simpleTrainConsistId,
      }),
    });
  } catch (error) {
    console.log(error);
  }
};

/**
 * Uploads a single file to S3 using a pre-signed URL.
 * @param {string} url - The pre-signed URL used to upload the file.
 * @param {any} postRequestFields - Request headers
 * @param {File} file - An object containing information about the file to upload. It should have the following properties:
 *   - file_name: The name of the file, including the extension.
 *   - file_type: The MIME type of the file.
 *   - file_uri: The URI of the file to upload.
 * @returns {Promise<void>} - A promise that resolves when the file has been successfully uploaded to S3.
 */
const uploadSingleFileToS3 = async (
  url: string,
  postRequestFields: any,
  file: File,
  s3PathKey: S3PathKey,
) => {
  console.log(`Uploading ${file.name}...`);

  const fileContent = await readAndDecompressGzipFile(s3PathKey, file.name);
  const fileContentObject = JSON.parse(fileContent);

  endTrip(
    fileContentObject.created_at_utc,
    fileContentObject.end_trip_timestamp_utc,
    fileContentObject.simple_train_consist_id,
  );

  const formData = new FormData();

  // Add post request fields to body requests
  for (const [key, value] of Object.entries(postRequestFields)) {
    formData.append(key, value);
  }

  formData.append('file', {
    ...file,
  });

  const response = await fetch(url, {
    method: 'POST',
    body: formData,
    headers: {
      'Content-Type': `multipart/form-data`,
    },
  });

  return response;
};

/**
 * Uploads an array of .gzip files read from native file system to S3 using pre-signed URLs.
 * @param {S3PathKey} s3PathKey - An enum value specifying the data type (e.g GPS, MODEL, etc.).
 */
export const uploadAllFilesToS3 = async (s3PathKey: S3PathKey) => {
  const files: File[] = await readGzipDirectory(s3PathKey);

  for (const file of files) {
    // 1. Request for a pre-signed url given railroad_id, timestamp and file format
    let preSignedUrl, postRequestFields;
    try {
      const fileMetaData = file.name.split(':');
      // File name on S3 is just the Simple Train Consist
      const fileNameOnS3 = fileMetaData[5];
      const preSignedUrlResponse: any = await getS3PreSignedUrl({
        s3PathKey: `${s3PathKey}/railroad=${fileMetaData[0]}/year=${fileMetaData[1]}/month=${fileMetaData[2]}/day=${fileMetaData[3]}/${fileNameOnS3}`,
      });

      const presignedPostUrl = preSignedUrlResponse?.data?.presigned_post_url;

      const {response_code, presigned_url, post_request_fields} =
        presignedPostUrl;

      const parsed_post_request_fields = JSON.parse(post_request_fields);

      if (response_code === 409) {
        console.error(`${file.name} already exists`);
        deleteGzipFile(s3PathKey, file.name);
        continue;
      }

      preSignedUrl = presigned_url;
      postRequestFields = parsed_post_request_fields;
    } catch (error) {
      console.error(`Error getting pre-signed URL for ${file.name}: ${error}`);
      continue;
    }

    // 2. Upload the file to S3 using the pre-signed URL
    try {
      const response = await uploadSingleFileToS3(
        preSignedUrl,
        postRequestFields,
        file,
        s3PathKey,
      );

      if (response.status === 409) {
        console.log(`${file.name} already exists`);
        deleteGzipFile(s3PathKey, file.name);
        return;
      }

      if (response.status !== 200 && response.status !== 204) {
        console.log(`${file.name} uploaded failed!`);
        return;
      }

      console.log(`${file.name} uploaded successfully!`);
      // Delete gzip file after uploading successfully
      deleteGzipFile(s3PathKey, file.name);
    } catch (error) {
      console.error(`Error uploading ${file.name}: ${error}`);
    }
  }
};