npm package: @psyora/psy-s3-uploader
Description: **Psy-S3-Uploader** library is created to securely upload images and files to S3 bucket from and Angular application. This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.14.
Latest version: 2.0.1

# Introduction
**Psy-S3-Uploader** library is created to securely upload images and files to S3 bucket from and Angular application. This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.14.

This library requries a backend that generates pre-signed url for POST request using [createPreSignedPost( )](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#createPresignedPost-property) method. So, bucket inforamtion, secret key and access key are provided in the backend.

Based on the response provided by the backend, the library will directly upload the file to S3 bucket. 

# Installation Instructions
## Step 1: Install the library from npm
To install, run the following command in your angular project
```javascript
npm i @psyora/psy-s3-uploader
```
## Step 2: Import the library to Angular
To use the library in your application, import it to your angular application in your app or shared module.

```javascript
import { PsyS3UploaderModule } from '@psyora/psy-s3-uploader';

@NgModule({
    imports: [
        ...,
        PsyS3UploaderModule,
        ...
    ]
})

export class AppModule { }
```

## Step 3: Use the library in the application using the selector
selector: **psy-s3-uploader**
```javascript
<psy-s3-uploader></psy-s3-uploader>
```

## Step 4: Verify the image upload dropbox

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/01.app-preview.jpeg)

# Usage instructions
## User Interaface
You can upload a file inside the application in **two** ways.
1. By dragging and dropping an image
1. By clicking on 'Browse File' and selecting a file to upload

By default, only jpeg, jpg or png images are allowed to be uploaded. However, other types of can be uploaded by specifiying their extensions. They will be explored later.

On successful load, a preview will be shown if an image is selected.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/02.image-preview.jpeg)

You can click on the <span style="color: #ff0000">x</span> button to remove the file from upload.

## Uploading a File
To upload the selected file to S3 bucket, you need to call the upload method from your application. This give you control to time your upload programatically.

In your parent component (**ts**)

```javascript
import { Component, ViewChild } from '@angular/core';
import { PsyS3UploaderComponent } from 'psy-s3-uploader';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})

export class AppComponent {
  
    @ViewChild(PsyS3UploaderComponent) psyS3Uploader: PsyS3UploaderComponent;

    public upload(): void {
        this.psyS3Uploader.upload();
    };
}
```

The library supports uploading files to S3 bucket using pre-signed url's. So you need to specify the API endpoint of a function that is able to generate a pre-signed url.

So, you need to construct a params object and provide the upload endpoint. You can import the **IPsyS3UploaderConfig** from the library and declare it as follows:

```javascript
import { Component, ViewChild } from '@angular/core';
import { IPsyS3UploaderConfig, PsyS3UploaderComponent } from 'psy-s3-uploader';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})

export class AppComponent {
  
    @ViewChild(PsyS3UploaderComponent) psyS3Uploader: PsyS3UploaderComponent;

    public params: IPsyS3UploaderConfig = {
        uploadApi: 'upload API'
    };

    public upload(): void {
        this.psyS3Uploader.upload();
    };
}
```

Next when the upload is in progress, two events are emitted to notify the status of upload.
1. response
1. error

Create two functions to handle these events.

```javascript
import { Component, ViewChild } from '@angular/core';
import { IPsyS3UploaderConfig, PsyS3UploaderComponent } from 'psy-s3-uploader';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})

export class AppComponent {
  
    @ViewChild(PsyS3UploaderComponent) psyS3Uploader: PsyS3UploaderComponent;

    public params: IPsyS3UploaderConfig = {
        uploadApi: 'upload API'
    };

    public upload(): void {
        this.psyS3Uploader.upload();
    };

    public handleResponse(res): void {
        console.log(res)
    };

    public handleError(err): void {
        console.log(err)
    };
}
```

Finally, in your parent component (**html**) update the selector with input and output operators

```html
<psy-s3-uploader
    [params]="params"
    (response)="handleResponse($event)"
    (error)="handleError($event)"
></psy-s3-uploader>
<button (click)="upload()">Upload</button>
```

Now when you can click on Upload button to initiate the upload to S3.

1. The library will reach out to the upload api end-point and get the pre-signed url, signature info, policy info, upload bucket info etc.
1. It will then create a form object and will upload to the S3 bucket that was provided by upload api with all the details.
1. Finally it will notify the parent component of its status

And the results will be logged by the app component.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/03.image-successful-upload.jpeg)

Finally, verify the uploaded image in S3 bucket.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/04.successful-upload-to-s3.jpeg)

## Uploading multiple Files
To upload multiple files, specify the **multipleFiles** param and set it to **true**
```javascript
import { Component, ViewChild } from '@angular/core';
import { IPsyS3UploaderConfig, PsyS3UploaderComponent } from 'psy-s3-uploader';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})

export class AppComponent {
  
    @ViewChild(PsyS3UploaderComponent) psyS3Uploader: PsyS3UploaderComponent;

    public params: IPsyS3UploaderConfig = {
        uploadApi: 'upload API',
        multipleFiles: true
    };

    public upload(): void {
        this.psyS3Uploader.upload();
    };

    public handleResponse(res): void {
        console.log(res)
    };

    public handleError(err): void {
        console.log(err)
    };
}
```
Now, you can select and upload multiple files. When you can click on Upload button to initiate the upload to S3.
And the results will be logged by the app component.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/06.multiple-file-upload-preview.jpeg)

1. The library will iterate through all the files and reach out to the upload api end-point and get the pre-signed url, signature info, policy info, upload bucket info etc.
1. It will then create a form object for every object and will upload to the S3 bucket that was provided by upload api with all the details.
1. Finally, once all the files are uploaded, it will notify the parent component of its status.
And the results will be logged by the app component.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/07.successful-multiple-file-upload-to-s3.jpeg)

**Note:** Due to asynchronous nature of Javascript, the upload process will be almost simultaneous rather than sequential.

## Handling a Failed Upload
On upload failure, you can 

1. retry the process by clicking on the Retry button or by calling the upload method again.
1. remove the failied upload by clicking on 'x'

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/05.image-failure-retry.jpeg)

In case of multiple files, hitting the retry button will reinitate the upload process for only the failed files.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/08.multiple-file-upload-failure.jpeg)

**Note:** A response will not be triggered by the library until all files are successfully uploaded.

In such cases, to get the upload response, the user needs to remove the failed files manually and call the upload method again.

## Specifying Maximum files to upload
In cases of multiple file uploads, the library allows you to upload upto 10 files by default. You can adjust the number of files being uploaded by specifying 'maxFiles' parameter.

In case the number of files selected is greater than the maximum files specified, the library will load valid files until the max file limit is reached.

You will be notified with an alert message if you try to upload more than maximum files.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/09.max-files-alert.jpg)

If/when the maximum files are uploded, the drop-area disappears.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/10.maximum-files-selected.jpg)

To upload more files, you can remove currently selected files to free up a slot for upload.

## Supported Icons
If the uploaded file is not an image. Then the library displays an icon preview.

Currently icon preview is supported for
1. PDF
1. Word
1. Excel

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/14.supported-file-icon.png)

## Preview Mode
Preview mode is used to render 
1. 'preview of an image' by passing image URL
1. 'icon for a document' by specifying the fileType

You can pass an array of URLs to the libray using the previewArray property. Refer to the interface **IFilePreview** for more information.

```ts
public params: IPsyS3UploaderConfig = {
    uploadApi: "value",
    previewArray: [{
        "url": "https://images.unsplash.com/photo-1622522486900-37e2c44647cb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1868&q=80",
        "fileType": "image/jpeg"
    }, {
        "url": "https://images.unsplash.com/photo-1624024946645-d02c208b1c4d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1951&q=80",
        "fileType": "image/jpeg"
    }]
};
```

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/11.preview-mode.jpg)

These files are readonly and upload activities cannot be performed on them. If you try to upload, you will get a notification that satys **'No files found'**.

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/12.uploading-files.jpg)

## Deleting a File
A file/image can be deleted from preview mode by specifying 'canDelete' as 'true'. When specified, a delete 'icon' appears on the top right corner of the preview.
```ts
public params: IPsyS3UploaderConfig = {
    uploadApi: "value",
    previewArray: [{
        url: "https://images.unsplash.com/photo-1622522486900-37e2c44647cb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1868&q=80",
        fileType: "image/jpeg",
        canDelete: true
    }, {
        url: "https://images.unsplash.com/photo-1624024946645-d02c208b1c4d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1951&q=80",
        fileType: "image/jpeg"
    }]
};
```

![image](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/13.delete-icon-in-preview.png)

However, just by clicking on the "🗑" icon, the delete function will not be executed. Since the library is created to delete S3 items from S3 bucket, the key of the object is a required parameter in performing the operation.

They key of the object required to delete the file can be passed to the library by specifying it in the request. It can be passed in two ways:
1. Inside the Delete URL - deleteUrl
1. Inside Query String Parameters  - queryStringParameters

By default, the library uses same url that is used for uploading. DELETE method is used and a call is made. However, in case the delete URL is different from the upload URL, it can be specified per object by using `deleteUrl` params. 

Since there are multiple files in previewMode, every file has their own individual delete URLs. They can be passed individally as follows:

In case the delete of S3 Object is done through lambda function, the lambda API can be specified in deleteUrl.

```typescript
public params: IPsyS3UploaderConfig = {
    uploadApi: "value",
    previewArray: [{
        url: "https://s3.amazonaws.com/uploads.uppy-upload-svc.psyora/obj_1.jpg",
        fileType: "image/jpeg",
        deleteUrl: "https://pbi55zcp89.execute-api.us-east-1.amazonaws.com/poc/upload?key=obj_1",
        canDelete: true
    }, {
        url: "https://s3.amazonaws.com/uploads.uppy-upload-svc.psyora/obj_2.jpg",
        fileType: "image/jpeg",
        deleteUrl: "https://pbi55zcp89.execute-api.us-east-1.amazonaws.com/poc/upload?key=obj_2",
        canDelete: true
    }]
};
```

In case the key is a queryStringParameter, it can be passed as an Object.
```typescript
public params: IPsyS3UploaderConfig = {
    uploadApi: "value",
    previewArray: [{
        url: "https://s3.amazonaws.com/uploads.uppy-upload-svc.psyora/obj_1.jpg",
        fileType: "image/jpeg",
        deleteUrl: "https://pbi55zcp89.execute-api.us-east-1.amazonaws.com/poc/upload",
        canDelete: true,
        queryStringParameters: {
            _key: "obj_1"
        },
    }, {
        url: "https://s3.amazonaws.com/uploads.uppy-upload-svc.psyora/obj_2.jpg",
        fileType: "image/jpeg",
        deleteUrl: "https://pbi55zcp89.execute-api.us-east-1.amazonaws.com/poc/upload",
        canDelete: true,
        queryStringParameters: {
            _key: "obj_2"
        }
    }]
};
```

![Objects before deleting](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/15.objects-before-deleting.png)

The delete process for a file can be initiated by clicking on the '🗑' icon and confirming the prompt.

![Objects confirmation prompt](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/16.objects-delete-confirmation-prompt.png)

![Object during deletion](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/17.objects-delete-in-progress.png)

![Object deleted](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/18.successful-object-delete.png)

---
**Note**
If all files are deleted, then library will switch from 'preview mode' to 'upload mode'.

---

## Patching files through dataURL
In case only base64 string of a file/image is known, it can be provided to the library as **dataURL** to the library using **fileData** parameter. 

The library converts the base64 string into a file and will treat just as normally selected file from local PC. Such files can be then be removed from the library or can be uploaded to S3 bucket in a regular manner.

The data is passed as an array using a @Input() parameter:

```ts
public fileData: IFileBase64Item[] = [{
    "dataURL": "data:image/jpeg;base64,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",
    "name": "image2.jfif",
    "type": "image/jpeg",
  }, {
    "dataURL": "data:image/png;base64,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",
    "name": "kube-pod.png",
    "type": "image/png"
}]
```

The interface **IFileBase64Item**, available from library has the following properties.
```ts
interface IFileBase64Item {
   dataURL: "string",
   name: "string",
   type: string
}
```

It is important to consider the role of **multipleFiles** property when using the **fileData** input params.

In case **multipleFiles** is **false**, then the first element of the array is patched into the library. If it **true**, then the array elements are uploaded as files until the maxFiles limit is reached.

The above base64 data, when patched, will render the following output.

**Single file**

![Single file Patch data](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/19.patch-files-single.png)

**Multiple files**

![Multiple files Patch data](https://s3.ap-south-1.amazonaws.com/images.psyora.com/library-assets/0002.frontend-angular.psy-s3-uploader/20.patch-files-multiple.png)

## API Reference (Library)
### Params
You can further configure the upload behavior through following API's. You can update the params object with the following values.

These values are available in the **IPsyS3UploaderConfig** interface.

```javascript
interface IPsyS3UploaderConfig {
    uploadApi: "string",
    uuid: boolean,
    pathPrefix: "string",
    validExtensions: string[],
    width: "string",
    height: "string",
    multipleFiles: boolean,
    maxFileSizeinMB: boolean
    maxFiles: number,
    previewArray?: IFilePreview[],
    customDeleteUrl?: "string",
}
```
|Param|Description|Required|
|--|--|:--:|
|uploadApi| API Endpoint of the function that generates the pre-signed url |  Y |
|uuid| Changes file name to a random uuid(v4)|N|
|pathPrefix| Allows you to specify a path that will creates/updates a folder in S3 to update the image. Use '/' as a delimter. <br/>**Note**: During input, 'path prefix' should not end with '/' <br/> **Eg:** <span style="color:#ff0000"> "public/" </span> (invalid) <span style="color:green"> "public" </span> (valid) )|N| 
|validExtensions|Overides the default extensions allowed for upload | N|
|width|Specifes the width of the drag area. Default is 20em |N| 
|height|Specifes the height of the drag area. Default is 20em |N| 
|multipleFiles| Allows to switch between single or multiple file upload mode. Default is 'false' | N |
|maxSizeinMB| Allows you to specify the maximum file size allowed for upload in MB. Default is '1 MB' | N |
|maxFiles| Specifies the maximum number of files. Default value is 10. | N |
|previewArray| Provides specifications of images/files to be previewed inside dropzone. The specifications are in the form of IFilePreview |N|


These values are available in the **IFilePreview** interface used in Preview Mode.

```typescript
interface IFilePreview {
    url: "string",
    key?: "string",
    name?: "string",
    canDelete?: "boolean",
    deleteUrl?: "string",
    fileType: "string",
    queryStringParameters?: "object"
}
```

|Param|Description|Required|
|--|--|:--:|
|url| API Endpoint of the file. In case the file is image, preview can be shown | Y |
|fileType| Determines to preview the icon or display an icon from provided application | Y |
|key| S3 Key of the object | N |
|name| Name of the Document | N |
|canDelete| A flag that determines whether to show the delete icon which call the delete method | N |
|deleteUrl| API end point that library should call on invoking the delete method | N |
|queryStringParameters| An object that is converted into queryStringParams while making an query to backend. Eg: (`{ key: "img_key" }` ) to ?key=img_key |N|


### Methods
By using ViewChild() decorator in Parent component, you can call following methods in the library directly.

|Method|Description|
|--|--|
| upload( ) | Initiates file upload process |
| clearAll( ) | Clears all the selected Files |

### API Response
On successful upload, the following objects are returned from library. 
1. **URL** of the uploaded file 
2. **xmlResponse** that is returned from the S3 bucket as text
3. **response** contains details of the uploaded file/files

---
**Note**:
URL and xmlResponse will be depreciated in the future

---

```javacript
interface IPsyS3UploaderResponse {
   url: string | string[],
   xmlResponse: null | string | string[],
   response: IUploadedFileInfo | IUploadedFileInfo[]
};

interface IUploadedFileInfo {
   url: "string",
   xmlResponse: "string",
   name: "string",
   size: number,
   key: "string",
   type: "string"
}
```
```json
{
    "url": "uploaded url of the object",
    "xmlResponse": "the xml response provided by s3 bucket, converted as a string",
    "response": {
        "url": "<uploaded url of the object>",
        "xmlResponse": "<the xml response provided by s3 bucket, converted as a string>",
        "name": "<name of the file>",
        "size": "<size of the file in bytes>",
        "key": "<object key>",
        "type": "<file Type of the object>"
    }
}
```
In case of multiple file upload, the responses are converted into array
```json
{
    "url": "array of uploaded url of the objects",
    "xmlResponse": "the array of xml responses provided by s3 bucket, converted as a string",
    "response": [{
        "url": "<uploaded url of the object>",
        "xmlResponse": "<the xml response provided by s3 bucket, converted as a string>",
        "name": "<name of the file>",
        "size": "<size of the file in bytes>",
        "key": "<object key>",
        "type": "<file Type of the object>"
    }]
}
```
**Note:** The interface is exported from library and can be used in the parent component.

### Events

In preview mode, when selecting or unselecting a file, an event - **fileChange** is emitted.

```ts
interface IOnFileChange {
    "action": eFileChangeStates,
    "index"?: "number",
    "data": {
        "dataUrl": "string",
        "name": "string",
        "type": "string",
        "size": "number"
    }
}

enum eFileChangeStates {
    "fileSelect" = "fileSelect",
    "fileRemove" = "fileRemove"
}
```

For example:

**Single File**
```json
{
    "action": "fileSelect",
    "data": {
        "dataURL": "string",
        "name": "string",
        "type": "string",
        "size": "number"
    }
}
```

**Multiple Files** will have an additional 'index' property and will send out the its position in the file upload data-array.
```json
{
    "action": "fileRemove",
    "index": 0,
    "data": {
        "dataURL": "string",
        "name": "string",
        "type": "string",
        "size": "number"
    }
}
```