Skip to main content

File Uploads Rocket

This package is a configurable rocket to add a storage API to your Booster applications.

Supported Providers

  • Azure Provider
  • AWS Provider
  • Local Provider

Overview

This rocket provides some methods to access files stores in your cloud provider:

  • presignedPut: Returns a presigned put url and the necessary form params. With this url files can be uploaded directly to your provider.
  • presignedGet: Returns a presigned get url to download a file. With this url files can be downloaded directly from your provider.
  • list: Returns a list of files stored in the provider.
  • deleteFile: Removes a file from a directory (only supported in AWS at the moment).

These methods may be used from a Command in your project secured via JWT Token. This rocket also provides a Booster Event each time a file is uploaded.

Usage

Install needed dependency packages:

npm install --save @boostercloud/rocket-file-uploads-core @boostercloud/rocket-file-uploads-types
npm install --save @boostercloud/rocket-file-uploads-azure

Also, you will need a devDependency in your project:

npm install --save-dev @boostercloud/rocket-file-uploads-azure-infrastructure

In your Booster config file, configure your BoosterRocketFiles:

src/config/config.ts
import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'
import { BoosterRocketFiles } from '@boostercloud/rocket-file-uploads-core'
import { RocketFilesUserConfiguration } from '@boostercloud/rocket-file-uploads-types'

const rocketFilesConfigurationDefault: RocketFilesUserConfiguration = {
storageName: 'STORAGE_NAME',
containerName: 'CONTAINER_NAME',
directories: ['DIRECTORY_1', 'DIRECTORY_2'],
}

const rocketFilesConfigurationCms: RocketFilesUserConfiguration = {
storageName: 'cmsst',
containerName: 'rocketfiles',
directories: ['cms1', 'cms2'],
}

Booster.configure('production', (config: BoosterConfig): void => {
config.appName = 'TEST_APP_NAME'
config.providerPackage = '@boostercloud/framework-provider-azure'
config.rockets = [
new BoosterRocketFiles(config, [rocketFilesConfigurationDefault, rocketFilesConfigurationCms]).rocketForAzure(),
]
})

info

Available parameters are:

  • storageName: Name of the storage repository.
  • containerName: Directories container.
  • directories: A list of folders where the files will be stored.

The structure created will be:

├── storageName
│ ├── containerName
│ │ ├── directory

NOTE: Azure Provider will use storageName as the Storage Account Name.

Rocket Methods Usage

Presigned Put

Create a command in your application and call the presignedPut method on the FileHandler class with the directory and filename you want to upload on the storage.

The storageName parameter is optional. It will use the first storage if undefined.

src/commands/file-upload-put.ts
import { Booster, Command } from '@boostercloud/framework-core'
import { Register } from '@boostercloud/framework-types'
import { FileHandler } from '@boostercloud/rocket-file-uploads-core'

@Command({
authorize: 'all',
})
export class FileUploadPut {
public constructor(readonly directory: string, readonly fileName: string, readonly storageName?: string) {}

public static async handle(command: FileUploadPut, register: Register): Promise<string> {
const boosterConfig = Booster.config
const fileHandler = new FileHandler(boosterConfig, command.storageName)
return await fileHandler.presignedPut(command.directory, command.fileName)
}
}

GraphQL Mutation:

mutation {
FileUploadPut(input: {
storageName: "clientst",
directory: "client1",
fileName: "myfile.txt"
}
)
}

Azure Response:

{
"data": {
"FileUploadPut": "https://clientst.blob.core.windows.net/rocketfiles/client1/myfile.txt?<SAS>"
}
}
Presigned Get

Create a command in your application and call the presignedGet method on the FileHandler class with the directory and filename you want to get on the storage.

The storageName parameter is optional. It will use the first storage if undefined.

src/commands/file-upload-get.ts
import { Booster, Command } from '@boostercloud/framework-core'
import { Register } from '@boostercloud/framework-types'
import { FileHandler } from '@boostercloud/rocket-file-uploads-core'

@Command({
authorize: 'all',
})
export class FileUploadGet {
public constructor(readonly directory: string, readonly fileName: string, readonly storageName?: string) {}

public static async handle(command: FileUploadGet, register: Register): Promise<string> {
const boosterConfig = Booster.config
const fileHandler = new FileHandler(boosterConfig, command.storageName)
return await fileHandler.presignedGet(command.directory, command.fileName)
}
}

GraphQL Mutation:

mutation {
FileUploadGet(input: {
storageName: "clientst",
directory: "client1",
fileName: "myfile.txt"
}
)
}

Azure Response:

{
"data": {
"FileUploadGet": "https://clientst.blob.core.windows.net/rocketfiles/folder01%2Fmyfile.txt?<SAS>"
}
}
List

Create a command in your application and call the list method on the FileHandler class with the directory you want to get the info and return the formatted results.

The storageName parameter is optional. It will use the first storage if undefined.

src/commands/file-upload-list.ts
import { Booster, Command } from '@boostercloud/framework-core'
import { Register } from '@boostercloud/framework-types'
import { FileHandler } from '@boostercloud/rocket-file-uploads-core'
import { ListItem } from '@boostercloud/rocket-file-uploads-types'

@Command({
authorize: 'all',
})
export class FileUploadList {
public constructor(readonly directory: string, readonly storageName?: string) {}

public static async handle(command: FileUploadList, register: Register): Promise<Array<ListItem>> {
const boosterConfig = Booster.config
const fileHandler = new FileHandler(boosterConfig, command.storageName)
return await fileHandler.list(command.directory)
}
}

GraphQL Mutation:

mutation {
FileUploadList(input: {
storageName: "clientst",
directory: "client1"
}
)
}

Response:

{
"data": {
"FileUploadList": [
{
"name": "client1/myfile.txt",
"properties": {
"createdOn": "2022-10-26T05:40:47.000Z",
"lastModified": "2022-10-26T05:40:47.000Z",
"contentLength": 6,
"contentType": "text/plain"
}
}
]
}
}
Details

Delete File Currently, the option to delete a file is only available on AWS. If this is a feature you were looking for, please let us know on Discord. Alternatively, you can implement this feature and submit a pull request on GitHub for this Rocket!

Azure Roles

info

Starting at version 0.31.0 this Rocket use Managed Identities instead of Connection Strings. Please, check that you have the required permissions to assign roles https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal-managed-identity#prerequisites

For uploading files to Azure you need the Storage Blob Data Contributor role. This can be assigned to a user using the portal or with the next scripts:

First, check if you have the correct permissions:

ACCOUNT_NAME="<STORAGE ACCOUNT NAME>"
CONTAINER_NAME="<CONTAINER NAME>"

# use this to test if you have the correct permissions
az storage blob exists --account-name $ACCOUNT_NAME `
--container-name $CONTAINER_NAME `
--name blob1.txt --auth-mode login

If you don't have it, then run this script as admin:

ACCOUNT_NAME="<STORAGE ACCOUNT NAME>"
CONTAINER_NAME="<CONTAINER NAME>"

OBJECT_ID=$(az ad user list --query "[?mailNickname=='<YOUR MAIL NICK NAME>'].objectId" -o tsv)
STORAGE_ID=$(az storage account show -n $ACCOUNT_NAME --query id -o tsv)

az role assignment create \
--role "Storage Blob Data Contributor" \
--assignee $OBJECT_ID \
--scope "$STORAGE_ID/blobServices/default/containers/$CONTAINER_NAME"

Events

For each uploaded file a new event will be automatically generated and properly reduced on the entity UploadedFileEntity.

The event will look like this:

{
"version": 1,
"kind": "snapshot",
"superKind": "domain",
"requestID": "xxx",
"entityID": "xxxx",
"entityTypeName": "UploadedFileEntity",
"typeName": "UploadedFileEntity",
"value": {
"id": "xxx",
"metadata": {
// A bunch of fields (depending on Azure or AWS)
}
},
"createdAt": "2022-10-26T10:23:36.562Z",
"snapshottedEventCreatedAt": "2022-10-26T10:23:32.34Z",
"entityTypeName_entityID_kind": "UploadedFileEntity-xxx-b842-x-8975-xx-snapshot",
"id": "x-x-x-x-x",
"_rid": "x==",
"_self": "dbs/x==/colls/x=/docs/x==/",
"_etag": "\"x-x-0500-0000-x\"",
"_attachments": "attachments/",
"_ts": 123456
}

TODOs

  • Add file deletion to Azure and Local (only supported in AWS at the moment).
  • Optional storage deletion when unmounting the stack.
  • Optional events, in case you don't want to store that information in the events-store.
  • When deleting a file, save a deletion event in the events-store. Only uploads are stored at the moment.