@cirrobio/sdk 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/api/error-handler.d.ts +8 -0
  2. package/dist/api/error-handler.js +37 -0
  3. package/dist/api.d.ts +1 -8
  4. package/dist/api.js +4 -103
  5. package/dist/file/__test__/s3-utils.test.d.ts +1 -0
  6. package/dist/file/__test__/s3-utils.test.js +16 -0
  7. package/dist/file/actions/delete.fn.d.ts +9 -0
  8. package/dist/file/actions/delete.fn.js +15 -0
  9. package/dist/file/actions/sign-url.fn.d.ts +15 -0
  10. package/dist/file/actions/sign-url.fn.js +26 -0
  11. package/dist/file/actions/upload.fn.d.ts +13 -0
  12. package/dist/file/actions/upload.fn.js +23 -0
  13. package/dist/{extensions.fn.js → file/extensions.fn.js} +21 -16
  14. package/dist/file/file-object.model.d.ts +54 -0
  15. package/dist/file/file-object.model.js +8 -0
  16. package/dist/file/file.service.d.ts +29 -0
  17. package/dist/file/file.service.js +75 -0
  18. package/dist/file/project-access-context.d.ts +14 -0
  19. package/dist/file/project-access-context.js +24 -0
  20. package/dist/file/shared.d.ts +5 -0
  21. package/dist/file/shared.js +11 -0
  22. package/dist/file/util/credentials-mutex.so.d.ts +15 -0
  23. package/dist/file/util/credentials-mutex.so.js +32 -0
  24. package/dist/file/util/s3-client.d.ts +6 -0
  25. package/dist/file/util/s3-client.js +19 -0
  26. package/dist/file/util/s3-utils.d.ts +7 -0
  27. package/dist/file/util/s3-utils.js +17 -0
  28. package/dist/file.d.ts +11 -0
  29. package/dist/file.js +38 -0
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +1 -1
  32. package/package.json +16 -3
  33. package/src/api/error-handler.ts +36 -0
  34. package/src/api.ts +1 -36
  35. package/src/file/__test__/s3-utils.test.ts +17 -0
  36. package/src/file/actions/delete.fn.ts +18 -0
  37. package/src/file/actions/sign-url.fn.ts +35 -0
  38. package/src/file/actions/upload.fn.ts +30 -0
  39. package/src/{extensions.fn.ts → file/extensions.fn.ts} +1 -2
  40. package/src/file/file-object.model.ts +57 -0
  41. package/src/file/file.service.ts +79 -0
  42. package/src/file/project-access-context.ts +26 -0
  43. package/src/file/shared.ts +7 -0
  44. package/src/file/util/credentials-mutex.so.ts +33 -0
  45. package/src/file/util/s3-client.ts +17 -0
  46. package/src/file/util/s3-utils.ts +14 -0
  47. package/src/file.ts +11 -0
  48. package/src/index.ts +2 -2
  49. package/tsconfig.json +3 -3
  50. /package/dist/{extensions.fn.d.ts → file/extensions.fn.d.ts} +0 -0
@@ -0,0 +1,8 @@
1
+ import { Middleware, ResponseContext } from "@cirrobio/api-client";
2
+ export declare class ApiError extends Error {
3
+ errors: string[];
4
+ constructor(message: string, errors: string[]);
5
+ }
6
+ export declare class PortalErrorHandler implements Middleware {
7
+ post(context: ResponseContext): Promise<Response | void>;
8
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PortalErrorHandler = exports.ApiError = void 0;
4
+ class ApiError extends Error {
5
+ constructor(message, errors) {
6
+ super(message);
7
+ this.errors = errors;
8
+ }
9
+ }
10
+ exports.ApiError = ApiError;
11
+ class PortalErrorHandler {
12
+ async post(context) {
13
+ const { response } = context;
14
+ if (response && (response.status >= 200 && response.status < 300)) {
15
+ return response;
16
+ }
17
+ // Handle Error
18
+ let errorMessage;
19
+ const errors = [];
20
+ try {
21
+ const err = await response.json();
22
+ console.warn(err);
23
+ if ('errorDetail' in err) {
24
+ errorMessage = err.errorDetail;
25
+ errors.push(err.errors.map((e) => e.message));
26
+ }
27
+ else {
28
+ errorMessage = err.message;
29
+ }
30
+ }
31
+ catch (ignore) {
32
+ errorMessage = "Unknown Error";
33
+ }
34
+ throw new ApiError(errorMessage, errors);
35
+ }
36
+ }
37
+ exports.PortalErrorHandler = PortalErrorHandler;
package/dist/api.d.ts CHANGED
@@ -1,8 +1 @@
1
- import { Middleware, ResponseContext } from "@cirrobio/api-client";
2
- export declare class ApiError extends Error {
3
- errors: string[];
4
- constructor(message: string, errors: string[]);
5
- }
6
- export declare class PortalErrorHandler implements Middleware {
7
- post(context: ResponseContext): Promise<Response | void>;
8
- }
1
+ export { PortalErrorHandler, ApiError } from './api/error-handler';
package/dist/api.js CHANGED
@@ -1,105 +1,6 @@
1
1
  "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- if (typeof b !== "function" && b !== null)
11
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
- return new (P || (P = Promise))(function (resolve, reject) {
20
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
- step((generator = generator.apply(thisArg, _arguments || [])).next());
24
- });
25
- };
26
- var __generator = (this && this.__generator) || function (thisArg, body) {
27
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
- function verb(n) { return function (v) { return step([n, v]); }; }
30
- function step(op) {
31
- if (f) throw new TypeError("Generator is already executing.");
32
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
- if (y = 0, t) op = [op[0] & 2, t.value];
35
- switch (op[0]) {
36
- case 0: case 1: t = op; break;
37
- case 4: _.label++; return { value: op[1], done: false };
38
- case 5: _.label++; y = op[1]; op = [0]; continue;
39
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
- default:
41
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
- if (t[2]) _.ops.pop();
46
- _.trys.pop(); continue;
47
- }
48
- op = body.call(thisArg, _);
49
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
- }
52
- };
53
2
  Object.defineProperty(exports, "__esModule", { value: true });
54
- exports.PortalErrorHandler = exports.ApiError = void 0;
55
- var ApiError = /** @class */ (function (_super) {
56
- __extends(ApiError, _super);
57
- function ApiError(message, errors) {
58
- var _this = _super.call(this, message) || this;
59
- _this.errors = errors;
60
- return _this;
61
- }
62
- return ApiError;
63
- }(Error));
64
- exports.ApiError = ApiError;
65
- var PortalErrorHandler = /** @class */ (function () {
66
- function PortalErrorHandler() {
67
- }
68
- PortalErrorHandler.prototype.post = function (context) {
69
- return __awaiter(this, void 0, void 0, function () {
70
- var response, errorMessage, errors, err, ignore_1;
71
- return __generator(this, function (_a) {
72
- switch (_a.label) {
73
- case 0:
74
- response = context.response;
75
- if (response && (response.status >= 200 && response.status < 300)) {
76
- return [2 /*return*/, response];
77
- }
78
- errors = [];
79
- _a.label = 1;
80
- case 1:
81
- _a.trys.push([1, 3, , 4]);
82
- return [4 /*yield*/, response.json()];
83
- case 2:
84
- err = _a.sent();
85
- console.warn(err);
86
- if ('errorDetail' in err) {
87
- errorMessage = err.errorDetail;
88
- errors.push(err.errors.map(function (e) { return e.message; }));
89
- }
90
- else {
91
- errorMessage = err.message;
92
- }
93
- return [3 /*break*/, 4];
94
- case 3:
95
- ignore_1 = _a.sent();
96
- errorMessage = "Unknown Error";
97
- return [3 /*break*/, 4];
98
- case 4: throw new ApiError(errorMessage, errors);
99
- }
100
- });
101
- });
102
- };
103
- return PortalErrorHandler;
104
- }());
105
- exports.PortalErrorHandler = PortalErrorHandler;
3
+ exports.ApiError = exports.PortalErrorHandler = void 0;
4
+ var error_handler_1 = require("./api/error-handler");
5
+ Object.defineProperty(exports, "PortalErrorHandler", { enumerable: true, get: function () { return error_handler_1.PortalErrorHandler; } });
6
+ Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return error_handler_1.ApiError; } });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const file_1 = require("../../file");
4
+ describe("s3-utils", () => {
5
+ describe("s3UriToParams", () => {
6
+ it("should parse a valid s3 URI", () => {
7
+ const uri = "s3://my-bucket/my-key/nested/folder";
8
+ const result = (0, file_1.s3UriToParams)(uri);
9
+ expect(result).toEqual({ Bucket: "my-bucket", Key: "my-key/nested/folder" });
10
+ });
11
+ it("should throw an error for an invalid URI", () => {
12
+ const uri = "invalid-uri";
13
+ expect(() => (0, file_1.s3UriToParams)(uri)).toThrowError(`Received invalid uri: '${uri}'`);
14
+ });
15
+ });
16
+ });
@@ -0,0 +1,9 @@
1
+ import { AWSCredentials } from "@cirrobio/api-client";
2
+ export interface DeleteFileParams {
3
+ url: string;
4
+ credentials: AWSCredentials;
5
+ }
6
+ /**
7
+ * Delete a file from S3 given its URL and credentials.
8
+ */
9
+ export declare function deleteFile({ url, credentials }: DeleteFileParams): Promise<void>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteFile = void 0;
4
+ const client_s3_1 = require("@aws-sdk/client-s3");
5
+ const s3_utils_1 = require("../util/s3-utils");
6
+ const s3_client_1 = require("../util/s3-client");
7
+ /**
8
+ * Delete a file from S3 given its URL and credentials.
9
+ */
10
+ async function deleteFile({ url, credentials }) {
11
+ const { Bucket, Key } = (0, s3_utils_1.s3UriToParams)(url);
12
+ const cmd = new client_s3_1.DeleteObjectCommand({ Bucket, Key });
13
+ await (0, s3_client_1.createS3Client)(credentials).send(cmd);
14
+ }
15
+ exports.deleteFile = deleteFile;
@@ -0,0 +1,15 @@
1
+ import { AWSCredentials } from "@cirrobio/api-client";
2
+ export interface GetFileUrlParams extends GetSignedUrlOptions {
3
+ url: string;
4
+ credentials?: AWSCredentials;
5
+ }
6
+ export interface GetSignedUrlOptions {
7
+ download?: boolean;
8
+ gzip?: boolean;
9
+ timeout?: number;
10
+ filename?: string;
11
+ }
12
+ /**
13
+ * Get a signed URL for a file in S3 given its S3 URI.
14
+ */
15
+ export declare function getSignedUrl({ url, credentials, ...params }: GetFileUrlParams): Promise<string>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSignedUrl = void 0;
4
+ const client_s3_1 = require("@aws-sdk/client-s3");
5
+ const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
6
+ const s3_client_1 = require("../util/s3-client");
7
+ const s3_utils_1 = require("../util/s3-utils");
8
+ /**
9
+ * Get a signed URL for a file in S3 given its S3 URI.
10
+ */
11
+ function getSignedUrl({ url, credentials, ...params }) {
12
+ var _a, _b;
13
+ const client = (0, s3_client_1.createS3Client)(credentials);
14
+ const { Bucket, Key } = (0, s3_utils_1.s3UriToParams)(url);
15
+ const fileName = (_a = params.filename) !== null && _a !== void 0 ? _a : Key.split('/').pop();
16
+ const args = { Bucket, Key };
17
+ if (params === null || params === void 0 ? void 0 : params.download) {
18
+ args.ResponseContentDisposition = `attachment; filename="${fileName}"`;
19
+ }
20
+ if (params === null || params === void 0 ? void 0 : params.gzip) {
21
+ args.ResponseContentEncoding = 'gzip';
22
+ }
23
+ const command = new client_s3_1.GetObjectCommand(args);
24
+ return (0, s3_request_presigner_1.getSignedUrl)(client, command, { expiresIn: 60 * ((_b = params === null || params === void 0 ? void 0 : params.timeout) !== null && _b !== void 0 ? _b : 5) });
25
+ }
26
+ exports.getSignedUrl = getSignedUrl;
@@ -0,0 +1,13 @@
1
+ import { Upload } from "@aws-sdk/lib-storage";
2
+ import { AWSCredentials } from '@cirrobio/api-client';
3
+ export interface UploadFileParams {
4
+ bucket: string;
5
+ path: string;
6
+ file: File;
7
+ credentials: AWSCredentials;
8
+ metadata?: Record<string, string>;
9
+ }
10
+ /**
11
+ * Upload a file to S3
12
+ */
13
+ export declare function uploadFile({ bucket, path, file, credentials, metadata }: UploadFileParams): Upload;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadFile = void 0;
4
+ const lib_storage_1 = require("@aws-sdk/lib-storage");
5
+ const s3_client_1 = require("../util/s3-client");
6
+ /**
7
+ * Upload a file to S3
8
+ */
9
+ function uploadFile({ bucket, path, file, credentials, metadata }) {
10
+ const params = {
11
+ Bucket: bucket,
12
+ Key: path,
13
+ Body: file,
14
+ ContentType: file.type,
15
+ Metadata: metadata,
16
+ };
17
+ return new lib_storage_1.Upload({
18
+ client: (0, s3_client_1.createS3Client)(credentials),
19
+ queueSize: 4,
20
+ params,
21
+ });
22
+ }
23
+ exports.uploadFile = uploadFile;
@@ -1,18 +1,9 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesExtension = exports.FILE_EXTENSIONS_TO_OPEN = exports.FILE_OME_EXTENSIONS = exports.FILE_IMAGE_EXTENSIONS = exports.FILE_TXT_EXTENSIONS = exports.FILE_TXT_GENOMICS_EXTENSIONS = exports.FILE_DSV_EXTENSIONS = exports.FILE_PROTEIN_STRUCTURE_EXTENSIONS = exports.FILE_BROWSER_EXTENSIONS = exports.FILE_TRACK_EXTENSIONS = exports.FILE_TRACK_EXTENSIONS_NO_INDEX = exports.FILE_VITESSCE_EXTENSIONS = exports.FILE_TRACK_INDEX_EXTENSIONS = exports.FILE_TRACK_SEG = exports.FILE_TRACK_WIG = exports.FILE_TRACK_VARIANT = exports.FILE_TRACK_ALIGNMENTS = exports.FILE_TRACK_ANNOTATION = void 0;
2
4
  /**
3
5
  * An array of file extensions that can be rendered in a genome viewer
4
6
  */
5
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
6
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
7
- if (ar || !(i in from)) {
8
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
9
- ar[i] = from[i];
10
- }
11
- }
12
- return to.concat(ar || Array.prototype.slice.call(from));
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.matchesExtension = exports.FILE_EXTENSIONS_TO_OPEN = exports.FILE_OME_EXTENSIONS = exports.FILE_IMAGE_EXTENSIONS = exports.FILE_TXT_EXTENSIONS = exports.FILE_TXT_GENOMICS_EXTENSIONS = exports.FILE_DSV_EXTENSIONS = exports.FILE_PROTEIN_STRUCTURE_EXTENSIONS = exports.FILE_BROWSER_EXTENSIONS = exports.FILE_TRACK_EXTENSIONS = exports.FILE_TRACK_EXTENSIONS_NO_INDEX = exports.FILE_VITESSCE_EXTENSIONS = exports.FILE_TRACK_INDEX_EXTENSIONS = exports.FILE_TRACK_SEG = exports.FILE_TRACK_WIG = exports.FILE_TRACK_VARIANT = exports.FILE_TRACK_ALIGNMENTS = exports.FILE_TRACK_ANNOTATION = void 0;
16
7
  exports.FILE_TRACK_ANNOTATION = ['bed', 'bed.gz', 'gtf', 'gtf.gz'];
17
8
  exports.FILE_TRACK_ALIGNMENTS = ['cram', 'cram.gz']; // TODO: put back bam
18
9
  exports.FILE_TRACK_VARIANT = ['vcf', 'vcf.gz'];
@@ -20,8 +11,17 @@ exports.FILE_TRACK_WIG = ['wig', 'wig.gz', 'bw', 'bw.gz', 'bigwig', 'bigwig.gz']
20
11
  exports.FILE_TRACK_SEG = ['seg', 'seg.gz'];
21
12
  exports.FILE_TRACK_INDEX_EXTENSIONS = ['tbi', 'bai', 'crai', 'csi'];
22
13
  exports.FILE_VITESSCE_EXTENSIONS = ['hdf5', 'h5ad', 'loom'];
23
- exports.FILE_TRACK_EXTENSIONS_NO_INDEX = __spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], exports.FILE_TRACK_ANNOTATION, true), exports.FILE_TRACK_ALIGNMENTS, true), exports.FILE_TRACK_VARIANT, true), exports.FILE_TRACK_WIG, true), exports.FILE_TRACK_SEG, true);
24
- exports.FILE_TRACK_EXTENSIONS = __spreadArray(__spreadArray([], exports.FILE_TRACK_EXTENSIONS_NO_INDEX, true), exports.FILE_TRACK_INDEX_EXTENSIONS, true);
14
+ exports.FILE_TRACK_EXTENSIONS_NO_INDEX = [
15
+ ...exports.FILE_TRACK_ANNOTATION,
16
+ ...exports.FILE_TRACK_ALIGNMENTS,
17
+ ...exports.FILE_TRACK_VARIANT,
18
+ ...exports.FILE_TRACK_WIG,
19
+ ...exports.FILE_TRACK_SEG
20
+ ];
21
+ exports.FILE_TRACK_EXTENSIONS = [
22
+ ...exports.FILE_TRACK_EXTENSIONS_NO_INDEX,
23
+ ...exports.FILE_TRACK_INDEX_EXTENSIONS
24
+ ];
25
25
  exports.FILE_BROWSER_EXTENSIONS = ['html', 'pdf'];
26
26
  /**
27
27
  * An array of file extensions that are protein structure files.
@@ -38,7 +38,7 @@ exports.FILE_TXT_GENOMICS_EXTENSIONS = ['fasta', 'fna', 'fsa', 'fa', 'fastp', 'f
38
38
  /**
39
39
  * An array of file extensions that are considered TXT files.
40
40
  */
41
- exports.FILE_TXT_EXTENSIONS = __spreadArray(__spreadArray(['txt', 'log', 'yml', 'cfg', 'xml', 'yaml'], exports.FILE_DSV_EXTENSIONS, true), exports.FILE_TXT_GENOMICS_EXTENSIONS, true);
41
+ exports.FILE_TXT_EXTENSIONS = ['txt', 'log', 'yml', 'cfg', 'xml', 'yaml', ...exports.FILE_DSV_EXTENSIONS, ...exports.FILE_TXT_GENOMICS_EXTENSIONS];
42
42
  /**
43
43
  * An array of file extensions that are considered image files.
44
44
  */
@@ -51,7 +51,12 @@ exports.FILE_OME_EXTENSIONS = ['tif', 'ome.tif', 'ome.tiff', 'ome.tif.gz', 'ome.
51
51
  * An array of file extensions that can be opened in the browser.
52
52
  * Includes common document formats such as HTML, PDF, and JSON, as well as image, DSV, TXT, and OME file formats.
53
53
  */
54
- exports.FILE_EXTENSIONS_TO_OPEN = __spreadArray(__spreadArray(__spreadArray(__spreadArray(['html', 'pdf', 'json', 'fcs'], exports.FILE_IMAGE_EXTENSIONS, true), exports.FILE_TXT_EXTENSIONS, true), exports.FILE_TRACK_EXTENSIONS, true), exports.FILE_PROTEIN_STRUCTURE_EXTENSIONS, true);
54
+ exports.FILE_EXTENSIONS_TO_OPEN = ['html', 'pdf', 'json', 'fcs',
55
+ ...exports.FILE_IMAGE_EXTENSIONS,
56
+ ...exports.FILE_TXT_EXTENSIONS,
57
+ ...exports.FILE_TRACK_EXTENSIONS,
58
+ ...exports.FILE_PROTEIN_STRUCTURE_EXTENSIONS
59
+ ];
55
60
  /**
56
61
  * Checks if a file has an extension that matches one in the provided list.
57
62
  * @param filePath The file to check the extension of.
@@ -65,7 +70,7 @@ function matchesExtension(filePath, extensions) {
65
70
  filePath = filePath.slice(0, -3); // remove the .gz extension
66
71
  }
67
72
  // Now, get the extension of the file
68
- var fileExtension = filePath.slice(filePath.lastIndexOf('.') + 1);
73
+ const fileExtension = filePath.slice(filePath.lastIndexOf('.') + 1);
69
74
  // Check if the fileExtension is in the list of extensions
70
75
  return extensions.includes(fileExtension);
71
76
  }
@@ -0,0 +1,54 @@
1
+ import { ProjectFileAccessContext } from "./project-access-context";
2
+ export declare enum FileSystemObjectType {
3
+ FILE = "file",
4
+ FOLDER = "folder"
5
+ }
6
+ /**
7
+ * Represents a file that can be downloaded
8
+ */
9
+ export interface DownloadableFile {
10
+ url: string;
11
+ name?: string;
12
+ fileAccessContext: ProjectFileAccessContext;
13
+ }
14
+ /**
15
+ * Represents a file in Cirro
16
+ */
17
+ export interface FileSystemObject extends DownloadableFile {
18
+ /**
19
+ * Unique Id For Object
20
+ */
21
+ id: string;
22
+ /**
23
+ * Object S3 Url
24
+ */
25
+ url: string;
26
+ /**
27
+ * Object Path
28
+ */
29
+ path: string;
30
+ /**
31
+ * Object Name
32
+ */
33
+ name: string;
34
+ /**
35
+ * Object Size
36
+ */
37
+ size: number;
38
+ /**
39
+ * Object Kind (PNG, TXT)
40
+ */
41
+ kind: string;
42
+ /**
43
+ * Object Type (File or Folder)
44
+ */
45
+ type: FileSystemObjectType;
46
+ /**
47
+ * Metadata
48
+ */
49
+ metadata?: Record<string, any>;
50
+ /**
51
+ * Access context
52
+ */
53
+ fileAccessContext: ProjectFileAccessContext;
54
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileSystemObjectType = void 0;
4
+ var FileSystemObjectType;
5
+ (function (FileSystemObjectType) {
6
+ FileSystemObjectType["FILE"] = "file";
7
+ FileSystemObjectType["FOLDER"] = "folder";
8
+ })(FileSystemObjectType = exports.FileSystemObjectType || (exports.FileSystemObjectType = {}));
@@ -0,0 +1,29 @@
1
+ import { AWSCredentials, FileApi } from "@cirrobio/api-client";
2
+ import { ProjectFileAccessContext } from "./project-access-context";
3
+ import { DownloadableFile } from "./file-object.model";
4
+ import { GetSignedUrlOptions, GetFileUrlParams } from "./actions/sign-url.fn";
5
+ /**
6
+ * Service for viewing files in Cirro
7
+ * currently this only operates on files within a project
8
+ */
9
+ export declare class FileService {
10
+ private readonly fileApi;
11
+ constructor(fileApi: FileApi);
12
+ /**
13
+ * Get contents of a file
14
+ */
15
+ getProjectFile(file: DownloadableFile): Promise<Response>;
16
+ /**
17
+ * Get a signed URL for a file
18
+ */
19
+ getSignedUrlFromProjectFile(file: DownloadableFile, params?: GetSignedUrlOptions): Promise<string>;
20
+ /**
21
+ * Get a signed URL for a file given a path
22
+ */
23
+ getSignedUrlFromProjectPath(fileAccessContext: ProjectFileAccessContext, path: string, params?: GetFileUrlParams): Promise<string>;
24
+ /**
25
+ * Get credentials for accessing a project file
26
+ */
27
+ getProjectAccessCredentials(fileAccessContext: ProjectFileAccessContext): Promise<AWSCredentials>;
28
+ private getProjectReadCredentials;
29
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileService = void 0;
4
+ const api_client_1 = require("@cirrobio/api-client");
5
+ const credentials_mutex_so_1 = require("./util/credentials-mutex.so");
6
+ const sign_url_fn_1 = require("./actions/sign-url.fn");
7
+ const shared_1 = require("./shared");
8
+ /**
9
+ * Service for viewing files in Cirro
10
+ * currently this only operates on files within a project
11
+ */
12
+ class FileService {
13
+ constructor(fileApi) {
14
+ this.fileApi = fileApi;
15
+ }
16
+ /**
17
+ * Get contents of a file
18
+ */
19
+ async getProjectFile(file) {
20
+ const url = await this.getSignedUrlFromProjectFile(file);
21
+ return fetch(url);
22
+ }
23
+ /**
24
+ * Get a signed URL for a file
25
+ */
26
+ async getSignedUrlFromProjectFile(file, params) {
27
+ const credentials = await this.getProjectAccessCredentials(file.fileAccessContext);
28
+ const _params = {
29
+ ...params,
30
+ filename: file.name,
31
+ url: file.url,
32
+ credentials,
33
+ };
34
+ return (0, sign_url_fn_1.getSignedUrl)(_params);
35
+ }
36
+ /**
37
+ * Get a signed URL for a file given a path
38
+ */
39
+ async getSignedUrlFromProjectPath(fileAccessContext, path, params) {
40
+ const credentials = await this.getProjectAccessCredentials(fileAccessContext);
41
+ const _params = {
42
+ ...params,
43
+ filename: path.split('/').pop(),
44
+ url: `s3://${(0, shared_1.getProjectS3Bucket)(fileAccessContext.project.id)}/${path}`,
45
+ credentials,
46
+ };
47
+ return (0, sign_url_fn_1.getSignedUrl)(_params);
48
+ }
49
+ /**
50
+ * Get credentials for accessing a project file
51
+ */
52
+ async getProjectAccessCredentials(fileAccessContext) {
53
+ // Special case for project download, since we can cache the credentials
54
+ if (fileAccessContext.fileAccessRequest.accessType === api_client_1.AccessType.ProjectDownload) {
55
+ return this.getProjectReadCredentials(fileAccessContext);
56
+ }
57
+ return this.fileApi.generateProjectFileAccessToken({ projectId: fileAccessContext.project.id, fileAccessRequest: fileAccessContext.fileAccessRequest });
58
+ }
59
+ async getProjectReadCredentials(fileAccessContext) {
60
+ const projectId = fileAccessContext.project.id;
61
+ return credentials_mutex_so_1.credentialsMutex.dispatch(async () => {
62
+ const cachedCredentials = credentials_mutex_so_1.credentialsCache.get(projectId);
63
+ const expirationTime = cachedCredentials ? cachedCredentials === null || cachedCredentials === void 0 ? void 0 : cachedCredentials.expiration : null;
64
+ const fetchNewCredentials = !expirationTime || expirationTime < new Date();
65
+ if (fetchNewCredentials) {
66
+ const fileAccessRequest = fileAccessContext.fileAccessRequest;
67
+ const credentials = await this.fileApi.generateProjectFileAccessToken({ projectId, fileAccessRequest });
68
+ credentials_mutex_so_1.credentialsCache.set(projectId, credentials);
69
+ return credentials;
70
+ }
71
+ return cachedCredentials;
72
+ });
73
+ }
74
+ }
75
+ exports.FileService = FileService;
@@ -0,0 +1,14 @@
1
+ import { DatasetDetail, FileAccessRequest, Project } from "@cirrobio/api-client";
2
+ type ProjectIdentifiable = Pick<Project, 'id'>;
3
+ /**
4
+ * Helper class to encapsulate the file access for a project.
5
+ */
6
+ export declare class ProjectFileAccessContext {
7
+ readonly project: ProjectIdentifiable;
8
+ readonly dataset: DatasetDetail;
9
+ readonly fileAccessRequest: FileAccessRequest;
10
+ constructor(project: ProjectIdentifiable, dataset: DatasetDetail, fileAccessRequest: FileAccessRequest);
11
+ static projectDownload(project: ProjectIdentifiable): ProjectFileAccessContext;
12
+ static datasetDownload(project: ProjectIdentifiable, dataset: DatasetDetail): ProjectFileAccessContext;
13
+ }
14
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProjectFileAccessContext = void 0;
4
+ const api_client_1 = require("@cirrobio/api-client");
5
+ /**
6
+ * Helper class to encapsulate the file access for a project.
7
+ */
8
+ class ProjectFileAccessContext {
9
+ constructor(project, dataset, fileAccessRequest) {
10
+ this.project = project;
11
+ this.dataset = dataset;
12
+ this.fileAccessRequest = fileAccessRequest;
13
+ }
14
+ static projectDownload(project) {
15
+ const request = { accessType: api_client_1.AccessType.ProjectDownload };
16
+ return new ProjectFileAccessContext(project, null, request);
17
+ }
18
+ static datasetDownload(project, dataset) {
19
+ const accessType = dataset.share ? api_client_1.AccessType.SharedDatasetDownload : api_client_1.AccessType.ProjectDownload;
20
+ const request = { accessType, datasetId: dataset.id };
21
+ return new ProjectFileAccessContext(project, dataset, request);
22
+ }
23
+ }
24
+ exports.ProjectFileAccessContext = ProjectFileAccessContext;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Get the S3 bucket name for a project.
3
+ * Will be deprecated in the future.
4
+ */
5
+ export declare function getProjectS3Bucket(projectId: string): string;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProjectS3Bucket = void 0;
4
+ /**
5
+ * Get the S3 bucket name for a project.
6
+ * Will be deprecated in the future.
7
+ */
8
+ function getProjectS3Bucket(projectId) {
9
+ return `project-${projectId}`;
10
+ }
11
+ exports.getProjectS3Bucket = getProjectS3Bucket;
@@ -0,0 +1,15 @@
1
+ import { AWSCredentials } from '@cirrobio/api-client';
2
+ declare class Mutex<T> {
3
+ private mutex;
4
+ lock(): PromiseLike<() => void>;
5
+ dispatch(fn: (() => T) | (() => PromiseLike<T>)): Promise<T>;
6
+ }
7
+ /**
8
+ * A mutex to ensure that only one request for credentials is made at a time.
9
+ */
10
+ export declare const credentialsMutex: Mutex<AWSCredentials>;
11
+ /**
12
+ * A cache of credentials to avoid making multiple requests for the same credentials.
13
+ */
14
+ export declare const credentialsCache: Map<string, AWSCredentials>;
15
+ export {};
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.credentialsCache = exports.credentialsMutex = void 0;
4
+ class Mutex {
5
+ constructor() {
6
+ this.mutex = Promise.resolve();
7
+ }
8
+ lock() {
9
+ let begin = () => { };
10
+ this.mutex = this.mutex.then(() => new Promise(begin));
11
+ return new Promise((res) => {
12
+ begin = res;
13
+ });
14
+ }
15
+ async dispatch(fn) {
16
+ const unlock = await this.lock();
17
+ try {
18
+ return await Promise.resolve(fn());
19
+ }
20
+ finally {
21
+ unlock();
22
+ }
23
+ }
24
+ }
25
+ /**
26
+ * A mutex to ensure that only one request for credentials is made at a time.
27
+ */
28
+ exports.credentialsMutex = new Mutex();
29
+ /**
30
+ * A cache of credentials to avoid making multiple requests for the same credentials.
31
+ */
32
+ exports.credentialsCache = new Map();
@@ -0,0 +1,6 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ import { AWSCredentials } from "@cirrobio/api-client";
3
+ /**
4
+ * Creates an S3 client using the provided credentials.
5
+ */
6
+ export declare function createS3Client(credentials: AWSCredentials): S3Client;