@intuitionrobotics/file-upload
Version:
File Uploader - Express & Typescript based backend framework
147 lines • 6.11 kB
JavaScript
import { __stringify, BadImplementationException, generateHex, Minute, Module, Queue } from "@intuitionrobotics/ts-common";
import { BaseHttpModule_Class, BaseHttpRequest, HttpMethod } from "@intuitionrobotics/thunderstorm";
import {} from "../../shared/types.js";
const RequestKey_UploadUrl = 'get-upload-url';
const RequestKey_UploadFile = 'upload-file';
export var FileStatus;
(function (FileStatus) {
FileStatus["ObtainingUrl"] = "ObtainingUrl";
FileStatus["UrlObtained"] = "UrlObtained";
FileStatus["UploadingFile"] = "UploadingFile";
// I can assume that in between I upload and I get
// the push I'm processing the file in the be
FileStatus["PostProcessing"] = "PostProcessing";
FileStatus["Completed"] = "Completed";
FileStatus["Error"] = "Error";
})(FileStatus || (FileStatus = {}));
export class BaseUploaderModule_Class extends Module {
files = {};
uploadQueue = new Queue("File Uploader").setParallelCount(2);
httpModule;
constructor(httpModule, moduleName) {
super(moduleName);
this.httpModule = httpModule;
}
init() {
if (this.config.uploadQueueParallelCount)
this.uploadQueue.setParallelCount(this.config.uploadQueueParallelCount);
}
getFileInfo(id, key) {
return this.files[id] && this.files[id][key];
}
getFullFileInfo(id) {
return this.files[id];
}
setFileInfo(id, key, value) {
if (!this.files[id])
throw new BadImplementationException(`Trying to set ${key} for non existent file with id: ${id}`);
this.files[id][key] = value;
this.files[id].statusCallBack(id, this.files[id].status);
}
uploadImpl(files, _bucketName, _pathPrefix) {
const body = files.map(fileData => {
const fileInfo = {
name: fileData.name,
mimeType: fileData.mimeType,
feId: generateHex(32)
};
if (fileData.key)
fileInfo.key = fileData.key;
if (fileData.public)
fileInfo.public = fileData.public;
this.files[fileInfo.feId] = {
file: fileData.file,
fileName: fileData.file.name,
status: FileStatus.ObtainingUrl,
name: fileData.name,
statusCallBack: fileData.onFileStatusChanged
};
return fileInfo;
});
if (_bucketName)
this.getUrlEndpointWithBucketName({ files: body, pathPrefix: _pathPrefix, bucketName: _bucketName });
else
this.getUrlEndpoint(body);
return body;
}
getUrlEndpoint(body) {
this
.httpModule
.createRequest(HttpMethod.POST, RequestKey_UploadUrl)
.setRelativeUrl('/v1/upload/get-url')
.setJsonBody(body)
.setOnError((request, resError) => {
body.forEach(f => {
this.setFileInfo(f.feId, "messageStatus", __stringify(resError?.debugMessage));
this.setFileInfo(f.feId, "status", FileStatus.Error);
});
})
.execute(async (response) => {
body.forEach(f => this.setFileInfo(f.feId, "status", FileStatus.UrlObtained));
if (!response)
return;
// Not a relevant await but still...
await this.uploadFiles(response);
});
}
getUrlEndpointWithBucketName(body) {
this
.httpModule
.createRequest(HttpMethod.POST, RequestKey_UploadUrl)
.setRelativeUrl('/v1/upload/get-url-with-bucket-name')
.setJsonBody(body)
.setOnError((request, resError) => {
body.files.forEach(f => {
this.setFileInfo(f.feId, "messageStatus", __stringify(resError?.debugMessage));
this.setFileInfo(f.feId, "status", FileStatus.Error);
});
})
.execute(async (response) => {
body.files.forEach(f => this.setFileInfo(f.feId, "status", FileStatus.UrlObtained));
if (!response)
return;
// Not a relevant await but still...
await this.uploadFiles(response);
});
}
uploadFiles = async (response) => {
// Subscribe
await this.subscribeToPush(response);
response.forEach(r => {
const feId = r.tempDoc.feId;
this.uploadQueue.addItem(async () => {
await this.uploadFile(r);
delete this.files[feId].file;
this.setFileInfo(feId, "progress", undefined);
//TODO: Probably need to set a timer here in case we dont get a push back (contingency)
}, () => {
this.setFileInfo(feId, "status", FileStatus.PostProcessing);
}, error => {
this.setFileInfo(feId, "status", FileStatus.Error);
this.setFileInfo(feId, "messageStatus", __stringify(error));
});
});
};
uploadFile = async (response) => {
const feId = response.tempDoc.feId;
this.setFileInfo(feId, "status", FileStatus.UploadingFile);
this.setFileInfo(feId, "tempDoc", response.tempDoc);
const fileInfo = this.files[feId];
if (!fileInfo)
throw new BadImplementationException(`Missing file with id ${feId} and name: ${response.tempDoc.name}`);
const request = this
.httpModule
.createRequest(HttpMethod.PUT, RequestKey_UploadFile)
.setUrl(response.secureUrl)
// Don't change this because it replaces the default headers which we dont need
.setDefaultHeaders({ 'Content-Type': response.tempDoc.mimeType })
.setTimeout(20 * Minute)
.setBody(fileInfo.file)
.setOnProgressListener((ev) => {
this.setFileInfo(feId, "progress", ev.loaded / ev.total);
});
this.setFileInfo(feId, "request", request);
await request.executeSync();
};
}
//# sourceMappingURL=BaseUploaderModule.js.map