github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/services/adhoc.ts (about)

     1  import { Result } from '@webapp/util/fp';
     2  import { CustomError } from 'ts-custom-error';
     3  import { FlamebearerProfileSchema, Profile } from '@pyroscope/models/src';
     4  import { AllProfilesSchema, AllProfiles } from '@webapp/models/adhoc';
     5  import type { ZodError } from 'zod';
     6  import { z } from 'zod';
     7  import { request, parseResponse } from './base';
     8  import type { RequestError } from './base';
     9  
    10  const uploadResponseSchema = z.object({
    11    flamebearer: FlamebearerProfileSchema,
    12    id: z.string().min(1),
    13  });
    14  type UploadResponse = z.infer<typeof uploadResponseSchema>;
    15  
    16  export async function upload(
    17    file: File,
    18    fileTypeData?: { spyName: string; units: string }
    19  ): Promise<
    20    Result<UploadResponse, FileToBase64Error | RequestError | ZodError>
    21  > {
    22    // prepare body
    23    const b64 = await fileToBase64(file);
    24    if (b64.isErr) {
    25      return Result.err<UploadResponse, FileToBase64Error>(b64.error);
    26    }
    27  
    28    const response = await request('/api/adhoc/v1/upload', {
    29      method: 'POST',
    30      body: JSON.stringify({
    31        filename: file.name,
    32        profile: b64.value,
    33        fileTypeData: fileTypeData || undefined,
    34      }),
    35    });
    36    return parseResponse(response, uploadResponseSchema);
    37  }
    38  
    39  export async function retrieve(
    40    id: string
    41  ): Promise<Result<Profile, RequestError | ZodError>> {
    42    const response = await request(`/api/adhoc/v1/profile/${id}`);
    43    return parseResponse<Profile>(response, FlamebearerProfileSchema);
    44  }
    45  
    46  export async function retrieveDiff(
    47    leftId: string,
    48    rightId: string
    49  ): Promise<Result<Profile, RequestError | ZodError>> {
    50    const response = await request(`/api/adhoc/v1/diff/${leftId}/${rightId}`);
    51    return parseResponse<Profile>(response, FlamebearerProfileSchema);
    52  }
    53  
    54  export async function retrieveAll(): Promise<
    55    Result<AllProfiles, RequestError | ZodError>
    56  > {
    57    const response = await request(`/api/adhoc/v1/profiles`);
    58    return parseResponse(response, AllProfilesSchema);
    59  }
    60  
    61  /**
    62   * represents an error when trying to convert a File to base64
    63   */
    64  export class FileToBase64Error extends CustomError {
    65    public constructor(
    66      public filename: string,
    67      public message: string,
    68      public cause?: Error | DOMException
    69    ) {
    70      super(message);
    71    }
    72  }
    73  
    74  export default function fileToBase64(
    75    file: File
    76  ): Promise<Result<string, FileToBase64Error>> {
    77    return new Promise((resolve) => {
    78      const reader = new FileReader();
    79  
    80      reader.onloadend = () => {
    81        // this is always called, even on failures
    82        if (!reader.error) {
    83          if (!reader.result) {
    84            return resolve(
    85              Result.err(new FileToBase64Error(file.name, 'No result'))
    86            );
    87          }
    88  
    89          // reader can be used with 'readAsArrayBuffer' which returns an ArrayBuffer
    90          // therefore for the sake of the compiler we must check its value
    91          if (typeof reader.result === 'string') {
    92            // remove the prefix
    93            const base64result = reader.result.split(';base64,')[1];
    94            if (!base64result) {
    95              return resolve(
    96                Result.err(
    97                  new FileToBase64Error(file.name, 'Failed to strip prefix')
    98                )
    99              );
   100            }
   101  
   102            // split didn't work
   103            if (base64result === reader.result) {
   104              return resolve(
   105                Result.err(
   106                  new FileToBase64Error(file.name, 'Failed to strip prefix')
   107                )
   108              );
   109            }
   110  
   111            // the string is prefixed with
   112            return resolve(Result.ok(base64result));
   113          }
   114        }
   115  
   116        // should not happen
   117        return resolve(Result.err(new FileToBase64Error(file.name, 'No result')));
   118      };
   119  
   120      reader.onerror = () => {
   121        resolve(
   122          Result.err(
   123            new FileToBase64Error(
   124              file.name,
   125              'File reading has failed',
   126              reader.error || undefined
   127            )
   128          )
   129        );
   130      };
   131  
   132      reader.readAsDataURL(file);
   133    });
   134  }