From adb32e88e7bc741441d2232a31101b54d76eca86 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Fri, 5 Jan 2024 12:19:02 -0300 Subject: [PATCH 01/36] Add SOS support --- packages/pangea-node-sdk/src/index.ts | 1 + packages/pangea-node-sdk/src/request.ts | 32 +- packages/pangea-node-sdk/src/response.ts | 56 ++- packages/pangea-node-sdk/src/services/base.ts | 6 +- .../pangea-node-sdk/src/services/index.ts | 2 + .../pangea-node-sdk/src/services/store.ts | 130 ++++++ packages/pangea-node-sdk/src/types.ts | 434 ++++++++++++++++++ .../pangea-node-sdk/src/utils/multipart.ts | 162 +++++++ .../tests/integration/store.test.ts | 367 +++++++++++++++ 9 files changed, 1183 insertions(+), 7 deletions(-) create mode 100644 packages/pangea-node-sdk/src/services/store.ts create mode 100644 packages/pangea-node-sdk/src/utils/multipart.ts create mode 100644 packages/pangea-node-sdk/tests/integration/store.test.ts diff --git a/packages/pangea-node-sdk/src/index.ts b/packages/pangea-node-sdk/src/index.ts index e53af2292..99835b661 100755 --- a/packages/pangea-node-sdk/src/index.ts +++ b/packages/pangea-node-sdk/src/index.ts @@ -39,3 +39,4 @@ export const URLIntelService = services.URLIntelService; export const UserIntelService = services.UserIntelService; export const VaultService = services.VaultService; export const FileScanService = services.FileScanService; +export const StoreService = services.StoreService; diff --git a/packages/pangea-node-sdk/src/request.ts b/packages/pangea-node-sdk/src/request.ts index 9e7d6810a..332bfc036 100644 --- a/packages/pangea-node-sdk/src/request.ts +++ b/packages/pangea-node-sdk/src/request.ts @@ -1,4 +1,4 @@ -import got, { Options, HTTPError } from "got"; +import got, { Options, HTTPError, ResponseType } from "got"; import type { Headers, Response } from "got"; import FormData from "form-data"; import fs from "fs"; @@ -7,7 +7,8 @@ import { FileData, FileItems, PostOptions, TransferMethod } from "./types.js"; import PangeaConfig, { version } from "./config.js"; import { ConfigEnv } from "./types.js"; import { PangeaErrors } from "./errors.js"; -import { PangeaResponse, ResponseObject } from "./response.js"; +import { AttachedFile, PangeaResponse, ResponseObject } from "./response.js"; +import { getHeaderField } from "./utils/multipart.js"; const delay = async (ms: number) => new Promise((resolve) => { @@ -62,11 +63,13 @@ class PangeaRequest { response = await this.postMultipart(endpoint, data, options.files); } } else { + let responseType: ResponseType = + data.transfer_method == TransferMethod.MULTIPART ? "buffer" : "json"; const request = new Options({ headers: this.getHeaders(), json: data, retry: { limit: this.config.requestRetries }, - responseType: "json", + responseType: responseType, }); response = await this.httpPost(url, request); } @@ -74,6 +77,29 @@ class PangeaRequest { return this.handleHttpResponse(response, options); } + public async downloadFile(url: string): Promise { + const options = new Options({ + retry: { limit: this.config.requestRetries }, + responseType: "buffer", + }); + const response = (await got.get(url, options)) as Response; + + let contentDispositionHeader = response.headers["Content-Disposition"]; + let contentDisposition = ""; + if (Array.isArray(contentDispositionHeader)) { + contentDisposition = contentDispositionHeader[0] ?? contentDisposition; + } + + const contentTypeHeader = response.headers["Content-Type"] ?? ""; + let contentType = "application/octet-stream"; + if (Array.isArray(contentTypeHeader)) { + contentType = contentTypeHeader[0] ?? contentType; + } + + const filename = getHeaderField(contentDisposition, "filename", "defaultFilename"); + return new AttachedFile(filename, response.rawBody, contentType); + } + private async postMultipart( endpoint: string, data: Request, diff --git a/packages/pangea-node-sdk/src/response.ts b/packages/pangea-node-sdk/src/response.ts index dd6116d05..cc0b3e168 100644 --- a/packages/pangea-node-sdk/src/response.ts +++ b/packages/pangea-node-sdk/src/response.ts @@ -1,5 +1,7 @@ import type { Response } from "got"; import { AcceptedResult } from "./types.js"; +import { getBoundary, parse } from "./utils/multipart.js"; +import fs from "fs"; const SupportedJSONFields = ["message", "new", "old"]; @@ -21,13 +23,61 @@ export class ResponseObject { } } +export class AttachedFile { + filename: string; + file: Buffer; + contentType: string; + + constructor(filename: string, file: Buffer, contentType: string) { + this.filename = filename; + this.file = file; + this.contentType = contentType; + } + + save(destFolder?: string, filename?: string) { + if (!destFolder) { + destFolder = "."; + } + if (!filename) { + filename = this.filename ? this.filename : "defaultName.txt"; + } + if (!fs.existsSync(destFolder)) { + // If it doesn't exist, create it + fs.mkdirSync(destFolder, { recursive: true }); + } + + const filepath = destFolder + "/" + filename; + fs.writeFileSync(filepath, this.file); + } +} + export class PangeaResponse extends ResponseObject { - gotResponse: Response | undefined; + gotResponse?: Response; success: boolean; + attachedFiles: AttachedFile[] = []; constructor(response: Response) { - const obj = JSON.parse(JSON.stringify(response.body), parseJSONfields); - super(obj); + let jsonResp = {}; + let attachedFilesTemp: AttachedFile[] = []; + + if ( + response.headers["content-type"] && + response.headers["content-type"].includes("multipart") + ) { + const boundary = getBoundary(response.headers["content-type"]); + const parts = parse(response.rawBody, boundary); + parts.forEach((part, index) => { + if (index == 0) { + jsonResp = JSON.parse(part.data.toString("utf-8")); + } else { + attachedFilesTemp.push(new AttachedFile(part.filename, part.data, part.type)); + } + }); + } else { + jsonResp = JSON.parse(JSON.stringify(response.body), parseJSONfields); + } + super(jsonResp); + this.attachedFiles = attachedFilesTemp; this.gotResponse = response as Response; this.success = this.status === "Success"; this.result = this.result == null ? ({} as M) : this.result; diff --git a/packages/pangea-node-sdk/src/services/base.ts b/packages/pangea-node-sdk/src/services/base.ts index f78968f7b..a4401e88c 100644 --- a/packages/pangea-node-sdk/src/services/base.ts +++ b/packages/pangea-node-sdk/src/services/base.ts @@ -1,6 +1,6 @@ import PangeaConfig from "@src/config.js"; import PangeaRequest from "@src/request.js"; -import PangeaResponse from "@src/response.js"; +import PangeaResponse, { AttachedFile } from "@src/response.js"; import { PostOptions } from "@src/types.js"; class BaseService { @@ -41,6 +41,10 @@ class BaseService { return await this.request.post(endpoint, data, options); } + async downloadFile(url: string): Promise { + return await this.request.downloadFile(url); + } + async pollResult(request_id: string): Promise> { return await this.request.pollResult(request_id, true); } diff --git a/packages/pangea-node-sdk/src/services/index.ts b/packages/pangea-node-sdk/src/services/index.ts index 8c970ecee..546dec873 100644 --- a/packages/pangea-node-sdk/src/services/index.ts +++ b/packages/pangea-node-sdk/src/services/index.ts @@ -12,6 +12,7 @@ import { } from "./intel.js"; import VaultService from "./vault.js"; import { FileScanService } from "./file_scan.js"; +import StoreService from "./store.js"; export default { AuditService, @@ -26,4 +27,5 @@ export default { UserIntelService, VaultService, FileScanService, + StoreService, }; diff --git a/packages/pangea-node-sdk/src/services/store.ts b/packages/pangea-node-sdk/src/services/store.ts new file mode 100644 index 000000000..a98bfb974 --- /dev/null +++ b/packages/pangea-node-sdk/src/services/store.ts @@ -0,0 +1,130 @@ +import PangeaResponse from "@src/response.js"; +import BaseService from "./base.js"; +import PangeaConfig from "@src/config.js"; +import { Store, FileData, TransferMethod, FileUploadParams } from "@src/types.js"; +import PangeaRequest from "@src/request.js"; +import { PangeaErrors } from "@src/errors.js"; +import { getFileUploadParams } from "@src/index.js"; + +/** + * StoreService class provides methods for interacting with the Store Service + * @extends BaseService + */ +class StoreService extends BaseService { + constructor(token: string, config: PangeaConfig) { + super("store", token, config); + } + + delete(request: Store.DeleteRequest): Promise> { + return this.post("v1beta/delete", request); + } + + folderCreate( + request: Store.FolderCreateRequest + ): Promise> { + return this.post("v1beta/folder/create", request); + } + + getItem(request: Store.GetRequest): Promise> { + return this.post("v1beta/get", request); + } + + getArchive(request: Store.GetArchiveRequest): Promise> { + return this.post("v1beta/get_archive", request); + } + + list(request: Store.ListRequest): Promise> { + return this.post("v1beta/list", request); + } + + put(request: Store.PutRequest, fileData: FileData): Promise> { + let fsData = {} as FileUploadParams; + + if (!request.transfer_method || request.transfer_method === TransferMethod.POST_URL) { + fsData = getFileUploadParams(fileData.file); + request.crc32c = fsData.crc32c; + request.sha256 = fsData.sha256; + request.size = fsData.size; + } + + return this.post("v1beta/put", request, { + files: { + file: fileData, + }, + }); + } + + requestUploadURL(request: Store.PutRequest): Promise> { + if ( + request.transfer_method === TransferMethod.POST_URL && + (!request.size || !request.crc32c || !request.sha256) + ) { + throw new PangeaErrors.PangeaError( + `When transfer_method is ${request.transfer_method}, crc32c, sha256 and size must be set. Set them or use transfer_method ${TransferMethod.PUT_URL}` + ); + } + + return this.request.requestPresignedURL("v1beta/put", request); + } + + update(request: Store.UpdateRequest): Promise> { + return this.post("v1beta/update", request); + } + + shareLinkCreate( + request: Store.ShareLinkCreateRequest + ): Promise> { + return this.post("v1beta/share/link/create", request); + } + + shareLinkGet( + request: Store.ShareLinkGetRequest + ): Promise> { + return this.post("v1beta/share/link/get", request); + } + + shareLinkList( + request: Store.ShareLinkListRequest = {} + ): Promise> { + return this.post("v1beta/share/link/list", request); + } + + shareLinkDelete( + request: Store.ShareLinkDeleteRequest + ): Promise> { + return this.post("v1beta/share/link/delete", request); + } +} + +export class StoreUploader { + protected serviceName: string = "FileScanFileUploader"; + protected request_: PangeaRequest | undefined = undefined; + + constructor() {} + + private get request(): PangeaRequest { + if (this.request_) { + return this.request_; + } + + this.request_ = new PangeaRequest(this.serviceName, "notatoken", new PangeaConfig()); + return this.request_; + } + + // TODO: Docs + public async uploadFile( + url: string, + fileData: FileData, + options: { + transfer_method?: TransferMethod; + } + ) { + if (!options.transfer_method || options.transfer_method === TransferMethod.PUT_URL) { + await this.request.putPresignedURL(url, fileData); + } else if (options.transfer_method === TransferMethod.POST_URL) { + await this.request.postPresignedURL(url, fileData); + } + } +} + +export default StoreService; diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index 351aef29d..0224342a2 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -31,6 +31,7 @@ export enum TransferMethod { POST_URL = "post-url", PUT_URL = "put-url", SOURCE_URL = "source-url", + DEST_URL = "dest-url", } export interface Dictionary { @@ -43,6 +44,12 @@ export interface FileData { file_details?: Dictionary; } +export interface FileUploadParams { + size: number; + crc32c: string; + sha256: string; +} + export interface FileItems { [key: string]: FileData; } @@ -1839,3 +1846,430 @@ export namespace AuthN { export interface UpdateResult extends UserItem {} } } + +export namespace Store { + export enum FileFormat { + F3G2 = "3G2", + F3GP = "3GP", + F3MF = "3MF", + F7Z = "7Z", + A = "A", + AAC = "AAC", + ACCDB = "ACCDB", + AIFF = "AIFF", + AMF = "AMF", + AMR = "AMR", + APE = "APE", + ASF = "ASF", + ATOM = "ATOM", + AU = "AU", + AVI = "AVI", + AVIF = "AVIF", + BIN = "BIN", + BMP = "BMP", + BPG = "BPG", + BZ2 = "BZ2", + CAB = "CAB", + CLASS = "CLASS", + CPIO = "CPIO", + CRX = "CRX", + CSV = "CSV", + DAE = "DAE", + DBF = "DBF", + DCM = "DCM", + DEB = "DEB", + DJVU = "DJVU", + DLL = "DLL", + DOC = "DOC", + DOCX = "DOCX", + DWG = "DWG", + EOT = "EOT", + EPUB = "EPUB", + EXE = "EXE", + FDF = "FDF", + FITS = "FITS", + FLAC = "FLAC", + FLV = "FLV", + GBR = "GBR", + GEOJSON = "GEOJSON", + GIF = "GIF", + GLB = "GLB", + GML = "GML", + GPX = "GPX", + GZ = "GZ", + HAR = "HAR", + HDR = "HDR", + HEIC = "HEIC", + HEIF = "HEIF", + HTML = "HTML", + ICNS = "ICNS", + ICO = "ICO", + ICS = "ICS", + ISO = "ISO", + JAR = "JAR", + JP2 = "JP2", + JPF = "JPF", + JPG = "JPG", + JPM = "JPM", + JS = "JS", + JSON = "JSON", + JXL = "JXL", + JXR = "JXR", + KML = "KML", + LIT = "LIT", + LNK = "LNK", + LUA = "LUA", + LZ = "LZ", + M3U = "M3U", + M4A = "M4A", + MACHO = "MACHO", + MDB = "MDB", + MIDI = "MIDI", + MKV = "MKV", + MOBI = "MOBI", + MOV = "MOV", + MP3 = "MP3", + MP4 = "MP4", + MPC = "MPC", + MPEG = "MPEG", + MQV = "MQV", + MRC = "MRC", + MSG = "MSG", + MSI = "MSI", + NDJSON = "NDJSON", + NES = "NES", + ODC = "ODC", + ODF = "ODF", + ODG = "ODG", + ODP = "ODP", + ODS = "ODS", + ODT = "ODT", + OGA = "OGA", + OGV = "OGV", + OTF = "OTF", + OTG = "OTG", + OTP = "OTP", + OTS = "OTS", + OTT = "OTT", + OWL = "OWL", + P7S = "P7S", + PAT = "PAT", + PDF = "PDF", + PHP = "PHP", + PL = "PL", + PNG = "PNG", + PPT = "PPT", + PPTX = "PPTX", + PS = "PS", + PSD = "PSD", + PUB = "PUB", + PY = "PY", + QCP = "QCP", + RAR = "RAR", + RMVB = "RMVB", + RPM = "RPM", + RSS = "RSS", + RTF = "RTF", + SHP = "SHP", + SHX = "SHX", + SO = "SO", + SQLITE = "SQLITE", + SRT = "SRT", + SVG = "SVG", + SWF = "SWF", + SXC = "SXC", + TAR = "TAR", + TCL = "TCL", + TCX = "TCX", + TIFF = "TIFF", + TORRENT = "TORRENT", + TSV = "TSV", + TTC = "TTC", + TTF = "TTF", + TXT = "TXT", + VCF = "VCF", + VOC = "VOC", + VTT = "VTT", + WARC = "WARC", + WASM = "WASM", + WAV = "WAV", + WEBM = "WEBM", + WEBP = "WEBP", + WOFF = "WOFF", + WOFF2 = "WOFF2", + X3D = "X3D", + XAR = "XAR", + XCF = "XCF", + XFDF = "XFDF", + XLF = "XLF", + XLS = "XLS", + XLSX = "XLSX", + XML = "XML", + XPM = "XPM", + XZ = "XZ", + ZIP = "ZIP", + ZST = "ZST", + } + + export enum ArchiveFormat { + TAR = "tar", + ZIP = "zip", + } + + export enum LinkType { + UPLOAD = "upload", + DOWNLOAD = "download", + ALL = "all", + } + export enum AuthenticatorType { + EMAIL_OTP = "email_otp", + PASSWORD = "password", + SMS_OTP = "sms_otp", + SOCIAL = "social", + } + + export enum ItemOrder { + ASC = "asc", + DESC = "desc", + } + + export enum ItemOrderBy { + ID = "id", + CREATED_AT = "created_at", + NAME = "name", + PARENT_ID = "parent_id", + TYPE = "type", + UPDATED_AT = "updated_at", + } + + export interface Metadata { + [key: string]: string; + } + + export type Tags = string[]; + + export interface ItemData { + id: string; + type: string; + name: string; + created_at: string; + updated_at: string; + parent_id: string; + size?: number; + billable_size?: number; + location?: string; + tags?: Tags; + metadata?: Metadata; + md5?: string; + sha256?: string; + sha512?: string; + } + + export interface DeleteRequest { + id?: string; + force?: boolean; + path?: string; + } + + export interface Authenticator { + auth_type: AuthenticatorType; + auth_context: string; + } + + export interface DeleteResult { + count: number; + } + + export interface FolderCreateRequest { + name?: string; + metadata?: Metadata; + parent_id?: string; + path?: string; + tags?: Tags; + } + + export interface FolderCreateResult { + object: ItemData; + } + + export interface GetRequest { + id?: string; + path?: string; + transfer_method?: TransferMethod; + } + + export interface GetResult { + object: ItemData; + dest_url?: string; + } + + export interface PutRequest { + name?: string; + format?: FileFormat; + metadata?: Metadata; + mimetype?: string; + parent_id?: string; + path?: string; + crc32c?: string; + md5?: string; + sha1?: string; + sha256?: string; + sha512?: string; + size?: number; + tags?: Tags; + transfer_method?: TransferMethod; + } + + export interface PutResult { + object: ItemData; + } + + export interface UpdateRequest { + id: string; + path?: string; + add_metadata?: Metadata; + remove_metadata?: Metadata; + metadata?: Metadata; + add_tags?: Tags; + remove_tags?: Tags; + tags?: Tags; + parent_id?: string; + updated_at?: string; + } + + export interface UpdateResult { + object: ItemData; + } + + export interface ListRequest { + filter?: { [key: string]: string }; + last?: string; + order?: ItemOrder; + order_by?: ItemOrderBy; + size?: number; + } + + export interface ListResult { + count: number; + last?: string; + objects: ItemData[]; + } + + export interface GetArchiveRequest { + ids: string[]; + format?: ArchiveFormat; + transfer_method?: TransferMethod; + } + + export interface GetArchiveResult { + dest_url?: string; + count: number; + } + + export interface Authenticator { + auth_type: AuthenticatorType; + auth_context: string; + } + + export interface ShareLinkCreateItem { + targets: string[]; + link_type?: LinkType; + expires_at?: string; + max_access_count?: number; + authenticators: Authenticator[]; + } + + export interface ShareLinkCreateRequest { + links: ShareLinkCreateItem[]; + } + + export interface ShareLinkItem { + id: string; + storage_pool_id: string; + targets: string[]; + link_type: string; + access_count: number; + max_access_count: number; + created_at: string; + expires_at: string; + last_accessed_at?: string; + authenticators: Authenticator[]; + link: string; + } + + export interface ShareLinkCreateResult { + share_link_objects: ShareLinkItem[]; + } + + export interface ShareLinkGetRequest { + id: string; + } + + export interface ShareLinkGetResult { + share_link_object: ShareLinkItem; + } + + export interface ShareLinkListFilter { + id?: string; + id__contains?: string[]; + id__in?: string[]; + storage_pool_id?: string; + storage_pool_id__contains?: string[]; + storage_pool_id__in?: string[]; + target?: string; + target__contains?: string[]; + target__in?: string[]; + link_type?: string; + link_type__contains?: string[]; + link_type__in?: string[]; + access_count?: number; + access_count__gt?: number; + access_count__gte?: number; + access_count__lt?: number; + access_count__lte?: number; + max_access_count?: number; + max_access_count__gt?: number; + max_access_count__gte?: number; + max_access_count__lt?: number; + max_access_count__lte?: number; + created_at?: string; + created_at__gt?: string; + created_at__gte?: string; + created_at__lt?: string; + created_at__lte?: string; + expires_at?: string; + expires_at__gt?: string; + expires_at__gte?: string; + expires_at__lt?: string; + expires_at__lte?: string; + last_accessed_at?: string; + last_accessed_at__gt?: string; + last_accessed_at__gte?: string; + last_accessed_at__lt?: string; + last_accessed_at__lte?: string; + link?: string; + link__contains?: string[]; + link__in?: string[]; + } + + export interface ShareLinkListRequest { + filter?: ShareLinkListFilter; + last?: string; + order?: ItemOrder; + order_by?: ItemOrderBy; + size?: number; + } + + export interface ShareLinkListResult { + count: number; + share_link_objects: ShareLinkItem[]; + } + + export interface ShareLinkDeleteRequest { + ids: string[]; + } + + export interface ShareLinkDeleteResult { + share_link_objects: ShareLinkItem[]; + } +} diff --git a/packages/pangea-node-sdk/src/utils/multipart.ts b/packages/pangea-node-sdk/src/utils/multipart.ts new file mode 100644 index 000000000..3e9b4080e --- /dev/null +++ b/packages/pangea-node-sdk/src/utils/multipart.ts @@ -0,0 +1,162 @@ +type Part = { + contentDispositionHeader: string; + contentTypeHeader: string; + part: number[]; +}; + +type Input = { + filename: string; + name?: string; + type: string; + data: Buffer; +}; + +enum ParsingState { + INIT, + READING_HEADERS, + READING_DATA, + READING_PART_SEPARATOR, +} + +export function parse(multipartBodyBuffer: Buffer, boundary: string): Input[] { + let lastline = ""; + let contentDispositionHeader = ""; + let contentTypeHeader = ""; + let state: ParsingState = ParsingState.INIT; + let buffer: number[] = []; + const allParts: Input[] = []; + + let currentPartHeaders: string[] = []; + + for (let i = 0; i < multipartBodyBuffer.length; i++) { + const oneByte: number = multipartBodyBuffer[i] ?? 0; + const prevByte: number | null = i > 0 ? multipartBodyBuffer[i - 1] ?? 0 : null; + // 0x0a => \n + // 0x0d => \r + const newLineDetected: boolean = oneByte === 0x0a && prevByte === 0x0d; + const newLineChar: boolean = oneByte === 0x0a || oneByte === 0x0d; + + if (!newLineChar) lastline += String.fromCharCode(oneByte); + if (ParsingState.INIT === state && newLineDetected) { + // searching for boundary + if ("--" + boundary === lastline) { + state = ParsingState.READING_HEADERS; // found boundary. start reading headers + } + lastline = ""; + } else if (ParsingState.READING_HEADERS === state && newLineDetected) { + // parsing headers. Headers are separated by an empty line from the content. Stop reading headers when the line is empty + if (lastline.length) { + currentPartHeaders.push(lastline); + } else { + // found empty line. search for the headers we want and set the values + for (const h of currentPartHeaders) { + if (h.toLowerCase().startsWith("content-disposition:")) { + contentDispositionHeader = h; + } else if (h.toLowerCase().startsWith("content-type:")) { + contentTypeHeader = h; + } + } + state = ParsingState.READING_DATA; + buffer = []; + } + lastline = ""; + } else if (ParsingState.READING_DATA === state) { + // parsing data + if (lastline.length > boundary.length + 4) { + lastline = ""; // mem save + } + if ("--" + boundary === lastline) { + const j = buffer.length - lastline.length; + const part = buffer.slice(0, j - 1); + + allParts.push(process({ contentDispositionHeader, contentTypeHeader, part })); + buffer = []; + currentPartHeaders = []; + lastline = ""; + state = ParsingState.READING_PART_SEPARATOR; + contentDispositionHeader = ""; + contentTypeHeader = ""; + } else { + buffer.push(oneByte); + } + if (newLineDetected) { + lastline = ""; + } + } else if (ParsingState.READING_PART_SEPARATOR === state) { + if (newLineDetected) { + state = ParsingState.READING_HEADERS; + } + } + } + return allParts; +} + +// read the boundary from the content-type header sent by the http client +// this value may be similar to: +// 'multipart/form-data; boundary=----WebKitFormBoundaryvm5A9tzU1ONaGP5B', +export function getBoundary(header: string): string { + const items = header.split(";"); + if (items) { + for (let i = 0; i < items.length; i++) { + const item = new String(items[i]).trim(); + if (item.indexOf("boundary") >= 0) { + const k = item.split("="); + return new String(k[1]).trim().replace(/^["']|["']$/g, ""); + } + } + } + return ""; +} + +function process(part: Part): Input { + let input = {}; + + const filename = getHeaderField(part.contentDispositionHeader, "filename", "defaultFilename"); + if (filename) { + Object.defineProperty(input, "filename", { + value: filename, + writable: true, + enumerable: true, + configurable: true, + }); + } + + const parts = part.contentTypeHeader.split(":"); + const contentType = parts && parts[1] ? parts[1].trim() : ""; + if (contentType) { + Object.defineProperty(input, "type", { + value: contentType, + writable: true, + enumerable: true, + configurable: true, + }); + } + + const name = getHeaderField(part.contentDispositionHeader, "name", "defaultName"); + // always process the name field + Object.defineProperty(input, "name", { + value: name, + writable: true, + enumerable: true, + configurable: true, + }); + + Object.defineProperty(input, "data", { + value: Buffer.from(part.part), + writable: true, + enumerable: true, + configurable: true, + }); + return input as Input; +} + +export function getHeaderField(header: string, field: string, defaultValue: string): string { + const parts = header.split(field + "="); + if (parts.length > 1 && parts[1]) { + const valueParts = parts[1].split(";"); + if (valueParts[0]) { + return valueParts[0].trim().replace(/['"]+/g, ""); + } + } + return defaultValue; +} diff --git a/packages/pangea-node-sdk/tests/integration/store.test.ts b/packages/pangea-node-sdk/tests/integration/store.test.ts new file mode 100644 index 000000000..49f822775 --- /dev/null +++ b/packages/pangea-node-sdk/tests/integration/store.test.ts @@ -0,0 +1,367 @@ +import PangeaConfig from "../../src/config.js"; +import { it, expect, jest } from "@jest/globals"; +import { + TestEnvironment, + getFileUploadParams, + getTestDomain, + getTestToken, +} from "../../src/utils/utils.js"; +import { StoreService } from "../../src/index.js"; +import { Store, TransferMethod } from "../../src/types.js"; +import { StoreUploader } from "@src/services/store.js"; + +const testEnvironment = TestEnvironment.DEVELOP; +const token = getTestToken(testEnvironment); +const testHost = getTestDomain(testEnvironment); +const config = new PangeaConfig({ domain: testHost, customUserAgent: "sdk-test" }); +const client = new StoreService(token, config); + +const TIME = Math.round(Date.now() / 1000); +const FOLDER_DELETE = "/sdk_tests/node/delete/" + TIME; +const FOLDER_FILES = "/sdk_tests/node/files/" + TIME; +const METADATA = { field1: "value1", field2: "value2" }; +const ADD_METADATA = { field3: "value3" }; +const TAGS = ["tag1", "tag2"]; +const ADD_TAGS = ["tag3"]; + +const testfilePath = "./tests/testdata/testfile.pdf"; +jest.setTimeout(120000); + +const delay = async (ms: number) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +xit("Folder create/delete", async () => { + try { + const respCreate = await client.folderCreate({ + path: FOLDER_DELETE, + }); + expect(respCreate.success).toBeTruthy(); + expect(respCreate.result.object.id).toBeDefined(); + expect(respCreate.result.object.type).toBe("folder"); + expect(respCreate.result.object.name).toBeDefined(); + expect(respCreate.result.object.created_at).toBeDefined(); + expect(respCreate.result.object.updated_at).toBeDefined(); + + const id = respCreate.result.object.id; + const respDelete = await client.delete({ + id: id, + }); + expect(respDelete.success).toBeTruthy(); + expect(respDelete.result.count).toBe(1); + } catch (e) { + console.log(e); + expect(false).toBeTruthy(); + } +}); + +xit("Put file. Multipart transfer_method", async () => { + try { + const name = TIME + "_file_post_url"; + const respPut = await client.put( + { + name: name, + transfer_method: TransferMethod.MULTIPART, + }, + { + file: testfilePath, + name: name, + } + ); + expect(respPut.success).toBeTruthy(); + console.log(respPut.result); + } catch (e) { + console.log(e); + expect(false).toBeTruthy(); + } +}); + +xit("Put file. post-url transfer_method", async () => { + try { + const name = TIME + "_file_post_url"; + const respPut = await client.put( + { + name: name, + transfer_method: TransferMethod.POST_URL, + }, + { + file: testfilePath, + name: name, + } + ); + expect(respPut.success).toBeTruthy(); + } catch (e) { + console.log(e); + expect(false).toBeTruthy(); + } +}); + +xit("get url and put upload", async () => { + let response; + const name = TIME + "_file_split_put_url"; + try { + const request: Store.PutRequest = { + transfer_method: TransferMethod.PUT_URL, + name: name, + }; + response = await client.requestUploadURL(request); + } catch (e) { + console.log(e); + expect(false).toBeTruthy(); + throw e; + } + + const url = response.accepted_result?.put_url || ""; + + const uploader = new StoreUploader(); + await uploader.uploadFile( + url, + { + file: testfilePath, + name: "file", + }, + { + transfer_method: TransferMethod.PUT_URL, + } + ); + + const maxRetry = 12; + for (let retry = 0; retry < maxRetry; retry++) { + try { + // Wait until result could be ready + await delay(10 * 1000); + const request_id = response.request_id || ""; + response = await client.pollResult(request_id); + expect(response.status).toBe("Success"); + break; + } catch { + expect(retry).toBeLessThan(maxRetry - 1); + } + } +}); + +xit("get url and post upload", async () => { + let response; + const name = TIME + "_file_split_post_url"; + try { + const params = getFileUploadParams(testfilePath); + + const request: Store.PutRequest = { + transfer_method: TransferMethod.POST_URL, + name: name, + crc32c: params.crc32c, + sha256: params.sha256, + size: params.size, + }; + + response = await client.requestUploadURL(request); + } catch (e) { + console.log(e); + expect(false).toBeTruthy(); + throw e; + } + + const url = response.accepted_result?.post_url || ""; + const file_details = response.accepted_result?.post_form_data; + + const uploader = new StoreUploader(); + await uploader.uploadFile( + url, + { + file: testfilePath, + name: name, + file_details: file_details, + }, + { + transfer_method: TransferMethod.POST_URL, + } + ); + + const maxRetry = 12; + for (let retry = 0; retry < maxRetry; retry++) { + try { + // Wait until result could be ready + await delay(10 * 1000); + const request_id = response.request_id || ""; + response = await client.pollResult(request_id); + expect(response.status).toBe("Success"); + break; + } catch { + expect(retry).toBeLessThan(maxRetry - 1); + } + } +}); + +it("get url and post upload", async () => { + // Create a folder + const respCreate = await client.folderCreate({ + path: FOLDER_FILES, + }); + expect(respCreate.success).toBeTruthy(); + const folderID = respCreate.result.object.id; + + // # Upload a file with path as unique param + const path1 = FOLDER_FILES + "/" + TIME + "_file_multipart_1"; + const respPutPath = await client.put( + { + path: path1, + transfer_method: TransferMethod.MULTIPART, + }, + { + file: testfilePath, + name: path1, + } + ); + + expect(respPutPath.success).toBeTruthy(); + expect(respPutPath.result.object.parent_id).toBe(folderID); + expect(respPutPath.result.object.metadata).toBeUndefined(); + expect(respPutPath.result.object.tags).toBeUndefined(); + expect(respPutPath.result.object.md5).toBeUndefined(); + expect(respPutPath.result.object.sha512).toBeUndefined(); + expect(respPutPath.result.object.sha256).toBeDefined(); + + // Upload a file with parent id and name + const name2 = TIME + "_file_multipart_2"; + const respPutId = await client.put( + { + parent_id: folderID, + name: name2, + transfer_method: TransferMethod.MULTIPART, + metadata: METADATA, + tags: TAGS, + }, + { + file: testfilePath, + name: path1, + } + ); + + expect(respPutId.success).toBeTruthy(); + expect(respPutId.result.object.parent_id).toBe(folderID); + expect(respPutId.result.object.metadata).toStrictEqual(METADATA); + expect(respPutId.result.object.tags).toStrictEqual(TAGS); + expect(respPutId.result.object.sha256).toBeDefined(); + expect(respPutId.result.object.md5).toBeUndefined(); + expect(respPutId.result.object.sha512).toBeUndefined(); + + // Update file. full metadata and tags + const respUpdate = await client.update({ + id: respPutPath.result.object.id, + metadata: METADATA, + tags: TAGS, + }); + expect(respUpdate.success).toBeTruthy(); + expect(respUpdate.result.object.metadata).toStrictEqual(METADATA); + expect(respUpdate.result.object.tags).toStrictEqual(TAGS); + + // Update file. add metadata and tags + const respUpdateAdd = await client.update({ + id: respPutPath.result.object.id, + add_metadata: ADD_METADATA, + add_tags: ADD_TAGS, + }); + + const metadataFinal = { + ...METADATA, + ...ADD_METADATA, + }; + const tagsFinal = [...TAGS, ...ADD_TAGS]; + expect(respUpdateAdd.success).toBeTruthy(); + expect(respUpdateAdd.result.object.metadata).toStrictEqual(metadataFinal); + expect(respUpdateAdd.result.object.tags).toStrictEqual(tagsFinal); + + // Get archive + const respGetArchive1 = await client.getArchive({ + ids: [folderID], + format: Store.ArchiveFormat.ZIP, + transfer_method: TransferMethod.MULTIPART, + }); + expect(respGetArchive1.success).toBeTruthy(); + expect(respGetArchive1.result.dest_url).toBeUndefined(); + expect(respGetArchive1.attachedFiles.length).toBe(1); + respGetArchive1.attachedFiles.forEach((file) => { + file.save("./"); + }); + + const respGetArchive2 = await client.getArchive({ + ids: [folderID], + format: Store.ArchiveFormat.TAR, + transfer_method: TransferMethod.DEST_URL, + }); + expect(respGetArchive2.success).toBeTruthy(); + expect(respGetArchive2.result.dest_url).toBeDefined(); + expect(respGetArchive2.attachedFiles.length).toBe(0); + + // Download file + const url = respGetArchive2.result.dest_url ?? ""; + let downloadedFile = await client.downloadFile(url); + expect(downloadedFile.file.length).toBeGreaterThan(0); + + // Create share link + const authenticators: Store.Authenticator[] = [ + { auth_type: Store.AuthenticatorType.PASSWORD, auth_context: "somepassword" }, + ]; + const linkList: Store.ShareLinkCreateItem[] = [ + { + targets: [folderID], + link_type: Store.LinkType.ALL, + max_access_count: 3, + authenticators: authenticators, + }, + ]; + + const respCreateLink = await client.shareLinkCreate({ + links: linkList, + }); + expect(respCreateLink.success).toBeTruthy(); + const links = respCreateLink.result.share_link_objects; + expect(links.length).toBe(1); + + const link = links[0]; + expect(link).toBeDefined(); + expect(link?.access_count).toBe(0); + expect(link?.max_access_count).toBe(3); + expect(link?.authenticators.length).toBe(1); + expect(link?.authenticators[0]?.auth_type).toBe(Store.AuthenticatorType.PASSWORD); + expect(link?.link).toBeDefined(); + expect(link?.id).toBeDefined(); + expect(link?.targets.length).toBe(1); + + // Get share link + const linkID = link?.id ?? ""; + const respGetLink = await client.shareLinkGet({ + id: linkID, + }); + expect(respGetLink.success).toBeTruthy(); + expect(respGetLink.result.share_link_object.link).toBe(link?.link); + expect(respGetLink.result.share_link_object.access_count).toBe(0); + expect(respGetLink.result.share_link_object.max_access_count).toBe(3); + expect(respGetLink.result.share_link_object.created_at).toBe(link?.created_at); + expect(respGetLink.result.share_link_object.expires_at).toBe(link?.expires_at); + + // List share link + const respListLink = await client.shareLinkList(); + expect(respListLink.success).toBeTruthy(); + expect(respListLink.result.count).toBeGreaterThan(0); + expect(respListLink.result.share_link_objects.length).toBeGreaterThan(0); + + // Delete share link + const respDeleteLink = await client.shareLinkDelete({ + ids: [linkID], + }); + expect(respDeleteLink.success).toBeTruthy(); + expect(respDeleteLink.result.share_link_objects.length).toBe(1); + + // List files in folder + const listFilter = { + folder: FOLDER_FILES, + }; + const respList = await client.list({ + filter: listFilter, + }); + expect(respList.success).toBeTruthy(); + expect(respList.result.count).toBe(2); + expect(respList.result.objects.length).toBe(2); +}); From 62b77240691e521c1d20bab1ff969f1f1c6e114f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20Andr=C3=A9s=20Tournour?= <111303288+pangea-andrest@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:43:55 -0300 Subject: [PATCH 02/36] Update packages/pangea-node-sdk/src/response.ts Use path.resolve() Co-authored-by: Kenan Yildirim --- packages/pangea-node-sdk/src/response.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pangea-node-sdk/src/response.ts b/packages/pangea-node-sdk/src/response.ts index cc0b3e168..427d4b701 100644 --- a/packages/pangea-node-sdk/src/response.ts +++ b/packages/pangea-node-sdk/src/response.ts @@ -46,7 +46,7 @@ export class AttachedFile { fs.mkdirSync(destFolder, { recursive: true }); } - const filepath = destFolder + "/" + filename; + const filepath = path.resolve(destFolder, filename); fs.writeFileSync(filepath, this.file); } } From 145ce203ade492bcbd88fa5461afa95c689f3a50 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 9 Jan 2024 15:46:16 -0300 Subject: [PATCH 03/36] import path package --- packages/pangea-node-sdk/src/response.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/pangea-node-sdk/src/response.ts b/packages/pangea-node-sdk/src/response.ts index 427d4b701..8a6a63f0c 100644 --- a/packages/pangea-node-sdk/src/response.ts +++ b/packages/pangea-node-sdk/src/response.ts @@ -1,6 +1,7 @@ import type { Response } from "got"; import { AcceptedResult } from "./types.js"; import { getBoundary, parse } from "./utils/multipart.js"; +import * as path from "path"; import fs from "fs"; const SupportedJSONFields = ["message", "new", "old"]; From 39916a2bf8d898f209171aef5ed70f7548ad8ea3 Mon Sep 17 00:00:00 2001 From: David Wayman Date: Tue, 9 Jan 2024 16:13:27 -0800 Subject: [PATCH 04/36] Add doc strings for Node SDK --- .../pangea-node-sdk/src/services/store.ts | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/packages/pangea-node-sdk/src/services/store.ts b/packages/pangea-node-sdk/src/services/store.ts index a98bfb974..f8dc48ac2 100644 --- a/packages/pangea-node-sdk/src/services/store.ts +++ b/packages/pangea-node-sdk/src/services/store.ts @@ -15,28 +15,129 @@ class StoreService extends BaseService { super("store", token, config); } + /** + * @summary Delete + * @description Delete object by ID or path. If both are supplied, the path must match that of the object represented by the ID. + * @operationId store_post_v1beta_delete + * @param {Store.DeleteRequest} request + * @returns {Promise} - A promise representing an async call to the delete endpoint. + * @example + * ```js + * const request = { id: "pos_3djfmzg2db4c6donarecbyv5begtj2bm" }; + * const response = await client.delete(request); + * ``` + */ delete(request: Store.DeleteRequest): Promise> { return this.post("v1beta/delete", request); } + /** + * @summary Create a folder + * @description Create a folder, either by name or path and parent_id. + * @operationId store_post_v1beta_folder_create + * @param {Store.FolderCreateRequest} request + * @returns {Promise} - A promise representing an async call to the folder create endpoint. + * @example + * ```js + * const request = { + * metadata: { + * created_by: "jim", + * priority: "medium", + * }, + * parent_id: "pos_3djfmzg2db4c6donarecbyv5begtj2bm", + * path: "/", + * tags: ["irs_2023", "personal"], + * }; + * + * const response = await client.folderCreate(request); + * ``` + */ folderCreate( request: Store.FolderCreateRequest ): Promise> { return this.post("v1beta/folder/create", request); } + /** + * @summary Get an object + * @description Get object. If both ID and Path are supplied, the call will fail if the target object doesn't match both properties. + * @operationId store_post_v1beta_get + * @param {Store.GetRequest} request + * @returns {Promise} - A promise representing an async call to the get item endpoint. + * @example + * ```js + * const request = { + * id: "pos_3djfmzg2db4c6donarecbyv5begtj2bm", + * path: "/", + * }; + * + * const response = await client.getItem(request); + * ``` + */ getItem(request: Store.GetRequest): Promise> { return this.post("v1beta/get", request); } + /** + * @summary Get archive + * @description Get an archive file of multiple objects. + * @operationId store_post_v1beta_get_archive + * @param {Store.GetArchiveRequest} request + * @returns {Promise} - A promise representing an async call to the get archive endpoint. + * @example + * ```js + * const request = { ids: ["pos_3djfmzg2db4c6donarecbyv5begtj2bm"] }; + * const response = await client.getArchive(request); + * ``` + */ getArchive(request: Store.GetArchiveRequest): Promise> { return this.post("v1beta/get_archive", request); } + /** + * @summary List + * @description List or filter/search records. + * @operationId store_post_v1beta_list + * @param {Store.ListRequest} request + * @returns {Promise} - A promise representing an async call to the list endpoint. + * @example + * ```js + * const request = {}; + * const response = await client.list(request); + * ``` + */ list(request: Store.ListRequest): Promise> { return this.post("v1beta/list", request); } + /** + * @summary Upload a file [beta] + * @description Upload a file. + * @operationId store_post_v1beta_put + * @param {Store.PutRequest} request + * @param {FileData} fileData + * @returns {Promise} - A promise representing an async call to the put endpoint. + * @example + * ```js + * const request = { + * transfer_method: TransferMethod.MULTIPART, + * Metadata: { + * created_by: "jim", + * priority: "medium", + * }, + * parent_id: "pos_3djfmzg2db4c6donarecbyv5begtj2bm", + * path: "/", + * tags: ["irs_2023", "personal"], + * }; + * const file = fs.readFileSync("./path/to/file.pdf"); + * const fileData = { + * file, + * name: "file", + * }; + * + * const response = await client.put(request, fileData); + * ``` + */ put(request: Store.PutRequest, fileData: FileData): Promise> { let fsData = {} as FileUploadParams; @@ -54,6 +155,34 @@ class StoreService extends BaseService { }); } + /** + * @summary Request upload URL + * @description Request an upload URL. + * @operationId store_post_v1beta_put 2 + * @param {Store.PutRequest} request + * @param {FileData} fileData + * @returns {Promise} - A promise representing an async call to the put endpoint. + * @example + * ```js + * const { crc32c, sha256, size } = getFileUploadParams("./path/to/file.pdf"); + * + * const request = { + * transfer_method: TransferMethod.POST_URL, + * crc32c, + * sha256, + * size, + * Metadata: { + * created_by: "jim", + * priority: "medium", + * }, + * parent_id: "pos_3djfmzg2db4c6donarecbyv5begtj2bm", + * path: "/", + * tags: ["irs_2023", "personal"], + * }; + * + * const response = await client.requestUploadURL(request); + * ``` + */ requestUploadURL(request: Store.PutRequest): Promise> { if ( request.transfer_method === TransferMethod.POST_URL && @@ -67,28 +196,112 @@ class StoreService extends BaseService { return this.request.requestPresignedURL("v1beta/put", request); } + /** + * @summary Update a file + * @description Update a file. + * @operationId store_post_v1beta_update + * @param {Store.UpdateRequest} request + * @param {FileData} fileData + * @returns {Promise} - A promise representing an async call to the update endpoint. + * @example + * ```js + * const request = { + * id: "pos_3djfmzg2db4c6donarecbyv5begtj2bm", + * path: "/", + * remove_metadata: { + * created_by: "jim", + * priority: "medium", + * } + * remove_tags: ["irs_2023", "personal"], + * }; + * const file = fs.readFileSync("./path/to/file.pdf"); + * const fileData = { + * file, + * name: "file", + * }; + * + * const response = await client.update(request, fileData); + * ``` + */ update(request: Store.UpdateRequest): Promise> { return this.post("v1beta/update", request); } + /** + * @summary Create share links + * @description Create a share link. + * @operationId store_post_v1beta_share_link_create + * @param {Store.ShareLinkCreateRequest} request + * @returns {Promise} - A promise representing an async call to the share link create endpoint. + * @example + * ```js + * const authenticator = { + * auth_type: Store.AuthenticatorType.PASSWORD, + * auth_context: "my_fav_Pa55word", + * }; + * const link = { + * targets: ["pos_3djfmzg2db4c6donarecbyv5begtj2bm"], + * link_type: Store.LinkType.DOWNLOAD, + * authenticators: [authenticator], + * }; + * const request = { links: [link] }; + * const response = await client.shareLinkCreate(request); + * ``` + */ shareLinkCreate( request: Store.ShareLinkCreateRequest ): Promise> { return this.post("v1beta/share/link/create", request); } + /** + * @summary Get share link + * @description Get a share link. + * @operationId store_post_v1beta_share_link_get + * @param {Store.ShareLinkGetRequest} request + * @returns {Promise} - A promise representing an async call to the share link get endpoint. + * @example + * ```js + * const request = { id: "psl_3djfmzg2db4c6donarecbyv5begtj2bm" }; + * const response = await client.shareLinkGet(request); + * ``` + */ shareLinkGet( request: Store.ShareLinkGetRequest ): Promise> { return this.post("v1beta/share/link/get", request); } + /** + * @summary List share links + * @description Look up share links by filter options. + * @operationId store_post_v1beta_share_link_list + * @param {Store.ShareLinkListRequest} request + * @returns {Promise} - A promise representing an async call to the share link list endpoint. + * @example + * ```js + * const request = {}; + * const response = await client.shareLinkList(request); + * ``` + */ shareLinkList( request: Store.ShareLinkListRequest = {} ): Promise> { return this.post("v1beta/share/link/list", request); } + /** + * @summary Delete share links + * @description Delete share links. + * @operationId store_post_v1beta_share_link_delete + * @param {Store.ShareLinkDeleteRequest} request + * @returns {Promise} - A promise representing an async call to the delete share links endpoint. + * @example + * ```js + * const request = { ids: ["psl_3djfmzg2db4c6donarecbyv5begtj2bm"] }; + * const response = await client.shareLinkDelete(request); + * ``` + */ shareLinkDelete( request: Store.ShareLinkDeleteRequest ): Promise> { From efd30df9f4e7689e086112502cb218658282b0e1 Mon Sep 17 00:00:00 2001 From: David Wayman Date: Wed, 10 Jan 2024 11:11:28 -0800 Subject: [PATCH 05/36] Update doc string --- packages/pangea-node-sdk/src/services/store.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/pangea-node-sdk/src/services/store.ts b/packages/pangea-node-sdk/src/services/store.ts index f8dc48ac2..ea1ed3606 100644 --- a/packages/pangea-node-sdk/src/services/store.ts +++ b/packages/pangea-node-sdk/src/services/store.ts @@ -201,7 +201,6 @@ class StoreService extends BaseService { * @description Update a file. * @operationId store_post_v1beta_update * @param {Store.UpdateRequest} request - * @param {FileData} fileData * @returns {Promise} - A promise representing an async call to the update endpoint. * @example * ```js @@ -214,13 +213,8 @@ class StoreService extends BaseService { * } * remove_tags: ["irs_2023", "personal"], * }; - * const file = fs.readFileSync("./path/to/file.pdf"); - * const fileData = { - * file, - * name: "file", - * }; * - * const response = await client.update(request, fileData); + * const response = await client.update(request); * ``` */ update(request: Store.UpdateRequest): Promise> { From 6ef66aa5dbceaba9bcacc3dca91425c63d585c2b Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 11 Jan 2024 16:06:11 -0300 Subject: [PATCH 06/36] add default value in list() --- packages/pangea-node-sdk/src/services/store.ts | 2 +- packages/pangea-node-sdk/src/types.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/pangea-node-sdk/src/services/store.ts b/packages/pangea-node-sdk/src/services/store.ts index ea1ed3606..bd1ce028a 100644 --- a/packages/pangea-node-sdk/src/services/store.ts +++ b/packages/pangea-node-sdk/src/services/store.ts @@ -106,7 +106,7 @@ class StoreService extends BaseService { * const response = await client.list(request); * ``` */ - list(request: Store.ListRequest): Promise> { + list(request: Store.ListRequest = {}): Promise> { return this.post("v1beta/list", request); } diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index dc645a1b5..596cbbfbe 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -2198,8 +2198,14 @@ export namespace Store { object: ItemData; } + export interface ListFilter { + folder?: string; + folder__contains?: string[]; + folder__in?: string[]; + } + export interface ListRequest { - filter?: { [key: string]: string }; + filter?: ListFilter; last?: string; order?: ItemOrder; order_by?: ItemOrderBy; From e9894e4325401a6414244ac67ad56d77c1d64a76 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 16 Jan 2024 10:28:35 -0300 Subject: [PATCH 07/36] Update LinkType --- packages/pangea-node-sdk/src/types.ts | 2 +- .../pangea-node-sdk/tests/integration/store.test.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index 596cbbfbe..95de8d30e 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -2076,7 +2076,7 @@ export namespace Store { export enum LinkType { UPLOAD = "upload", DOWNLOAD = "download", - ALL = "all", + EDITOR = "editor", } export enum AuthenticatorType { EMAIL_OTP = "email_otp", diff --git a/packages/pangea-node-sdk/tests/integration/store.test.ts b/packages/pangea-node-sdk/tests/integration/store.test.ts index 49f822775..44c8ddb15 100644 --- a/packages/pangea-node-sdk/tests/integration/store.test.ts +++ b/packages/pangea-node-sdk/tests/integration/store.test.ts @@ -32,7 +32,7 @@ const delay = async (ms: number) => setTimeout(resolve, ms); }); -xit("Folder create/delete", async () => { +it("Folder create/delete", async () => { try { const respCreate = await client.folderCreate({ path: FOLDER_DELETE, @@ -56,7 +56,7 @@ xit("Folder create/delete", async () => { } }); -xit("Put file. Multipart transfer_method", async () => { +it("Put file. Multipart transfer_method", async () => { try { const name = TIME + "_file_post_url"; const respPut = await client.put( @@ -77,7 +77,7 @@ xit("Put file. Multipart transfer_method", async () => { } }); -xit("Put file. post-url transfer_method", async () => { +it("Put file. post-url transfer_method", async () => { try { const name = TIME + "_file_post_url"; const respPut = await client.put( @@ -97,7 +97,7 @@ xit("Put file. post-url transfer_method", async () => { } }); -xit("get url and put upload", async () => { +it("get url and put upload", async () => { let response; const name = TIME + "_file_split_put_url"; try { @@ -141,7 +141,7 @@ xit("get url and put upload", async () => { } }); -xit("get url and post upload", async () => { +it("get url and post upload", async () => { let response; const name = TIME + "_file_split_post_url"; try { @@ -306,7 +306,7 @@ it("get url and post upload", async () => { const linkList: Store.ShareLinkCreateItem[] = [ { targets: [folderID], - link_type: Store.LinkType.ALL, + link_type: Store.LinkType.EDITOR, max_access_count: 3, authenticators: authenticators, }, From 74bdc95cda7084aeb73a3013047b0a8e26fbca1e Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 16 Jan 2024 11:56:52 -0300 Subject: [PATCH 08/36] examples --- examples/store/README.md | 12 + examples/store/folder_create_n_delete.mjs | 35 +++ examples/store/item_life_cycle.mjs | 215 ++++++++++++++++++ examples/store/package.json | 14 ++ examples/store/put_split_upload.mjs | 93 ++++++++ .../store/put_transfer_method_multipart.mjs | 42 ++++ .../store/put_transfer_method_post_url.mjs | 43 ++++ examples/store/testfile.pdf | Bin 0 -> 10028 bytes packages/pangea-node-sdk/src/index.ts | 1 + .../tests/integration/store.test.ts | 2 +- 10 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 examples/store/README.md create mode 100644 examples/store/folder_create_n_delete.mjs create mode 100644 examples/store/item_life_cycle.mjs create mode 100644 examples/store/package.json create mode 100644 examples/store/put_split_upload.mjs create mode 100644 examples/store/put_transfer_method_multipart.mjs create mode 100644 examples/store/put_transfer_method_post_url.mjs create mode 100644 examples/store/testfile.pdf diff --git a/examples/store/README.md b/examples/store/README.md new file mode 100644 index 000000000..7894c37f0 --- /dev/null +++ b/examples/store/README.md @@ -0,0 +1,12 @@ +# Pangea Store Service Example + +## Setup + +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_STORE_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Store service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. + +## Run example + +``` +yarn install +node folder_create_n_delete.mjs +``` diff --git a/examples/store/folder_create_n_delete.mjs b/examples/store/folder_create_n_delete.mjs new file mode 100644 index 000000000..5e8bdec70 --- /dev/null +++ b/examples/store/folder_create_n_delete.mjs @@ -0,0 +1,35 @@ +/* eslint-disable no-console */ + +import { PangeaConfig, StoreService } from "pangea-node-sdk"; + +// Load pangea token and domain from environment variables +const token = process.env.PANGEA_STORE_TOKEN; +const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); + +// Create Store client +const client = new StoreService(token, config); + +// Create unique folder path +const time = Math.round(Date.now() / 1000); +const folderPath = "/sdk_examples/node/delete/" + time; + +(async () => { + try { + console.log("Creating folder..."); + const respCreate = await client.folderCreate({ + path: folderPath, + }); + + const id = respCreate.result.object.id; + console.log(`Folder create success. Folder ID: ${id}`); + + console.log("Deleting folder..."); + const respDelete = await client.delete({ + id: id, + }); + + console.log(`Deleted ${respDelete.result.count} item(s)`); + } catch (e) { + console.log(e); + } +})(); diff --git a/examples/store/item_life_cycle.mjs b/examples/store/item_life_cycle.mjs new file mode 100644 index 000000000..77ed1acd7 --- /dev/null +++ b/examples/store/item_life_cycle.mjs @@ -0,0 +1,215 @@ +/* eslint-disable no-console */ + +import { + PangeaConfig, + StoreService, + TransferMethod, + Store, +} from "pangea-node-sdk"; +import * as fs from "fs"; + +// Load pangea token and domain from environment variables +const token = process.env.PANGEA_STORE_TOKEN; +const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); + +// Create Store client +const client = new StoreService(token, config); + +// Create unique folder path +const time = Math.round(Date.now() / 1000); +const folderPath = "/sdk_examples/node/delete/" + time; +const filepath = "./testfile.pdf"; + +(async () => { + try { + console.log("Creating folder..."); + // Create a folder + const respCreate = await client.folderCreate({ + path: folderPath, + }); + const folderID = respCreate.result.object.id; + console.log(`Create folder success. Folder ID: ${folderID}`); + + // # Upload a file with path as unique param + // Read file content as buffer + const data = fs.readFileSync(filepath); + + console.log("\nUploading file with path as unique field..."); + const path1 = folderPath + "/" + time + "_file_multipart_1"; + const respPutPath = await client.put( + { + path: path1, + transfer_method: TransferMethod.MULTIPART, + }, + { + file: data, + name: path1, + } + ); + + console.log(`Upload success. Item ID: ${respPutPath.result.object.id}`); + console.log(`\tParent ID: ${respPutPath.result.object.parent_id}`); + console.log(`\tMetadata: ${respPutPath.result.object.metadata}`); + console.log(`\tTags: ${respPutPath.result.object.tags}`); + + console.log("\nUploading file with parent_id and name..."); + // Upload a file with parent id and name + const name2 = time + "_file_multipart_2"; + const metadata = { field1: "value1", field2: "value2" }; + const tags = ["tag1", "tag2"]; + const respPutId = await client.put( + { + parent_id: folderID, + name: name2, + transfer_method: TransferMethod.MULTIPART, + metadata: metadata, + tags: tags, + }, + { + file: data, + name: path1, + } + ); + + console.log(`Upload success. Item ID: ${respPutId.result.object.id}`); + console.log(`\tParent ID: ${respPutId.result.object.parent_id}`); + console.log(`\tMetadata: ${respPutId.result.object.metadata}`); + console.log(`\tTags: ${respPutId.result.object.tags}`); + + console.log("\nUpdating file with full metadata and tags..."); + // Update file. full metadata and tags + const respUpdate = await client.update({ + id: respPutPath.result.object.id, + metadata: metadata, + tags: tags, + }); + + console.log(`Upload success. Item ID: ${respUpdate.result.object.id}`); + console.log(`\tParent ID: ${respUpdate.result.object.parent_id}`); + console.log(`\tMetadata: ${respUpdate.result.object.metadata}`); + console.log(`\tTags: ${respUpdate.result.object.tags}`); + + console.log("\nUpdating file adding metadata and tags..."); + // Update file. add metadata and tags + const addMetadata = { field3: "value3" }; + const addTags = ["tag3"]; + + const respUpdateAdd = await client.update({ + id: respPutPath.result.object.id, + add_metadata: addMetadata, + add_tags: addTags, + }); + + console.log(`Upload success. Item ID: ${respUpdateAdd.result.object.id}`); + console.log(`\tParent ID: ${respUpdateAdd.result.object.parent_id}`); + console.log(`\tMetadata: ${respUpdateAdd.result.object.metadata}`); + console.log(`\tTags: ${respUpdateAdd.result.object.tags}`); + + console.log("\nGetting archive with multipart transfer method..."); + // Get archive + const respGetArchive1 = await client.getArchive({ + ids: [folderID], + format: Store.ArchiveFormat.ZIP, + transfer_method: TransferMethod.MULTIPART, + }); + + // Using multipart as transfer method it should return just 1 file and no dest url + console.log( + `Got ${respGetArchive1.attachedFiles.length} attached file(s).` + ); + console.log(`Got URL: ${respGetArchive1.result.dest_url}`); + + // Saving attached files + respGetArchive1.attachedFiles.forEach((file) => { + file.save("./"); + }); + + console.log("\nGetting archive with dest-url transfer method..."); + const respGetArchive2 = await client.getArchive({ + ids: [folderID], + format: Store.ArchiveFormat.TAR, + transfer_method: TransferMethod.DEST_URL, + }); + + // Using dest-url as transfer method it should return no attahched files but dest url + console.log( + `Got ${respGetArchive2.attachedFiles.length} attached file(s).` + ); + console.log(`Got URL: ${respGetArchive2.result.dest_url}`); + + console.log("\nDownloading file..."); + // Download file + const url = respGetArchive2.result.dest_url ?? ""; + let downloadedFile = await client.downloadFile(url); + + console.log("\nCreating share link..."); + // Create share link + // Create authenticators list to access share link + const authenticators = [ + { + auth_type: Store.AuthenticatorType.PASSWORD, + auth_context: "somepassword", + }, + ]; + + // Create a link's list, each link should have targets (folder or objects), link_type, etc. + const linkList = [ + { + targets: [folderID], + link_type: Store.LinkType.EDITOR, + max_access_count: 3, + authenticators: authenticators, + }, + ]; + + // Send share link create request + const respCreateLink = await client.shareLinkCreate({ + links: linkList, + }); + + const links = respCreateLink.result.share_link_objects; + console.log(`Created ${links.length} share links`); + + const link = links[0]; + console.log(`Link ID: ${link.id}. Link: ${link.link}`); + + console.log("\nGetting already created link..."); + // Get share link + const linkID = link?.id ?? ""; + const respGetLink = await client.shareLinkGet({ + id: linkID, + }); + console.log( + `Got link ID: ${respGetLink.result.share_link_object.id}. Link: ${respGetLink.result.share_link_object.link}` + ); + + console.log("\nListing share links..."); + // List share link + const respListLink = await client.shareLinkList(); + console.log(`Got ${respListLink.result.count} link(s)`); + + console.log("\nDeleting link..."); + // Delete share link + const respDeleteLink = await client.shareLinkDelete({ + ids: [linkID], + }); + console.log( + `Deleted ${respDeleteLink.result.share_link_objects.length} link(s)` + ); + + console.log("\nListing files..."); + // List files in folder + // Create a ListFilter and set its possible values + const listFilter = { + folder: folderPath, + }; + + const respList = await client.list({ + filter: listFilter, + }); + + console.log(`Got ${respList.result.count} item(s)`); + } catch (e) { + console.log(e.toString()); + } +})(); diff --git a/examples/store/package.json b/examples/store/package.json new file mode 100644 index 000000000..eac206eb1 --- /dev/null +++ b/examples/store/package.json @@ -0,0 +1,14 @@ +{ + "name": "redact_examples", + "version": "2.0.0", + "description": "Pangea Redact text example", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Pangea", + "license": "ISC", + "dependencies": { + "pangea-node-sdk": "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz" + } +} diff --git a/examples/store/put_split_upload.mjs b/examples/store/put_split_upload.mjs new file mode 100644 index 000000000..df4e26f4b --- /dev/null +++ b/examples/store/put_split_upload.mjs @@ -0,0 +1,93 @@ +/* eslint-disable no-console */ + +import { + PangeaConfig, + StoreService, + TransferMethod, + StoreUploader, +} from "pangea-node-sdk"; +import * as fs from "fs"; + +// Load pangea token and domain from environment variables +const token = process.env.PANGEA_STORE_TOKEN; +const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); + +// Create Store client +const client = new StoreService(token, config); + +// Create unique folder path +const time = Math.round(Date.now() / 1000); +const filepath = "./testfile.pdf"; + +// Auxiliary function +const delay = async (ms) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +(async () => { + try { + console.log("Request upload url with put transfer method..."); + let response; + // Create a file unique name + const name = time + "_file_split_put_url"; + try { + // Request upload url with put-url transfer method + response = await client.requestUploadURL({ + transfer_method: TransferMethod.PUT_URL, + name: name, + }); + } catch (e) { + console.log(e.toString()); + process.exit(1); + } + + const url = response.accepted_result?.put_url || ""; + console.log(`Got URL: ${url}`); + + // Create StoreUploader client + const uploader = new StoreUploader(); + + // Read file content as buffer + const data = fs.readFileSync(filepath); + + console.log("Uploading file..."); + // Upload the file to received url + await uploader.uploadFile( + url, + { + file: data, + name: "file", + }, + { + transfer_method: TransferMethod.PUT_URL, + } + ); + + const maxRetry = 12; + let retry; + for (retry = 0; retry < maxRetry; retry++) { + try { + console.log(`Polling result. Retry: ${retry}`); + // Wait until result could be ready + await delay(10 * 1000); + const request_id = response.request_id || ""; + response = await client.pollResult(request_id); + + console.log( + `Poll result success. Item ID: ${response.result.object.id}` + ); + break; + } catch { + console.log("Result is not ready yet."); + } + } + + if (retry >= maxRetry) { + console.log("Failed to poll result. Reached max retry."); + } + } catch (e) { + console.log(e.toString()); + process.exit(1); + } +})(); diff --git a/examples/store/put_transfer_method_multipart.mjs b/examples/store/put_transfer_method_multipart.mjs new file mode 100644 index 000000000..1322583da --- /dev/null +++ b/examples/store/put_transfer_method_multipart.mjs @@ -0,0 +1,42 @@ +/* eslint-disable no-console */ + +import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; +import * as fs from "fs"; + +// Load pangea token and domain from environment variables +const token = process.env.PANGEA_STORE_TOKEN; +const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); + +// Create Store client +const client = new StoreService(token, config); + +// Create unique folder path +const time = Math.round(Date.now() / 1000); +const filepath = "./testfile.pdf"; + +(async () => { + try { + console.log("Uploading file with multipart transfer method..."); + // Create a unique name + const name = time + "_file_multipart"; + + // Read file content as buffer + const data = fs.readFileSync(filepath); + + // Send Put request setting transfer_method to multipart + const respPut = await client.put( + { + name: name, + transfer_method: TransferMethod.MULTIPART, + }, + { + file: data, + name: name, + } + ); + + console.log(`Upload success. Item ID: ${respPut.result.object.id}`); + } catch (e) { + console.log(e); + } +})(); diff --git a/examples/store/put_transfer_method_post_url.mjs b/examples/store/put_transfer_method_post_url.mjs new file mode 100644 index 000000000..80ac9c621 --- /dev/null +++ b/examples/store/put_transfer_method_post_url.mjs @@ -0,0 +1,43 @@ +/* eslint-disable no-console */ + +import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; +import * as fs from "fs"; + +// Load pangea token and domain from environment variables +const token = process.env.PANGEA_STORE_TOKEN; +const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); + +// Create Store client +const client = new StoreService(token, config); + +// Create unique folder path +const time = Math.round(Date.now() / 1000); +const filepath = "./testfile.pdf"; + +(async () => { + try { + console.log("Uploading file with post-url transfer method..."); + // Create a unique name + const name = time + "_file_post_url"; + + // Read file content as buffer + const data = fs.readFileSync(filepath); + + // Send Put request setting transfer_method to post-url + // SDK will request an upload url, post the file to that url and then poll the upload result to Store service + const respPut = await client.put( + { + name: name, + transfer_method: TransferMethod.POST_URL, + }, + { + file: data, + name: name, + } + ); + + console.log(`Upload success. Item ID: ${respPut.result.object.id}`); + } catch (e) { + console.log(e.toString()); + } +})(); diff --git a/examples/store/testfile.pdf b/examples/store/testfile.pdf new file mode 100644 index 0000000000000000000000000000000000000000..26704774248aa71cd9fcd48053789418f0176652 GIT binary patch literal 10028 zcmbulby!qg_dZUkq@W-Oq6{^F#4s>Jcb9;aG|mh|H$#^o(v5UUgQSv@(hUMqDj*=; zAtCi0^znUspXa?kzdwG5!Kr-f5D5L}W*9wM0N+A)p#z+82329@BG6Qh&Z~{a`@EuVONSHOg zE2eeSfJK`aq5rmb2!C~8Y1R5UsXsM5KQ~Yei@*y5ade~pYwfm5m3?fTDoWctF;2v! z6^~S|_l-J`PPc?^;mh2$4CZy6?6gb}v}XPk9JbeD6i7?lfz9%$e^`eQe#YID*{ts% z^9E}5wP7#y-aQ@7ELsg2N~VE#tm8HEVEdeqT_Yx?#aOYX!Dd!Tl!D`jyP5Y3L$X+G zKG}_u2!TZAn&*ov`}H5ZnR+GkaVT7>d>TISu_L+dWaW`vYu@u3Um&|(4)xDESw}EF z-!B7X9k~Gf|51}coA*;gLjw%p`7Qq2j;lt$?f5n8zdHsYU;s`IPHuj#tK|91@T;!> zuLj^hbir3H_^qsr_7F(?Do1UutOiLToy`$QHCc&2vJ}kqzw`Ld+)ayj(NLcv4;*Ub zZTxVX4#0Ro<&Fm!5tO@w^Eg=knJQL*KhRm~nhe=37A<3q_n5MQbO>oOg6Dql3A{ZL z)y|g3QqqglVU~E?CD8quvqSmDbnA7UzmpwgDJ>*Dxw)QuiMOSQ*B&&l55cgl=qDIbvp5aYs7&1OTBDq|6&u>1^)5eyX z@t$J$#fs+Rm6v$_!DjU@gnYH5IE?ZAea7Z|?(#6*p!80&Br|g|!ti>*uUadAquW*( zLy{hw!A8^_%WGYa1dQL^)L>Q;UFI0c<$kgJgN8sq#AZ%OoKAh|kTBBNfia7XGJDqF zss-zI)D6@te26axCl=dNBI;+{cGxoj4F6|xB-ruT9CBFZVmNQbMJ6%u#le%9&d)9E zFh2Uaf51`pm2$n4dV}Kq=Q)L1a*>-gtrTs ziDHSzgoNAo#4i^<_9jo8saVt*kLOlOID5O+)5xcK1<|-!qdgE^dqK? z2QXMMXpm6hF5{m2-)eZL!-L_^=b;^s85?4JV`^@z6pF2uk1*!aQb~;F zx{cIUKB^S>g#5&LUzRuLbToFgCUKZ?k*}3BZO6#Q(q3D?R{6+j0knWw zOj(?caTJ9QbP1|(bQg7(s|-2glD7IaOc*Od`7=vOH}lztMRKN#gi8)BDvBU&kWty=?M#u!86_Dx&9cpUKE3Zh06skp6;Ej?$PCGBhNZ)@ z=MeO-ql<<1Jbc$uhU-7pIb$e#JrJS%52#0EirjbvR8?kM&sk5)M(de+~T+_C$Tr2<4_NhJD z(g5Mr6=1P|s2I$fUaBf7WS@0z-t-boHYlGauUD=gI=p#EcM3cO-w+OT4Q#x5G>LL% zuJ`vEyS2VDS39^Hws3>VgXztveEw{j&LGPm(O@4%k?A&hMK0PTsbzXbQN~}m6EgRbPGCy{iYKXuvD5300^pmoh zm?QYM#df}kfJm@NxCm*Jb5oHQ=oog6b?*FQdVgYP`Nzbm3JxDmG2RM(4vq|t)(sH? zF1*{!3HS?Zn)KIx_Z01;IPA` zr6k+yVJNx>|)STH}}11J<=LrEjP8(C({>>yf1cS#S@qE%Jy?03gFe6;+#^IgX@ z+v$WtQ*I$%uUDDQGY+di=!WQKj(-^suFl~x_fYbfU41s1c7O0byJ*U#=Rw$EQgTJF zsp3%d;GNVI{tOrIJ&Dd2yP?4pfdWrZB6XGDM$$^lOK19g6Uq#C-#HY1Zah8HOeiuf z?Ca3{mntgPG4BU=)#L;mW%46xZ$D< zZdErZdTO*2ERAD=)LIQ zo7Z1kV|A2+69T2-fJCa<+M)m;MF98;HK_f$<^4B|_|;0<3TX`hsiXJ3wg@NdD?|ZO z1^gTX4H^{Ddw=jR0AT?5v$sD1-k&!7r#ID~-Tblwq-qNI>kVz$pZ)xMDp%OX@&86E zR{+EbZ6_FQpSiK)m5DzmYw)Lmf8v;521>$EFe_WrU+~HCH*V5GIyjo!+5kXc4hVW! zn1iLfjfw3~WO7wN`ycIFfc;U-ke%+B&*u*)NcVe)&%bukg-q z`0>lQpKu7I?gU5uM0gtLF@IHD<-o5>@vBRM^^vU&3}K6gBWM66g>*zXnA@Rj9q_@w z5Ew|>1`V*yZP3$v1VgCVTElGqmdKbpIHDxYU=9FoG@em{{Z#;ic>tg%=Ef+qD_`<( za^hdXqyPVZzj{JH|3_bcOaFfRUwXe}S3UjK`{(cPR#)p*~uZzP0dgMUML@W&7qLiS^$3jE1U_tiWxp1 z8i{KEr2>UOf2qg;c+v3@cQ*YQqMvg_$JX_C30-$(Aev=Caskkh{KvBYGu3{PE@-&T z!-vlGzbF_8FDH5-{V$T{PjH*dZ|O=p5x3kQ2BANp)$x|+ISdo3CXBbHJi~ZSgT=6q z|1|y0=ek|&YlM7>ljZgbL67!d`E3Y1N=?9H@ny)#NqOU{aeX8g6508=GBd^GbB?u~ zeG~9}K49a9PV!Upq5G6fA1ZwgBL;BYtSngr5ufsiS z6>8zwn(bz6^r`ik*{5hNex+LD{rGCUCMTL<%KPjtN-Kf0q$OX*M{Wi62r|gH*sQKa z3?CYil&&=hbJ9i<8LJQh#yTZ2?dr&GXt}?}XeSQty1hVZJMQ8<>lK>2#sz$1gV}VR z|HPg0V!=)2JH_5uez=~JDAPzzANTdDSOAutl=1uv!W7~@#;YwwC5@G9D{dy>+3_7q(yMX{N>UGZck`5n%` zC~agZgVx-*oh6yXK`m}rm}I@DcpSGsv_CGrWp`Z2YmiJ^7Nxf6U#q4AiSGrP*Y!J& zE^=OMNR>*8NRr5?$jO>gCZ{~R^v!=2iE8%GXJGJj_Y=93Jk6BR8Ouk=TIkNlkf6R` z*F7e?(v>+?5#;7~w|HPTN+_c{+f^w+n7o%An3<`{VVQNmZ#$N8o5eF;9}z;tA9gJ> zi_Xo4dP$6R=`qD1UMkV+XH4ISjWwG%1=(Kls*99y#QR8$v{{r;Yq5SaE$}Q@3G%dK z+?CuLEfEkPkyI&!<@BCE)J%`Mq1o)|Q|wwMAlt+a93_`5f^SlE9us`Lw&w|Dd%WOQ z3}jHTBtjUZspcPN_3$ArZI>~FKbv^!{+7-)UiVTSBxF4zCir-iTjkv=gi zLNfSy2Z_RCwX*?{Q%-=)wim$Kqoi?MD^^2i`82s$2Thi|YgRf0h1TXYEkpQyxPx9Y zrIIX0O1Zu1#)&VcQ*ItyHBG&UY`?hMm7Xj7)>!lTVOz0`^>tRM3aSLN(gVoMot_&0c?V0XuIqCSfx z!}5+qDM!Nkj<>W;f#%_9fN`%JaGIc`PRY_;NoM4fN>@jUOZsJWf0&_mVPA3Ta8b%w z>qnx(hr#`MVNyHP1$99o%P39H;(G=_QDAKCX|mG%R OnQjuu>ylW}*G;x)`xE3V zalwVj7!AN|{LpIl>4dx99i-W)L0G0)L(#h`YwR|5%uNpH*HNBA5oCd`tqZotvo}SW zvm}JaPk0H5T(O%}t7<-#RUcDDl23s2e9}s{AFb(XT|(>DbWHmaOW}>vM0Mlb&fw9i z^i|y<)E$pdCxeSa&Hanv^7^_OCnwU1q9TI#wS>mHgb)1SnRv#}R`~snC?nWHn{0Dp zncgO7N!`e4Q!&$nktja5i%Gv${ybg$F74AHdA^W)yH=2X3x6tClG3k_A3Bx`Q@wa$ z7iJvsfn1+Z5PXfzjvV=Vs;dWO!Utm_oTsA>IR@M`X&KO$K(T&&zrG`IsW{KmVz7fj z_&#5 z=5tnkR?A9Cltk*HMsu``_@`C=>zCJ>7y7^8`b5zLQ%C9tm4-M=yU3UyJtYp$)T>#5 zjo6bYuNb=CzmQt+Botl5KD(8vtWC)tQp!a1_>(#ra~7EG8MK_-CeYfBsY(!UyY*Tv zRz`N%iFVk&J<*JO_KB_X(vpMXiDy#;@h-g|o2&n`o%@Kc;}f|j5b$eXOobl5F_LHO z#vRJFDMI~1?lA}0ZjAQ{3d*hv1r068QroTS(>`fspO3f4EPZ}@FQc_=X<~=H)euaU zMDE7r!1z#xLW(Bx!3P&nUm-?c?6iOv$9UG!>qI?TG3$OG)3zXKHtY;GYqzGXNUZx?AV zTRZwi+HjtCfBMYu0#~&4GQuY#o%Kv}?-|=S0!t#+QYyMgY3~Oo3_sM9s|t-jyRSU+ z_)6=VZnDA9fK}GYe+(UHGwKcqwWD;ad=+O^9(yNOJD_5vu%n}v{GL6wa}-ptoS3Fl z{4I+=z42&%s&9p3=)1-*ZC{Arxzrx@A0S`r20;);D+H)-LE8tc9ycPtwU-3v3fPPF;V$NG-eT715WMzLkqB za@>(oc60CDliBbuZQFIaq9SHd%opxYiO+m5n*ePD<)K1PFutaN8{5_OHC8whd7!g# z2}MhMclZS@6-)x!($nLUW2_Y4>fR=uU+4$QL#S}lC7BP3oCZRcEXHvX>g-^*vd73@ zo1G{~SLCT`PJic0Zv9k6vMwMGlAm#Zp2G`VO?xR-q;^eH`bCJ5W+kuZ>)V~S#b&Se zutlmK4|g8ysWr0IqUd~a^J8_Hy-%-aWWg%tC)TQMaYu9vHG+z6*n<4Nwlx(8`uY-mJ5Xxo9LEp^2*OTpX*y@pDS9aU&kUqEDe5dz-*q=^-b| zolFGac{vG=pJ;eIth6TNBx#4>b`zm5Xz6`m%1qxTsPtJkzzDC@Mm0^>I`7ludO@^TfbTDCv)bOpth)cJU zIz{wq@x%;hK~(3Y7P1{1WQ8V<(Wi2@~Ref zPCam_9_<0zs3dL)NSQUVQo$>+N|I=I+`oiVP4x4bmfEXLa4K&@2aWel;^2FCtt&7J zlfNaO=@gsyTo6@2l}?#9o(<;Lq_r}_ILCMiX)i1hi8i^g9VF4HAgNDZO=zoXuV2%& z>;h9(anwWnRE>uqjkWG=r*E4wj0HFpB?J$hdavVK(yDZ|Vd7pQ0p$Ag;vr2oks+MtN#fN92uMlN zJ=O-&JFus8R_y+7YSKU5R!d7NmT-El%vCfj`+A9NWZE5WPhk6A5=V?v&dyhio|$%1 zdWX8j+wXy28_obE3ALd%IP)wVbsKZD#u8|8uy*-G2E=$Ue{vLTsDEI5>@iLRm2sbY z%5vCiT^C`Qr>Dg>ko9>Su>&NZuh{{85!!md(J#uL7~C_AN8ov5n%HfvXB!&a$wo$~ zBSg{p-0&XJEL)I{kZ~emC)>^YBl2rFEn9{rjT%*`28*Qc+ySqudj$-kgKt+EM>%^~ z1Y%P?yJ|x{mZQ5B&Rnv;M}zV>_4ovs#3#jn^he$ZsT<}I z!`2&n8K91f|Q43>rV0AVNxMW%r8!2TXGZ)xxD$MFcAuDpc)?Ol`oo)}iJN_WM( z+e%uz!2Opx?XJ8GlxkmAOhn^KIWn7cN~c zAer>W%W#SW2(`fXf?)wNxB0!mXl&5^6Op~_ZG#QOA^r5aSfcb{%2QvL*TYdD`7GRq zs3EZ4X$MVEva1YPd$_(#>QlPx`%9z5USkG|vMJrpRQ@F+r27;)FBP`vYTuVp&UUij z7qzI|x_61PYw$bEjc)oZXC#y(UhlHjQ#+gQncC`jJ@sKO`?m!OAkaamOTJo7T0nBU z1^W=vzxaBzB%%9J1{5qBon={?43`7f7rS)|DW(pDvs~9B0w&#_j$+FjR}y$lT*Cv4 zQSk1S*^7ODrty3c$&nx867jXPxoNZ!v^UJeYAfQD^}RzbC$Q))4Oo}=d~eHF&Uol) zUvT)*CcS7})U-;>G@S+!C3AA+0;&%~9(N%XArnn4dCj(!e2bJr0ozW_acjKsW97l^ z6BEc%)+(c11dD0u9OwKk0lJQW_tEqj#4$$B(nAEic!JC!Z@+fUEIJihs;PADADQ02 zeN%p@i-saXu`5<)WG0-O5~uj(GsQ>B6?(T9j}I!Z1(Opb6ef2V$AwnV$bEO|M0f^R z@qY!6Q}Jih*LyDevmnT39x2N91S6^tR!yBnoIP6dqOTg%BAd)3*k%&V^4M%-_x1^FRQ~*26SW--Q zF1JZmtY^v6-50Nq3_T9YE~`&R=*zs^p(Afa?KaZvZ#+R6EsvX$+j`=RX1G&xr-y~f zcnemYD(&At)?YeG_Xd(@4@GagjMf_n;56s}S#=kRcNJ1^8^KB{O5Z$4FpO7{xwSy5 z?)rmdaX@*IPcBT9gd(9JnWP`9*SyERhecnZ@i}X?9!RHbB*>>lW=P8**C|X+W~v+8 zidrvC-onk?O@~M)BV8en2~Pf2zAe90U&m$Zg~5^Vzwv5>CyD-Cu0np{2BF=#i&sr*+`)<)tFxxYa*$BN+CBnZow$1Hh5SkDhU- zPDET$wo4WiK03o~n2T+FNk{Vwpk8wr*d>{RUlb zvs!IEb;)L`>QayH0MU`tx48R_${)rgm4piyMR8gH3l8qA9+A!pfn=RilD1u0{+mAI zktH-LpITD00@=A2s^o%V%X>&Z9i6CAv9->voJvr3Y(lJdZrbxd*m)JWuatsFMwE7! zMolb!@M1W+*>``O!}N)A!l$@X)jiS^**%p{tvy|NjY#*tGv8J0ea2BnM;_bx0bpp4 zMK2ff-Ys_X_ya<_wvUyCtD5CFXCbpEkF-mQooxaRYY+Vo+np7~ZZe}k%IK?jsz|87 zRoo-JQW_rNjVSgJa268^8a;9qyx7EHs*YqSyh? zxG!GHGOnt(#FW&-(})K0SZt8ZbiE*}fo*niPXDCTftTxOSETskwu6@t*;p-(yOQ_O0wnBH3)zN_$-Q0T^%Cn`VMlK4cs%@uXR17zJ#*SxqHn&J8|!37pzA;1I!yXJSS>BmdJubT z-abEFxpLu~l~g$Uq1DxyWJt#1I=$oetXg$Ic;K`GvvlO!iXWD0LSwr>DE!qD zTOJ({!PvhP_t8x<_v&zsh|+N;e*PDe{P+>)J>MYM$=+}b$EIGlz9GuUm)Ji zTjllReK@wa!ep3k$HCtAH#O?3oZ7rvN8h%up3I7^Uxs?-`kebDmVMad?Y3?5%>&s? zOic`|Tw*7-PLTea1qc6TQh(BR<|s6=#*9KbqD;^n7Ymxdb+9#dLZFWnFi&<{91nPXU?|mby9(!Yn1g#mU9Z$;ZhD=7I8nA-bHLOz0b}_sG`x|5o|q zRE?_x(ge+_g8A^#wBTPCfCmcYh5}3gzh&G|G--Wx0c?KDxOpIG0`I?NoSa-}+Wa3f z2+zOULOB1`mYa_s&D{TYTW&rmny>zE8Rt)y^`9~b`i#v#W&fi;2>-vwg+TsgGdGm? z-!}82J@St+AY9OY*$jbRec { } }); -it("get url and post upload", async () => { +it("Item life cycle", async () => { // Create a folder const respCreate = await client.folderCreate({ path: FOLDER_FILES, From eff56d22685d4eb130c4a27d015ca52a1bd8a03e Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 16 Jan 2024 17:29:55 -0300 Subject: [PATCH 09/36] address PR comments --- examples/audit/README.md | 2 +- examples/embargo/README.md | 2 +- examples/file_scan/README.md | 2 +- examples/file_scan/package.json | 2 +- examples/intel/README.md | 2 +- examples/intel/package.json | 2 +- examples/redact/README.md | 2 +- examples/redact/package.json | 2 +- examples/store/README.md | 2 +- examples/store/folder_create_n_delete.mjs | 2 +- examples/store/item_life_cycle.mjs | 8 ++++---- examples/store/package.json | 4 ++-- examples/store/put_split_upload.mjs | 2 +- examples/store/put_transfer_method_multipart.mjs | 2 +- examples/store/put_transfer_method_post_url.mjs | 2 +- 15 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/audit/README.md b/examples/audit/README.md index cd6feb13c..a9c603a46 100644 --- a/examples/audit/README.md +++ b/examples/audit/README.md @@ -2,7 +2,7 @@ ## Setup -Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_AUDIT_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Audit service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_AUDIT_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Audit service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. ## Run example diff --git a/examples/embargo/README.md b/examples/embargo/README.md index 0e15b3a43..a830a9e9b 100644 --- a/examples/embargo/README.md +++ b/examples/embargo/README.md @@ -2,7 +2,7 @@ ## Setup -Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_EMBARGO_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Embargo service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_EMBARGO_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Embargo service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. ## Run examples diff --git a/examples/file_scan/README.md b/examples/file_scan/README.md index f4a295c99..25438c293 100644 --- a/examples/file_scan/README.md +++ b/examples/file_scan/README.md @@ -2,7 +2,7 @@ ## Setup -Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_FILE_SCAN_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to FileScan services [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_FILE_SCAN_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to FileScan services [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. Run examples diff --git a/examples/file_scan/package.json b/examples/file_scan/package.json index 17a81e515..2594f7271 100644 --- a/examples/file_scan/package.json +++ b/examples/file_scan/package.json @@ -7,7 +7,7 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Pangea", - "license": "ISC", + "license": "MIT", "dependencies": { "pangea-node-sdk": "3.5.0" } diff --git a/examples/intel/README.md b/examples/intel/README.md index 4041496d0..be8459d89 100644 --- a/examples/intel/README.md +++ b/examples/intel/README.md @@ -2,7 +2,7 @@ ## Setup -Set environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_INTEL_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Intel services [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. +Set environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_INTEL_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Intel services [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. Run examples diff --git a/examples/intel/package.json b/examples/intel/package.json index 17a81e515..2594f7271 100644 --- a/examples/intel/package.json +++ b/examples/intel/package.json @@ -7,7 +7,7 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Pangea", - "license": "ISC", + "license": "MIT", "dependencies": { "pangea-node-sdk": "3.5.0" } diff --git a/examples/redact/README.md b/examples/redact/README.md index 53ad9b6cb..7df6855bc 100644 --- a/examples/redact/README.md +++ b/examples/redact/README.md @@ -2,7 +2,7 @@ ## Setup -Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_REDACT_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Redact service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_REDACT_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Redact service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. ## Run example diff --git a/examples/redact/package.json b/examples/redact/package.json index ee7979843..943d935eb 100644 --- a/examples/redact/package.json +++ b/examples/redact/package.json @@ -7,7 +7,7 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Pangea", - "license": "ISC", + "license": "MIT", "dependencies": { "pangea-node-sdk": "3.5.0" } diff --git a/examples/store/README.md b/examples/store/README.md index 7894c37f0..1ad1bab1a 100644 --- a/examples/store/README.md +++ b/examples/store/README.md @@ -2,7 +2,7 @@ ## Setup -Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_STORE_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Store service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your pangea domain. +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_STORE_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Store service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. ## Run example diff --git a/examples/store/folder_create_n_delete.mjs b/examples/store/folder_create_n_delete.mjs index 5e8bdec70..659f93552 100644 --- a/examples/store/folder_create_n_delete.mjs +++ b/examples/store/folder_create_n_delete.mjs @@ -2,7 +2,7 @@ import { PangeaConfig, StoreService } from "pangea-node-sdk"; -// Load pangea token and domain from environment variables +// Load Pangea token and domain from environment variables const token = process.env.PANGEA_STORE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); diff --git a/examples/store/item_life_cycle.mjs b/examples/store/item_life_cycle.mjs index 77ed1acd7..bcadbde20 100644 --- a/examples/store/item_life_cycle.mjs +++ b/examples/store/item_life_cycle.mjs @@ -8,7 +8,7 @@ import { } from "pangea-node-sdk"; import * as fs from "fs"; -// Load pangea token and domain from environment variables +// Load Pangea token and domain from environment variables const token = process.env.PANGEA_STORE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); @@ -77,7 +77,7 @@ const filepath = "./testfile.pdf"; console.log(`\tTags: ${respPutId.result.object.tags}`); console.log("\nUpdating file with full metadata and tags..."); - // Update file. full metadata and tags + // Update file with full metadata and tags const respUpdate = await client.update({ id: respPutPath.result.object.id, metadata: metadata, @@ -89,8 +89,8 @@ const filepath = "./testfile.pdf"; console.log(`\tMetadata: ${respUpdate.result.object.metadata}`); console.log(`\tTags: ${respUpdate.result.object.tags}`); - console.log("\nUpdating file adding metadata and tags..."); - // Update file. add metadata and tags + console.log("\nUpdating file with additional metadata and tags..."); + // Update file with added metadata and tags const addMetadata = { field3: "value3" }; const addTags = ["tag3"]; diff --git a/examples/store/package.json b/examples/store/package.json index eac206eb1..8627663a4 100644 --- a/examples/store/package.json +++ b/examples/store/package.json @@ -1,13 +1,13 @@ { "name": "redact_examples", "version": "2.0.0", - "description": "Pangea Redact text example", + "description": "Pangea Store text example", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Pangea", - "license": "ISC", + "license": "MIT", "dependencies": { "pangea-node-sdk": "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz" } diff --git a/examples/store/put_split_upload.mjs b/examples/store/put_split_upload.mjs index df4e26f4b..e304915f5 100644 --- a/examples/store/put_split_upload.mjs +++ b/examples/store/put_split_upload.mjs @@ -8,7 +8,7 @@ import { } from "pangea-node-sdk"; import * as fs from "fs"; -// Load pangea token and domain from environment variables +// Load Pangea token and domain from environment variables const token = process.env.PANGEA_STORE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); diff --git a/examples/store/put_transfer_method_multipart.mjs b/examples/store/put_transfer_method_multipart.mjs index 1322583da..d4892adbc 100644 --- a/examples/store/put_transfer_method_multipart.mjs +++ b/examples/store/put_transfer_method_multipart.mjs @@ -3,7 +3,7 @@ import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; import * as fs from "fs"; -// Load pangea token and domain from environment variables +// Load Pangea token and domain from environment variables const token = process.env.PANGEA_STORE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); diff --git a/examples/store/put_transfer_method_post_url.mjs b/examples/store/put_transfer_method_post_url.mjs index 80ac9c621..53c346140 100644 --- a/examples/store/put_transfer_method_post_url.mjs +++ b/examples/store/put_transfer_method_post_url.mjs @@ -3,7 +3,7 @@ import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; import * as fs from "fs"; -// Load pangea token and domain from environment variables +// Load Pangea token and domain from environment variables const token = process.env.PANGEA_STORE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); From 58bf16927716a2127a4235f725a00f68087adafb Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 16 Jan 2024 18:04:41 -0300 Subject: [PATCH 10/36] rename example --- .../{folder_create_n_delete.mjs => folder_create_and_delete.mjs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/store/{folder_create_n_delete.mjs => folder_create_and_delete.mjs} (100%) diff --git a/examples/store/folder_create_n_delete.mjs b/examples/store/folder_create_and_delete.mjs similarity index 100% rename from examples/store/folder_create_n_delete.mjs rename to examples/store/folder_create_and_delete.mjs From 8540833484e9281f10e62fda0dd09f766ca636c7 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 17 Jan 2024 14:41:11 -0300 Subject: [PATCH 11/36] update example name --- examples/store/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/store/package.json b/examples/store/package.json index 8627663a4..cf10c663d 100644 --- a/examples/store/package.json +++ b/examples/store/package.json @@ -1,5 +1,5 @@ { - "name": "redact_examples", + "name": "store_examples", "version": "2.0.0", "description": "Pangea Store text example", "main": "index.js", From 16eb47dbad951de21c9509ec799c7a5301a49ce7 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 15 Feb 2024 14:14:33 -0300 Subject: [PATCH 12/36] Add `share/link/send` support --- .../pangea-node-sdk/src/services/store.ts | 23 +++++++++++++++++++ packages/pangea-node-sdk/src/types.ts | 17 ++++++++++++++ .../tests/integration/store.test.ts | 17 ++++++++++++-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/packages/pangea-node-sdk/src/services/store.ts b/packages/pangea-node-sdk/src/services/store.ts index bd1ce028a..81703f70e 100644 --- a/packages/pangea-node-sdk/src/services/store.ts +++ b/packages/pangea-node-sdk/src/services/store.ts @@ -301,6 +301,29 @@ class StoreService extends BaseService { ): Promise> { return this.post("v1beta/share/link/delete", request); } + + /** + * @summary Send share links + * @description Send share links. + * @operationId store_post_v1beta_share_link_send + * @param {Store.ShareLinkDeleteRequest} request + * @returns {Promise} - A promise representing an async call to the send share links endpoint. + * @example + * ```js + * const resp = await client.shareLinkSend({ + * links: [{ + * id: linkID, + * email: "user@email.com", + * }], + * sender_email: "sender@email.com", + * sender_name: "Sender Name" + * }) + */ + shareLinkSend( + request: Store.ShareLinkSendRequest + ): Promise> { + return this.post("v1beta/share/link/send", request); + } } export class StoreUploader { diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index 825715e70..9f79b2904 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -2242,6 +2242,8 @@ export namespace Store { expires_at?: string; max_access_count?: number; authenticators: Authenticator[]; + message?: string; + title?: string; } export interface ShareLinkCreateRequest { @@ -2337,4 +2339,19 @@ export namespace Store { export interface ShareLinkDeleteResult { share_link_objects: ShareLinkItem[]; } + + export interface ShareLinkSendItem { + id: string; + email: string; + } + + export interface ShareLinkSendRequest { + links: ShareLinkSendItem[]; + sender_email: string; + sender_name?: string; + } + + export interface ShareLinkSendResult { + share_link_objects: ShareLinkItem[]; + } } diff --git a/packages/pangea-node-sdk/tests/integration/store.test.ts b/packages/pangea-node-sdk/tests/integration/store.test.ts index 0e80ca943..1b637ae6e 100644 --- a/packages/pangea-node-sdk/tests/integration/store.test.ts +++ b/packages/pangea-node-sdk/tests/integration/store.test.ts @@ -70,7 +70,6 @@ it("Put file. Multipart transfer_method", async () => { } ); expect(respPut.success).toBeTruthy(); - console.log(respPut.result); } catch (e) { console.log(e); expect(false).toBeTruthy(); @@ -329,8 +328,22 @@ it("Item life cycle", async () => { expect(link?.id).toBeDefined(); expect(link?.targets.length).toBe(1); - // Get share link const linkID = link?.id ?? ""; + + // Send share link + const respSendLink = await client.shareLinkSend({ + links: [ + { + id: linkID, + email: "user@email.com", + }, + ], + sender_email: "sender@email.com", + sender_name: "Sender Name", + }); + expect(respSendLink.result.share_link_objects.length).toBe(1); + + // Get share link const respGetLink = await client.shareLinkGet({ id: linkID, }); From 92cb07b330bd72745de6b848da1908758000fd31 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 5 Mar 2024 12:33:31 -0300 Subject: [PATCH 13/36] Rename `Store` to `Share` --- packages/pangea-node-sdk/src/index.ts | 4 +- packages/pangea-node-sdk/src/request.ts | 3 +- packages/pangea-node-sdk/src/response.ts | 2 +- .../pangea-node-sdk/src/services/index.ts | 4 +- .../src/services/{store.ts => share.ts} | 108 +++++++++--------- packages/pangea-node-sdk/src/types.ts | 2 +- .../{store.test.ts => share.test.ts} | 38 +++--- 7 files changed, 80 insertions(+), 81 deletions(-) rename packages/pangea-node-sdk/src/services/{store.ts => share.ts} (75%) rename packages/pangea-node-sdk/tests/integration/{store.test.ts => share.test.ts} (92%) diff --git a/packages/pangea-node-sdk/src/index.ts b/packages/pangea-node-sdk/src/index.ts index b942fc8d3..f5014c2b5 100755 --- a/packages/pangea-node-sdk/src/index.ts +++ b/packages/pangea-node-sdk/src/index.ts @@ -22,7 +22,7 @@ export { } from "./utils/utils.js"; export { FileScanUploader } from "./services/file_scan.js"; -export { StoreUploader } from "./services/store.js"; +export { ShareUploader } from "./services/share.js"; export const PangeaConfig = _PangeaConfig; export const PangeaRequest = _PangeaRequest; @@ -40,4 +40,4 @@ export const URLIntelService = services.URLIntelService; export const UserIntelService = services.UserIntelService; export const VaultService = services.VaultService; export const FileScanService = services.FileScanService; -export const StoreService = services.StoreService; +export const ShareService = services.ShareService; diff --git a/packages/pangea-node-sdk/src/request.ts b/packages/pangea-node-sdk/src/request.ts index 7aaaee7d7..6bcc4f56d 100644 --- a/packages/pangea-node-sdk/src/request.ts +++ b/packages/pangea-node-sdk/src/request.ts @@ -172,7 +172,8 @@ class PangeaRequest { } } - form.append(fileData.name, this.getFileToForm(fileData.file), { + // Right now, only accept the file with name "file" + form.append("file", this.getFileToForm(fileData.file), { contentType: "application/octet-stream", }); diff --git a/packages/pangea-node-sdk/src/response.ts b/packages/pangea-node-sdk/src/response.ts index 41b33839e..d31b7e0dc 100644 --- a/packages/pangea-node-sdk/src/response.ts +++ b/packages/pangea-node-sdk/src/response.ts @@ -40,7 +40,7 @@ export class AttachedFile { destFolder = "."; } if (!filename) { - filename = this.filename ? this.filename : "defaultName.txt"; + filename = this.filename ? this.filename : "defaultName"; } if (!fs.existsSync(destFolder)) { // If it doesn't exist, create it diff --git a/packages/pangea-node-sdk/src/services/index.ts b/packages/pangea-node-sdk/src/services/index.ts index 546dec873..12b26b16f 100644 --- a/packages/pangea-node-sdk/src/services/index.ts +++ b/packages/pangea-node-sdk/src/services/index.ts @@ -12,7 +12,7 @@ import { } from "./intel.js"; import VaultService from "./vault.js"; import { FileScanService } from "./file_scan.js"; -import StoreService from "./store.js"; +import ShareService from "./share.js"; export default { AuditService, @@ -27,5 +27,5 @@ export default { UserIntelService, VaultService, FileScanService, - StoreService, + ShareService: ShareService, }; diff --git a/packages/pangea-node-sdk/src/services/store.ts b/packages/pangea-node-sdk/src/services/share.ts similarity index 75% rename from packages/pangea-node-sdk/src/services/store.ts rename to packages/pangea-node-sdk/src/services/share.ts index 81703f70e..d1eac5704 100644 --- a/packages/pangea-node-sdk/src/services/store.ts +++ b/packages/pangea-node-sdk/src/services/share.ts @@ -1,25 +1,25 @@ import PangeaResponse from "@src/response.js"; import BaseService from "./base.js"; import PangeaConfig from "@src/config.js"; -import { Store, FileData, TransferMethod, FileUploadParams } from "@src/types.js"; +import { Share, FileData, TransferMethod, FileUploadParams } from "@src/types.js"; import PangeaRequest from "@src/request.js"; import { PangeaErrors } from "@src/errors.js"; import { getFileUploadParams } from "@src/index.js"; /** - * StoreService class provides methods for interacting with the Store Service + * ShareService class provides methods for interacting with the Share Service * @extends BaseService */ -class StoreService extends BaseService { +class ShareService extends BaseService { constructor(token: string, config: PangeaConfig) { - super("store", token, config); + super("share", token, config); } /** * @summary Delete * @description Delete object by ID or path. If both are supplied, the path must match that of the object represented by the ID. - * @operationId store_post_v1beta_delete - * @param {Store.DeleteRequest} request + * @operationId share_post_v1beta_delete + * @param {Share.DeleteRequest} request * @returns {Promise} - A promise representing an async call to the delete endpoint. * @example * ```js @@ -27,15 +27,15 @@ class StoreService extends BaseService { * const response = await client.delete(request); * ``` */ - delete(request: Store.DeleteRequest): Promise> { + delete(request: Share.DeleteRequest): Promise> { return this.post("v1beta/delete", request); } /** * @summary Create a folder * @description Create a folder, either by name or path and parent_id. - * @operationId store_post_v1beta_folder_create - * @param {Store.FolderCreateRequest} request + * @operationId share_post_v1beta_folder_create + * @param {Share.FolderCreateRequest} request * @returns {Promise} - A promise representing an async call to the folder create endpoint. * @example * ```js @@ -53,16 +53,16 @@ class StoreService extends BaseService { * ``` */ folderCreate( - request: Store.FolderCreateRequest - ): Promise> { + request: Share.FolderCreateRequest + ): Promise> { return this.post("v1beta/folder/create", request); } /** * @summary Get an object * @description Get object. If both ID and Path are supplied, the call will fail if the target object doesn't match both properties. - * @operationId store_post_v1beta_get - * @param {Store.GetRequest} request + * @operationId share_post_v1beta_get + * @param {Share.GetRequest} request * @returns {Promise} - A promise representing an async call to the get item endpoint. * @example * ```js @@ -74,15 +74,15 @@ class StoreService extends BaseService { * const response = await client.getItem(request); * ``` */ - getItem(request: Store.GetRequest): Promise> { + getItem(request: Share.GetRequest): Promise> { return this.post("v1beta/get", request); } /** * @summary Get archive * @description Get an archive file of multiple objects. - * @operationId store_post_v1beta_get_archive - * @param {Store.GetArchiveRequest} request + * @operationId share_post_v1beta_get_archive + * @param {Share.GetArchiveRequest} request * @returns {Promise} - A promise representing an async call to the get archive endpoint. * @example * ```js @@ -90,15 +90,15 @@ class StoreService extends BaseService { * const response = await client.getArchive(request); * ``` */ - getArchive(request: Store.GetArchiveRequest): Promise> { + getArchive(request: Share.GetArchiveRequest): Promise> { return this.post("v1beta/get_archive", request); } /** * @summary List * @description List or filter/search records. - * @operationId store_post_v1beta_list - * @param {Store.ListRequest} request + * @operationId share_post_v1beta_list + * @param {Share.ListRequest} request * @returns {Promise} - A promise representing an async call to the list endpoint. * @example * ```js @@ -106,15 +106,15 @@ class StoreService extends BaseService { * const response = await client.list(request); * ``` */ - list(request: Store.ListRequest = {}): Promise> { + list(request: Share.ListRequest = {}): Promise> { return this.post("v1beta/list", request); } /** * @summary Upload a file [beta] * @description Upload a file. - * @operationId store_post_v1beta_put - * @param {Store.PutRequest} request + * @operationId share_post_v1beta_put + * @param {Share.PutRequest} request * @param {FileData} fileData * @returns {Promise} - A promise representing an async call to the put endpoint. * @example @@ -138,7 +138,7 @@ class StoreService extends BaseService { * const response = await client.put(request, fileData); * ``` */ - put(request: Store.PutRequest, fileData: FileData): Promise> { + put(request: Share.PutRequest, fileData: FileData): Promise> { let fsData = {} as FileUploadParams; if (!request.transfer_method || request.transfer_method === TransferMethod.POST_URL) { @@ -158,8 +158,8 @@ class StoreService extends BaseService { /** * @summary Request upload URL * @description Request an upload URL. - * @operationId store_post_v1beta_put 2 - * @param {Store.PutRequest} request + * @operationId share_post_v1beta_put 2 + * @param {Share.PutRequest} request * @param {FileData} fileData * @returns {Promise} - A promise representing an async call to the put endpoint. * @example @@ -183,7 +183,7 @@ class StoreService extends BaseService { * const response = await client.requestUploadURL(request); * ``` */ - requestUploadURL(request: Store.PutRequest): Promise> { + requestUploadURL(request: Share.PutRequest): Promise> { if ( request.transfer_method === TransferMethod.POST_URL && (!request.size || !request.crc32c || !request.sha256) @@ -199,8 +199,8 @@ class StoreService extends BaseService { /** * @summary Update a file * @description Update a file. - * @operationId store_post_v1beta_update - * @param {Store.UpdateRequest} request + * @operationId share_post_v1beta_update + * @param {Share.UpdateRequest} request * @returns {Promise} - A promise representing an async call to the update endpoint. * @example * ```js @@ -217,25 +217,25 @@ class StoreService extends BaseService { * const response = await client.update(request); * ``` */ - update(request: Store.UpdateRequest): Promise> { + update(request: Share.UpdateRequest): Promise> { return this.post("v1beta/update", request); } /** * @summary Create share links * @description Create a share link. - * @operationId store_post_v1beta_share_link_create - * @param {Store.ShareLinkCreateRequest} request + * @operationId share_post_v1beta_share_link_create + * @param {Share.ShareLinkCreateRequest} request * @returns {Promise} - A promise representing an async call to the share link create endpoint. * @example * ```js * const authenticator = { - * auth_type: Store.AuthenticatorType.PASSWORD, + * auth_type: Share.AuthenticatorType.PASSWORD, * auth_context: "my_fav_Pa55word", * }; * const link = { * targets: ["pos_3djfmzg2db4c6donarecbyv5begtj2bm"], - * link_type: Store.LinkType.DOWNLOAD, + * link_type: Share.LinkType.DOWNLOAD, * authenticators: [authenticator], * }; * const request = { links: [link] }; @@ -243,16 +243,16 @@ class StoreService extends BaseService { * ``` */ shareLinkCreate( - request: Store.ShareLinkCreateRequest - ): Promise> { + request: Share.ShareLinkCreateRequest + ): Promise> { return this.post("v1beta/share/link/create", request); } /** * @summary Get share link * @description Get a share link. - * @operationId store_post_v1beta_share_link_get - * @param {Store.ShareLinkGetRequest} request + * @operationId share_post_v1beta_share_link_get + * @param {Share.ShareLinkGetRequest} request * @returns {Promise} - A promise representing an async call to the share link get endpoint. * @example * ```js @@ -261,16 +261,16 @@ class StoreService extends BaseService { * ``` */ shareLinkGet( - request: Store.ShareLinkGetRequest - ): Promise> { + request: Share.ShareLinkGetRequest + ): Promise> { return this.post("v1beta/share/link/get", request); } /** * @summary List share links * @description Look up share links by filter options. - * @operationId store_post_v1beta_share_link_list - * @param {Store.ShareLinkListRequest} request + * @operationId share_post_v1beta_share_link_list + * @param {Share.ShareLinkListRequest} request * @returns {Promise} - A promise representing an async call to the share link list endpoint. * @example * ```js @@ -279,16 +279,16 @@ class StoreService extends BaseService { * ``` */ shareLinkList( - request: Store.ShareLinkListRequest = {} - ): Promise> { + request: Share.ShareLinkListRequest = {} + ): Promise> { return this.post("v1beta/share/link/list", request); } /** * @summary Delete share links * @description Delete share links. - * @operationId store_post_v1beta_share_link_delete - * @param {Store.ShareLinkDeleteRequest} request + * @operationId share_post_v1beta_share_link_delete + * @param {Share.ShareLinkDeleteRequest} request * @returns {Promise} - A promise representing an async call to the delete share links endpoint. * @example * ```js @@ -297,16 +297,16 @@ class StoreService extends BaseService { * ``` */ shareLinkDelete( - request: Store.ShareLinkDeleteRequest - ): Promise> { + request: Share.ShareLinkDeleteRequest + ): Promise> { return this.post("v1beta/share/link/delete", request); } /** * @summary Send share links * @description Send share links. - * @operationId store_post_v1beta_share_link_send - * @param {Store.ShareLinkDeleteRequest} request + * @operationId share_post_v1beta_share_link_send + * @param {Share.ShareLinkDeleteRequest} request * @returns {Promise} - A promise representing an async call to the send share links endpoint. * @example * ```js @@ -320,14 +320,14 @@ class StoreService extends BaseService { * }) */ shareLinkSend( - request: Store.ShareLinkSendRequest - ): Promise> { + request: Share.ShareLinkSendRequest + ): Promise> { return this.post("v1beta/share/link/send", request); } } -export class StoreUploader { - protected serviceName: string = "FileScanFileUploader"; +export class ShareUploader { + protected serviceName: string = "ShareFileUploader"; protected request_: PangeaRequest | undefined = undefined; constructor() {} @@ -357,4 +357,4 @@ export class StoreUploader { } } -export default StoreService; +export default ShareService; diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index 074429fb8..9bd4efcc1 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -1948,7 +1948,7 @@ export namespace AuthN { } } -export namespace Store { +export namespace Share { export enum FileFormat { F3G2 = "3G2", F3GP = "3GP", diff --git a/packages/pangea-node-sdk/tests/integration/store.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts similarity index 92% rename from packages/pangea-node-sdk/tests/integration/store.test.ts rename to packages/pangea-node-sdk/tests/integration/share.test.ts index 1b637ae6e..b9f0d82f0 100644 --- a/packages/pangea-node-sdk/tests/integration/store.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -6,15 +6,15 @@ import { getTestDomain, getTestToken, } from "../../src/utils/utils.js"; -import { StoreService } from "../../src/index.js"; -import { Store, TransferMethod } from "../../src/types.js"; -import { StoreUploader } from "@src/services/store.js"; +import { ShareService } from "../../src/index.js"; +import { Share, TransferMethod } from "../../src/types.js"; +import { ShareUploader } from "@src/services/share.js"; const testEnvironment = TestEnvironment.DEVELOP; const token = getTestToken(testEnvironment); const testHost = getTestDomain(testEnvironment); const config = new PangeaConfig({ domain: testHost, customUserAgent: "sdk-test" }); -const client = new StoreService(token, config); +const client = new ShareService(token, config); const TIME = Math.round(Date.now() / 1000); const FOLDER_DELETE = "/sdk_tests/node/delete/" + TIME; @@ -25,7 +25,7 @@ const TAGS = ["tag1", "tag2"]; const ADD_TAGS = ["tag3"]; const testfilePath = "./tests/testdata/testfile.pdf"; -jest.setTimeout(120000); +jest.setTimeout(60000); const delay = async (ms: number) => new Promise((resolve) => { @@ -58,7 +58,7 @@ it("Folder create/delete", async () => { it("Put file. Multipart transfer_method", async () => { try { - const name = TIME + "_file_post_url"; + const name = TIME + "_file_multipart"; const respPut = await client.put( { name: name, @@ -100,25 +100,24 @@ it("get url and put upload", async () => { let response; const name = TIME + "_file_split_put_url"; try { - const request: Store.PutRequest = { + const request: Share.PutRequest = { transfer_method: TransferMethod.PUT_URL, name: name, }; response = await client.requestUploadURL(request); } catch (e) { console.log(e); - expect(false).toBeTruthy(); throw e; } const url = response.accepted_result?.put_url || ""; - const uploader = new StoreUploader(); + const uploader = new ShareUploader(); await uploader.uploadFile( url, { file: testfilePath, - name: "file", + name: name, }, { transfer_method: TransferMethod.PUT_URL, @@ -146,7 +145,7 @@ it("get url and post upload", async () => { try { const params = getFileUploadParams(testfilePath); - const request: Store.PutRequest = { + const request: Share.PutRequest = { transfer_method: TransferMethod.POST_URL, name: name, crc32c: params.crc32c, @@ -157,14 +156,13 @@ it("get url and post upload", async () => { response = await client.requestUploadURL(request); } catch (e) { console.log(e); - expect(false).toBeTruthy(); throw e; } const url = response.accepted_result?.post_url || ""; const file_details = response.accepted_result?.post_form_data; - const uploader = new StoreUploader(); + const uploader = new ShareUploader(); await uploader.uploadFile( url, { @@ -274,7 +272,7 @@ it("Item life cycle", async () => { // Get archive const respGetArchive1 = await client.getArchive({ ids: [folderID], - format: Store.ArchiveFormat.ZIP, + format: Share.ArchiveFormat.ZIP, transfer_method: TransferMethod.MULTIPART, }); expect(respGetArchive1.success).toBeTruthy(); @@ -286,7 +284,7 @@ it("Item life cycle", async () => { const respGetArchive2 = await client.getArchive({ ids: [folderID], - format: Store.ArchiveFormat.TAR, + format: Share.ArchiveFormat.TAR, transfer_method: TransferMethod.DEST_URL, }); expect(respGetArchive2.success).toBeTruthy(); @@ -299,13 +297,13 @@ it("Item life cycle", async () => { expect(downloadedFile.file.length).toBeGreaterThan(0); // Create share link - const authenticators: Store.Authenticator[] = [ - { auth_type: Store.AuthenticatorType.PASSWORD, auth_context: "somepassword" }, + const authenticators: Share.Authenticator[] = [ + { auth_type: Share.AuthenticatorType.PASSWORD, auth_context: "somepassword" }, ]; - const linkList: Store.ShareLinkCreateItem[] = [ + const linkList: Share.ShareLinkCreateItem[] = [ { targets: [folderID], - link_type: Store.LinkType.EDITOR, + link_type: Share.LinkType.EDITOR, max_access_count: 3, authenticators: authenticators, }, @@ -323,7 +321,7 @@ it("Item life cycle", async () => { expect(link?.access_count).toBe(0); expect(link?.max_access_count).toBe(3); expect(link?.authenticators.length).toBe(1); - expect(link?.authenticators[0]?.auth_type).toBe(Store.AuthenticatorType.PASSWORD); + expect(link?.authenticators[0]?.auth_type).toBe(Share.AuthenticatorType.PASSWORD); expect(link?.link).toBeDefined(); expect(link?.id).toBeDefined(); expect(link?.targets.length).toBe(1); From 86c09b2d609ecb18d8cb442286da19cbc1399e7f Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 5 Mar 2024 13:09:03 -0300 Subject: [PATCH 14/36] Updates: Improve downloaded file naming --- packages/pangea-node-sdk/.gitignore | 1 + packages/pangea-node-sdk/src/request.ts | 30 +++++++++++++++--- packages/pangea-node-sdk/src/response.ts | 2 +- .../pangea-node-sdk/src/utils/multipart.ts | 31 ++++++++++++++++++- .../tests/integration/share.test.ts | 1 + 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/packages/pangea-node-sdk/.gitignore b/packages/pangea-node-sdk/.gitignore index ecf919f4e..a650315b2 100644 --- a/packages/pangea-node-sdk/.gitignore +++ b/packages/pangea-node-sdk/.gitignore @@ -11,6 +11,7 @@ .DS_Store *.pem yarn-error.log +*.tar # Editors .idea diff --git a/packages/pangea-node-sdk/src/request.ts b/packages/pangea-node-sdk/src/request.ts index 6bcc4f56d..eed3c139f 100644 --- a/packages/pangea-node-sdk/src/request.ts +++ b/packages/pangea-node-sdk/src/request.ts @@ -86,6 +86,25 @@ class PangeaRequest { return this.handleHttpResponse(response, options); } + private getFilenameFromContentDisposition( + contentDispositionHeader: string | string[] | undefined + ): string | undefined { + let contentDisposition = ""; + if (contentDispositionHeader === undefined) { + return undefined; + } else if (Array.isArray(contentDispositionHeader)) { + contentDisposition = contentDispositionHeader[0] ?? contentDisposition; + } else { + contentDisposition = contentDispositionHeader; + } + + return getHeaderField(contentDisposition, "filename", undefined); + } + + private getFilenameFromURL(url: string): string | undefined { + return url.split("/").pop()?.split("?")[0]; + } + public async downloadFile(url: string): Promise { const options = new Options({ retry: { limit: this.config.requestRetries }, @@ -93,10 +112,12 @@ class PangeaRequest { }); const response = (await got.get(url, options)) as Response; - let contentDispositionHeader = response.headers["Content-Disposition"]; - let contentDisposition = ""; - if (Array.isArray(contentDispositionHeader)) { - contentDisposition = contentDispositionHeader[0] ?? contentDisposition; + let filename = this.getFilenameFromContentDisposition(response.headers["content-disposition"]); + if (filename === undefined) { + filename = this.getFilenameFromURL(url); + if (filename === undefined) { + filename = "default_filename"; + } } const contentTypeHeader = response.headers["Content-Type"] ?? ""; @@ -105,7 +126,6 @@ class PangeaRequest { contentType = contentTypeHeader[0] ?? contentType; } - const filename = getHeaderField(contentDisposition, "filename", "defaultFilename"); return new AttachedFile(filename, response.rawBody, contentType); } diff --git a/packages/pangea-node-sdk/src/response.ts b/packages/pangea-node-sdk/src/response.ts index d31b7e0dc..9a3356670 100644 --- a/packages/pangea-node-sdk/src/response.ts +++ b/packages/pangea-node-sdk/src/response.ts @@ -40,7 +40,7 @@ export class AttachedFile { destFolder = "."; } if (!filename) { - filename = this.filename ? this.filename : "defaultName"; + filename = this.filename ? this.filename : "defaultSaveFilename"; } if (!fs.existsSync(destFolder)) { // If it doesn't exist, create it diff --git a/packages/pangea-node-sdk/src/utils/multipart.ts b/packages/pangea-node-sdk/src/utils/multipart.ts index 3e9b4080e..f0ebbd598 100644 --- a/packages/pangea-node-sdk/src/utils/multipart.ts +++ b/packages/pangea-node-sdk/src/utils/multipart.ts @@ -1,3 +1,28 @@ +/** + * MIT License + * + * Copyright (c) 2018-2022 Ignacio Mazzara + * Copyright (c) 2024 Pangea Cyber Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + type Part = { contentDispositionHeader: string; contentTypeHeader: string; @@ -150,7 +175,11 @@ function process(part: Part): Input { return input as Input; } -export function getHeaderField(header: string, field: string, defaultValue: string): string { +export function getHeaderField( + header: string, + field: string, + defaultValue: string | undefined +): string | undefined { const parts = header.split(field + "="); if (parts.length > 1 && parts[1]) { const valueParts = parts[1].split(";"); diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index b9f0d82f0..0e6f75799 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -294,6 +294,7 @@ it("Item life cycle", async () => { // Download file const url = respGetArchive2.result.dest_url ?? ""; let downloadedFile = await client.downloadFile(url); + downloadedFile.save("./"); expect(downloadedFile.file.length).toBeGreaterThan(0); // Create share link From 6eef763cf02e629f52d2cbefdcbcdac7c6b723a9 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 6 Mar 2024 13:42:12 -0300 Subject: [PATCH 15/36] fixup! Add message, title, notify_email and tags --- packages/pangea-node-sdk/src/types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index 9bd4efcc1..f56cbc58a 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -2286,6 +2286,8 @@ export namespace Share { authenticators: Authenticator[]; message?: string; title?: string; + notify_email?: string; + tags?: Tags; } export interface ShareLinkCreateRequest { @@ -2304,6 +2306,10 @@ export namespace Share { last_accessed_at?: string; authenticators: Authenticator[]; link: string; + message?: string; + title?: string; + notify_email?: string; + tags?: Tags; } export interface ShareLinkCreateResult { From 850457f15b2ca1f64c36fe383bbe9e9b288d82d9 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 6 Mar 2024 16:40:20 -0300 Subject: [PATCH 16/36] fixup! keep renaming from store to share --- examples/.examples-ci.yml | 2 ++ examples/store/README.md | 2 +- examples/store/folder_create_and_delete.mjs | 2 +- examples/store/item_life_cycle.mjs | 2 +- examples/store/put_split_upload.mjs | 2 +- examples/store/put_transfer_method_multipart.mjs | 2 +- examples/store/put_transfer_method_post_url.mjs | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/.examples-ci.yml b/examples/.examples-ci.yml index 2d28a00f4..b3e4ca4ee 100644 --- a/examples/.examples-ci.yml +++ b/examples/.examples-ci.yml @@ -14,6 +14,7 @@ examples-tests: - "intel" - "redact" - "vault" + - "share" rules: - changes: - examples/**/* @@ -39,6 +40,7 @@ examples-tests: - export PANGEA_URL_INTEL_TOKEN="${PANGEA_INTEGRATION_TOKEN_LVE_AWS}" - export PANGEA_USER_INTEL_TOKEN="${PANGEA_INTEGRATION_TOKEN_LVE_AWS}" - export PANGEA_VAULT_TOKEN="${PANGEA_INTEGRATION_TOKEN_LVE_AWS}" + - export PANGEA_SHARE_TOKEN="${PANGEA_INTEGRATION_TOKEN_LVE_AWS}" - pushd packages/pangea-node-sdk - tar -xf pangea-node-sdk-v*.tgz --strip-components 1 -C . - popd diff --git a/examples/store/README.md b/examples/store/README.md index 1ad1bab1a..7f9e4a521 100644 --- a/examples/store/README.md +++ b/examples/store/README.md @@ -2,7 +2,7 @@ ## Setup -Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_STORE_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Store service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. +Set up environment variables ([Instructions](https://pangea.cloud/docs/getting-started/integrate/#set-environment-variables)) `PANGEA_SHARE_TOKEN` and `PANGEA_DOMAIN` with your project token configured on Pangea User Console (token should have access to Store service [Instructions](https://pangea.cloud/docs/getting-started/configure-services/#configure-a-pangea-service)) and with your Pangea domain. ## Run example diff --git a/examples/store/folder_create_and_delete.mjs b/examples/store/folder_create_and_delete.mjs index 659f93552..e37e9d89e 100644 --- a/examples/store/folder_create_and_delete.mjs +++ b/examples/store/folder_create_and_delete.mjs @@ -3,7 +3,7 @@ import { PangeaConfig, StoreService } from "pangea-node-sdk"; // Load Pangea token and domain from environment variables -const token = process.env.PANGEA_STORE_TOKEN; +const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client diff --git a/examples/store/item_life_cycle.mjs b/examples/store/item_life_cycle.mjs index bcadbde20..213d68d6a 100644 --- a/examples/store/item_life_cycle.mjs +++ b/examples/store/item_life_cycle.mjs @@ -9,7 +9,7 @@ import { import * as fs from "fs"; // Load Pangea token and domain from environment variables -const token = process.env.PANGEA_STORE_TOKEN; +const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client diff --git a/examples/store/put_split_upload.mjs b/examples/store/put_split_upload.mjs index e304915f5..67cf84cbd 100644 --- a/examples/store/put_split_upload.mjs +++ b/examples/store/put_split_upload.mjs @@ -9,7 +9,7 @@ import { import * as fs from "fs"; // Load Pangea token and domain from environment variables -const token = process.env.PANGEA_STORE_TOKEN; +const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client diff --git a/examples/store/put_transfer_method_multipart.mjs b/examples/store/put_transfer_method_multipart.mjs index d4892adbc..4e49254f8 100644 --- a/examples/store/put_transfer_method_multipart.mjs +++ b/examples/store/put_transfer_method_multipart.mjs @@ -4,7 +4,7 @@ import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; import * as fs from "fs"; // Load Pangea token and domain from environment variables -const token = process.env.PANGEA_STORE_TOKEN; +const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client diff --git a/examples/store/put_transfer_method_post_url.mjs b/examples/store/put_transfer_method_post_url.mjs index 53c346140..92aa8cae8 100644 --- a/examples/store/put_transfer_method_post_url.mjs +++ b/examples/store/put_transfer_method_post_url.mjs @@ -4,7 +4,7 @@ import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; import * as fs from "fs"; // Load Pangea token and domain from environment variables -const token = process.env.PANGEA_STORE_TOKEN; +const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client From cb9c7752e7acd35aae2a5c124c8e6078f4597797 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 6 Mar 2024 16:41:03 -0300 Subject: [PATCH 17/36] fixup! rename share examples folder --- examples/{store => share}/README.md | 0 .../{store => share}/folder_create_and_delete.mjs | 0 examples/{store => share}/item_life_cycle.mjs | 0 examples/{store => share}/package.json | 0 examples/{store => share}/put_split_upload.mjs | 0 .../put_transfer_method_multipart.mjs | 0 .../put_transfer_method_post_url.mjs | 0 examples/{store => share}/testfile.pdf | Bin 8 files changed, 0 insertions(+), 0 deletions(-) rename examples/{store => share}/README.md (100%) rename examples/{store => share}/folder_create_and_delete.mjs (100%) rename examples/{store => share}/item_life_cycle.mjs (100%) rename examples/{store => share}/package.json (100%) rename examples/{store => share}/put_split_upload.mjs (100%) rename examples/{store => share}/put_transfer_method_multipart.mjs (100%) rename examples/{store => share}/put_transfer_method_post_url.mjs (100%) rename examples/{store => share}/testfile.pdf (100%) diff --git a/examples/store/README.md b/examples/share/README.md similarity index 100% rename from examples/store/README.md rename to examples/share/README.md diff --git a/examples/store/folder_create_and_delete.mjs b/examples/share/folder_create_and_delete.mjs similarity index 100% rename from examples/store/folder_create_and_delete.mjs rename to examples/share/folder_create_and_delete.mjs diff --git a/examples/store/item_life_cycle.mjs b/examples/share/item_life_cycle.mjs similarity index 100% rename from examples/store/item_life_cycle.mjs rename to examples/share/item_life_cycle.mjs diff --git a/examples/store/package.json b/examples/share/package.json similarity index 100% rename from examples/store/package.json rename to examples/share/package.json diff --git a/examples/store/put_split_upload.mjs b/examples/share/put_split_upload.mjs similarity index 100% rename from examples/store/put_split_upload.mjs rename to examples/share/put_split_upload.mjs diff --git a/examples/store/put_transfer_method_multipart.mjs b/examples/share/put_transfer_method_multipart.mjs similarity index 100% rename from examples/store/put_transfer_method_multipart.mjs rename to examples/share/put_transfer_method_multipart.mjs diff --git a/examples/store/put_transfer_method_post_url.mjs b/examples/share/put_transfer_method_post_url.mjs similarity index 100% rename from examples/store/put_transfer_method_post_url.mjs rename to examples/share/put_transfer_method_post_url.mjs diff --git a/examples/store/testfile.pdf b/examples/share/testfile.pdf similarity index 100% rename from examples/store/testfile.pdf rename to examples/share/testfile.pdf From dc42871913cf42c28fa7463f6197e3c5026aae0e Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 6 Mar 2024 17:00:27 -0300 Subject: [PATCH 18/36] fixup! remove unit_tests stage from gitlab ci. They already run in github. --- .gitlab-ci.yml | 1 - packages/pangea-node-sdk/.gitlab-ci.yml | 6 ------ 2 files changed, 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 450cd8eb1..54ba18021 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,7 +6,6 @@ default: stages: - build - lint - - unit_tests - integration_tests - publish - tag_release diff --git a/packages/pangea-node-sdk/.gitlab-ci.yml b/packages/pangea-node-sdk/.gitlab-ci.yml index d086085c5..3f7ce3f10 100644 --- a/packages/pangea-node-sdk/.gitlab-ci.yml +++ b/packages/pangea-node-sdk/.gitlab-ci.yml @@ -160,12 +160,6 @@ pangea-node-sdk-lint: script: - yarn lint -pangea-node-sdk-unit-tests: - extends: .pangea-node-sdk-base - stage: unit_tests - script: - - yarn test:unit --ci --coverage - pangea-node-sdk-publish: extends: .pangea-node-sdk-publish stage: publish From 3d867fb20838eda9ebf0de4a440bc68ead8c9f5b Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 6 Mar 2024 17:18:51 -0300 Subject: [PATCH 19/36] fixup! lock file --- examples/share/package.json | 6 +- examples/share/yarn.lock | 336 ++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 examples/share/yarn.lock diff --git a/examples/share/package.json b/examples/share/package.json index cf10c663d..f83cd85ac 100644 --- a/examples/share/package.json +++ b/examples/share/package.json @@ -1,7 +1,7 @@ { - "name": "store_examples", + "name": "share_examples", "version": "2.0.0", - "description": "Pangea Store text example", + "description": "Pangea Share example", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -9,6 +9,6 @@ "author": "Pangea", "license": "MIT", "dependencies": { - "pangea-node-sdk": "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz" + "pangea-node-sdk": "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.7.0.tgz" } } diff --git a/examples/share/yarn.lock b/examples/share/yarn.lock new file mode 100644 index 000000000..715db968c --- /dev/null +++ b/examples/share/yarn.lock @@ -0,0 +1,336 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@emnapi/core@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-0.45.0.tgz#e58dc1f70ab3af3f6708991ba56d988088daf204" + integrity sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw== + dependencies: + tslib "^2.4.0" + +"@emnapi/runtime@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-0.45.0.tgz#e754de04c683263f34fd0c7f32adfe718bbe4ddd" + integrity sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w== + dependencies: + tslib "^2.4.0" + +"@napi-rs/wasm-runtime@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.1.tgz#ec090e2f46bee2ed0c8486dd9d97ddca836ae30e" + integrity sha512-ATj9ua659JgrkICjJscaeZdmPr44cb/KFjNWuD0N6pux0SpzaM7+iOuuK11mAnQM2N9q0DT4REu6NkL8ZEhopw== + dependencies: + "@emnapi/core" "^0.45.0" + "@emnapi/runtime" "^0.45.0" + "@tybys/wasm-util" "^0.8.1" + +"@node-rs/crc32-android-arm-eabi@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-android-arm-eabi/-/crc32-android-arm-eabi-1.10.0.tgz#7beace1e2755bdd444d67233e8448afa8d184192" + integrity sha512-IRas7ylc8nB3988nnaT4PC5ZuaK3VOrLbTyg1Y/5ZHlxsYpqLpCb7VMf/oRrHxkSzSTlluD+inv3J8UE3i5Ojg== + +"@node-rs/crc32-android-arm64@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-android-arm64/-/crc32-android-arm64-1.10.0.tgz#8a926ad4ba8609c026b52d9b7dfa97e7591bfaa5" + integrity sha512-4vX1gB+rf3sYma/LLycmYsuFKolWdZX7tQOwLQ6PDwE7dAoN3mWAgS3RBw2G6PerGD9r90vSXWXPLJnF3OAhlw== + +"@node-rs/crc32-darwin-arm64@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-darwin-arm64/-/crc32-darwin-arm64-1.10.0.tgz#fba0b3cf8f17c6f5768604d6d84decb44f332396" + integrity sha512-nAAdxZqxFBxqhI3ZMEGi7QDwg44N4laYO4iGIGhjLvsUDqJlYeIlqZ39Lh2gOK3D2uF/TaT4b0bU5EPHWnKMOQ== + +"@node-rs/crc32-darwin-x64@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-darwin-x64/-/crc32-darwin-x64-1.10.0.tgz#3301fba789c59e75fcae1563c1bc635b57ee8325" + integrity sha512-0YhLJFDY7VAKlJ4+SdfZFY+u0X18tkuD3NCtPp1SYh1o9pWpFVBbTKWvdjjx/Ihqw0ozkfc3iewFJU7vlrDQJg== + +"@node-rs/crc32-freebsd-x64@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-freebsd-x64/-/crc32-freebsd-x64-1.10.0.tgz#f6d5065905a9179e1b3f289ff305e1d02b42bf6b" + integrity sha512-BE0IeHn59GzaebTM85Dpe+ErPV8E+WuXd/sNyLLS8jZUuNoOJwFUKotm8CUFG+MI40N0U9PzvZjQSwaeMsyMsQ== + +"@node-rs/crc32-linux-arm-gnueabihf@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm-gnueabihf/-/crc32-linux-arm-gnueabihf-1.10.0.tgz#42c12d5aebd398409b28e84623e645002d13aaac" + integrity sha512-R3mN3uSZaslJtXW3NXdropB9tHCnOgbrvq7MtmCRpHi2Ie3E46Ohi8cW0HgHjihptafTf8NWsoYzErm39BTY0Q== + +"@node-rs/crc32-linux-arm64-gnu@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm64-gnu/-/crc32-linux-arm64-gnu-1.10.0.tgz#4a1849b882737c73541cf7141557ea12269161ee" + integrity sha512-2zZ2RQLVhjCWRWiLvz/CoP5BFld/zE/uD2Z9Nk+Y5zmJ11CD1RC3lqKG1M3MgEiQq9CnWJxwyy5kM2q4jDeXkg== + +"@node-rs/crc32-linux-arm64-musl@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm64-musl/-/crc32-linux-arm64-musl-1.10.0.tgz#0847466e4bc812abf9de31e509eceec90d29c45f" + integrity sha512-WEIavGFHMAFe8NIKhbYnM6k2x7y6M/NQewXE8cqeV03Q8mLzCDBr34i/MzpW+M42NvEYgcM8c3Ayn2FijHb64Q== + +"@node-rs/crc32-linux-x64-gnu@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-x64-gnu/-/crc32-linux-x64-gnu-1.10.0.tgz#02c5bc402d783cce8d922240ebaba58c4ed07052" + integrity sha512-K/7aY/h8QngsLk0KsalQ3AlZ8ljXRisZgc20RcbB4UZkjl5AN6TeHQlVbx9U2MSBE5f6ViiZEr8c8CcID3W2Mg== + +"@node-rs/crc32-linux-x64-musl@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-x64-musl/-/crc32-linux-x64-musl-1.10.0.tgz#07d0a1914652a3cf058e7cde5bc85f75a4106bbf" + integrity sha512-GyCSm+Dp96qUvqrsxKgfd3TFrE8v5sRUYiMgNKK6G1m7nQb/VXvab9UoBSKeFw131odt3LlIuBAuhMnbb4za5w== + +"@node-rs/crc32-wasm32-wasi@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-wasm32-wasi/-/crc32-wasm32-wasi-1.10.0.tgz#6718e4b3a7f9e207b400abf3968e008ec804808c" + integrity sha512-C+2IK5HwNUz2aiMGiN0RTijb80X5V1jo/o8bsHqi8ukoRyO6HLMhVn+xptqY+RRSf4VUzzNR5eHqD+WLcLId0g== + dependencies: + "@napi-rs/wasm-runtime" "^0.1.1" + +"@node-rs/crc32-win32-arm64-msvc@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-arm64-msvc/-/crc32-win32-arm64-msvc-1.10.0.tgz#8641440a45511fdc04eac13338c9cf8336092c29" + integrity sha512-RaVo4edbEM3DyQkvXGKdPizUmr2A4NjLMk/1x9b/tz/k2rdd+QaPAauDwWAzs7SKoDBV9H4qc3hNFuKGjjRhjA== + +"@node-rs/crc32-win32-ia32-msvc@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-ia32-msvc/-/crc32-win32-ia32-msvc-1.10.0.tgz#236ffc75ced7a29e2b732fe1050cdaa17d3e4cca" + integrity sha512-5KqJFdzRXELpXcdNgahafjkc9MxZJfKDVkFPBMkQIjjkv8PQ49DVw15/7yuhAN0pyYccNaUil4vtVoo7WTIVgQ== + +"@node-rs/crc32-win32-x64-msvc@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-x64-msvc/-/crc32-win32-x64-msvc-1.10.0.tgz#9fe9a4f4020a330dc500421d7c8da805bab08fdd" + integrity sha512-6b99QpwNCQube1xleD+9IcF6foEWHYQYjuZrHAR5diuP/uqM7i+KCgMU9fbCFLs5zmssYHO3CQSZ8G+V0eC59g== + +"@node-rs/crc32@^1.7.2": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@node-rs/crc32/-/crc32-1.10.0.tgz#31d3562b1bc615bd784e8dc07f690482686bb91f" + integrity sha512-SFvU8PGZexRMRPUhi+4a9LW4oqFuK5CLEElysrKoRtNkJ+LcRFL+b3wfuzbcDq/ea0rS0nzRLFZwVsNVyWaGew== + optionalDependencies: + "@node-rs/crc32-android-arm-eabi" "1.10.0" + "@node-rs/crc32-android-arm64" "1.10.0" + "@node-rs/crc32-darwin-arm64" "1.10.0" + "@node-rs/crc32-darwin-x64" "1.10.0" + "@node-rs/crc32-freebsd-x64" "1.10.0" + "@node-rs/crc32-linux-arm-gnueabihf" "1.10.0" + "@node-rs/crc32-linux-arm64-gnu" "1.10.0" + "@node-rs/crc32-linux-arm64-musl" "1.10.0" + "@node-rs/crc32-linux-x64-gnu" "1.10.0" + "@node-rs/crc32-linux-x64-musl" "1.10.0" + "@node-rs/crc32-wasm32-wasi" "1.10.0" + "@node-rs/crc32-win32-arm64-msvc" "1.10.0" + "@node-rs/crc32-win32-ia32-msvc" "1.10.0" + "@node-rs/crc32-win32-x64-msvc" "1.10.0" + +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + +"@tybys/wasm-util@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.8.1.tgz#10360976b7f679497ea8526791006417ff304abb" + integrity sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q== + dependencies: + tslib "^2.4.0" + +"@types/http-cache-semantics@^4.0.2": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +got@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/got/-/got-13.0.0.tgz#a2402862cef27a5d0d1b07c0fb25d12b58175422" + integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +merkle-tools@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merkle-tools/-/merkle-tools-1.4.1.tgz#d08799886a6d51f5ee2bf0195f967b3cc3afd62c" + integrity sha512-QhO1/eDvAnyn0oXgRWlydVWYVMrVJwrdNICYvQXYhBU1Bjj1LoxsQxdAKJ5ttN3L6pkKhjcK6O4k927kgTMdqw== + dependencies: + js-sha3 "^0.8.0" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + +normalize-url@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.0.tgz#593dbd284f743e8dcf6a5ddf8fadff149c82701a" + integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw== + +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + +"pangea-node-sdk@file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz": + version "3.5.0" + resolved "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz#5c15df1deb8a29cd05733c30eb6b6dfa7aba959e" + dependencies: + "@node-rs/crc32" "^1.7.2" + crypto-js "^4.2.0" + form-data "^4.0.0" + got "^13.0.0" + merkle-tools "^1.4.1" + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + +tslib@^2.4.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== From 55294c4c9a087495ff33309b2ba91ef73d87830f Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Wed, 6 Mar 2024 17:23:56 -0300 Subject: [PATCH 20/36] fixup! disable share examples tests --- examples/.examples-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/.examples-ci.yml b/examples/.examples-ci.yml index b3e4ca4ee..64e4b9835 100644 --- a/examples/.examples-ci.yml +++ b/examples/.examples-ci.yml @@ -14,7 +14,7 @@ examples-tests: - "intel" - "redact" - "vault" - - "share" + # - "share" # FIXME: Uncomment once SDK is released rules: - changes: - examples/**/* From e87273fa4cc521c452a1a5c9c2dc9847094f0917 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Fri, 8 Mar 2024 13:16:26 -0300 Subject: [PATCH 21/36] fixup! Move FileUploader to its own file to be reused --- packages/pangea-node-sdk/src/file_uploader.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 packages/pangea-node-sdk/src/file_uploader.ts diff --git a/packages/pangea-node-sdk/src/file_uploader.ts b/packages/pangea-node-sdk/src/file_uploader.ts new file mode 100644 index 000000000..dcaadd3f1 --- /dev/null +++ b/packages/pangea-node-sdk/src/file_uploader.ts @@ -0,0 +1,36 @@ +import PangeaRequest from "@src/request.js"; +import { FileData, TransferMethod } from "@src/types.js"; +import PangeaConfig from "@src/config.js"; + +export class FileUploader { + protected serviceName: string = "FileUploader"; + protected request_: PangeaRequest | undefined = undefined; + + constructor() {} + + private get request(): PangeaRequest { + if (this.request_) { + return this.request_; + } + + this.request_ = new PangeaRequest(this.serviceName, "notatoken", new PangeaConfig()); + return this.request_; + } + + // TODO: Docs + public async uploadFile( + url: string, + fileData: FileData, + options: { + transfer_method?: TransferMethod; + } + ) { + if (!options.transfer_method || options.transfer_method === TransferMethod.PUT_URL) { + await this.request.putPresignedURL(url, fileData); + } else if (options.transfer_method === TransferMethod.POST_URL) { + await this.request.postPresignedURL(url, fileData); + } + } +} + +export default FileUploader; From c58f5b391e9826306622a9a317ae21a5c9c4185f Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Fri, 8 Mar 2024 13:22:15 -0300 Subject: [PATCH 22/36] fixup! Use FileUploader --- packages/pangea-node-sdk/src/index.ts | 2 +- .../pangea-node-sdk/src/services/share.ts | 32 ------------------- .../tests/integration/share.test.ts | 6 ++-- 3 files changed, 4 insertions(+), 36 deletions(-) diff --git a/packages/pangea-node-sdk/src/index.ts b/packages/pangea-node-sdk/src/index.ts index f5014c2b5..0540feeec 100755 --- a/packages/pangea-node-sdk/src/index.ts +++ b/packages/pangea-node-sdk/src/index.ts @@ -22,7 +22,7 @@ export { } from "./utils/utils.js"; export { FileScanUploader } from "./services/file_scan.js"; -export { ShareUploader } from "./services/share.js"; +export { FileUploader } from "./file_uploader.js"; export const PangeaConfig = _PangeaConfig; export const PangeaRequest = _PangeaRequest; diff --git a/packages/pangea-node-sdk/src/services/share.ts b/packages/pangea-node-sdk/src/services/share.ts index d1eac5704..19c24e2b3 100644 --- a/packages/pangea-node-sdk/src/services/share.ts +++ b/packages/pangea-node-sdk/src/services/share.ts @@ -2,7 +2,6 @@ import PangeaResponse from "@src/response.js"; import BaseService from "./base.js"; import PangeaConfig from "@src/config.js"; import { Share, FileData, TransferMethod, FileUploadParams } from "@src/types.js"; -import PangeaRequest from "@src/request.js"; import { PangeaErrors } from "@src/errors.js"; import { getFileUploadParams } from "@src/index.js"; @@ -326,35 +325,4 @@ class ShareService extends BaseService { } } -export class ShareUploader { - protected serviceName: string = "ShareFileUploader"; - protected request_: PangeaRequest | undefined = undefined; - - constructor() {} - - private get request(): PangeaRequest { - if (this.request_) { - return this.request_; - } - - this.request_ = new PangeaRequest(this.serviceName, "notatoken", new PangeaConfig()); - return this.request_; - } - - // TODO: Docs - public async uploadFile( - url: string, - fileData: FileData, - options: { - transfer_method?: TransferMethod; - } - ) { - if (!options.transfer_method || options.transfer_method === TransferMethod.PUT_URL) { - await this.request.putPresignedURL(url, fileData); - } else if (options.transfer_method === TransferMethod.POST_URL) { - await this.request.postPresignedURL(url, fileData); - } - } -} - export default ShareService; diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index 0e6f75799..66d053115 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -8,7 +8,7 @@ import { } from "../../src/utils/utils.js"; import { ShareService } from "../../src/index.js"; import { Share, TransferMethod } from "../../src/types.js"; -import { ShareUploader } from "@src/services/share.js"; +import { FileUploader } from "../../src/file_uploader.js"; const testEnvironment = TestEnvironment.DEVELOP; const token = getTestToken(testEnvironment); @@ -112,7 +112,7 @@ it("get url and put upload", async () => { const url = response.accepted_result?.put_url || ""; - const uploader = new ShareUploader(); + const uploader = new FileUploader(); await uploader.uploadFile( url, { @@ -162,7 +162,7 @@ it("get url and post upload", async () => { const url = response.accepted_result?.post_url || ""; const file_details = response.accepted_result?.post_form_data; - const uploader = new ShareUploader(); + const uploader = new FileUploader(); await uploader.uploadFile( url, { From fb3f2a45ce041a1e8dc69cae4cb161a4aa546343 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 15:22:49 -0300 Subject: [PATCH 23/36] fixup! Add support to zero bytes files --- .gitignore | 1 + packages/pangea-node-sdk/src/request.ts | 7 +- .../pangea-node-sdk/src/services/share.ts | 3 + .../pangea-node-sdk/src/utils/multipart.ts | 7 +- packages/pangea-node-sdk/src/utils/utils.ts | 15 +- .../tests/integration/share.test.ts | 135 +++++++++++++++--- .../tests/testdata/zerobytes.txt | 0 7 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 packages/pangea-node-sdk/tests/testdata/zerobytes.txt diff --git a/.gitignore b/.gitignore index bb88442b2..486fbb303 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules *.exe pangea-node-sdk-*.tgz *.gz +packages/pangea-node-sdk/download/ \ No newline at end of file diff --git a/packages/pangea-node-sdk/src/request.ts b/packages/pangea-node-sdk/src/request.ts index e4966139d..32a38932a 100644 --- a/packages/pangea-node-sdk/src/request.ts +++ b/packages/pangea-node-sdk/src/request.ts @@ -175,6 +175,10 @@ class PangeaRequest { fileData: FileData ): Promise { const response = await this.requestPresignedURL(endpoint, data); + if (response.success && response.gotResponse) { + return response.gotResponse; + } + if (!response.gotResponse || !response.accepted_result?.post_url) { throw new PangeaErrors.PangeaError("Failed to request post presigned URL"); } @@ -259,10 +263,9 @@ class PangeaRequest { } try { - await this.post(endpoint, data, { + return await this.post(endpoint, data, { pollResultSync: false, }); - throw new PangeaErrors.PangeaError("This call should return 202"); } catch (error) { if (!(error instanceof PangeaErrors.AcceptedRequestException)) { throw error; diff --git a/packages/pangea-node-sdk/src/services/share.ts b/packages/pangea-node-sdk/src/services/share.ts index 19c24e2b3..13988407b 100644 --- a/packages/pangea-node-sdk/src/services/share.ts +++ b/packages/pangea-node-sdk/src/services/share.ts @@ -4,6 +4,7 @@ import PangeaConfig from "@src/config.js"; import { Share, FileData, TransferMethod, FileUploadParams } from "@src/types.js"; import { PangeaErrors } from "@src/errors.js"; import { getFileUploadParams } from "@src/index.js"; +import { getFileSize } from "@src/utils/utils.js"; /** * ShareService class provides methods for interacting with the Share Service @@ -145,6 +146,8 @@ class ShareService extends BaseService { request.crc32c = fsData.crc32c; request.sha256 = fsData.sha256; request.size = fsData.size; + } else if (getFileSize(fileData.file) == 0) { + request.size = 0; } return this.post("v1beta/put", request, { diff --git a/packages/pangea-node-sdk/src/utils/multipart.ts b/packages/pangea-node-sdk/src/utils/multipart.ts index 3276e289c..4205f2c70 100644 --- a/packages/pangea-node-sdk/src/utils/multipart.ts +++ b/packages/pangea-node-sdk/src/utils/multipart.ts @@ -183,8 +183,11 @@ export function getHeaderField( const parts = header.split(field + "="); if (parts.length > 1 && parts[1]) { const valueParts = parts[1].split(";"); - if (valueParts[0]) { - return valueParts[0].trim().replace(/['"]+/g, ""); + if (valueParts[0] !== undefined) { + const value = valueParts[0].trim().replace(/['"]+/g, ""); + if (value.length > 0) { + return value; + } } } return defaultValue; diff --git a/packages/pangea-node-sdk/src/utils/utils.ts b/packages/pangea-node-sdk/src/utils/utils.ts index 3b1358bef..909358cd5 100644 --- a/packages/pangea-node-sdk/src/utils/utils.ts +++ b/packages/pangea-node-sdk/src/utils/utils.ts @@ -156,7 +156,20 @@ export function getFileUploadParams(file: string | Buffer): FileScan.ScanFilePar const crcValue = crc32c(data); return { sha256: sha256hex, - crc32c: crcValue.toString(16), + crc32c: crcValue.toString(16).padStart(8, "0"), size: size, }; } + +export function getFileSize(file: string | Buffer): number { + let data: Buffer; + if (typeof file === "string") { + data = fs.readFileSync(file); + } else if (Buffer.isBuffer(file)) { + data = file; + } else { + throw new PangeaErrors.PangeaError("Invalid file type"); + } + + return data.length; +} diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index 66d053115..4bbd44548 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -6,7 +6,7 @@ import { getTestDomain, getTestToken, } from "../../src/utils/utils.js"; -import { ShareService } from "../../src/index.js"; +import { ShareService, PangeaErrors } from "../../src/index.js"; import { Share, TransferMethod } from "../../src/types.js"; import { FileUploader } from "../../src/file_uploader.js"; @@ -24,7 +24,8 @@ const ADD_METADATA = { field3: "value3" }; const TAGS = ["tag1", "tag2"]; const ADD_TAGS = ["tag3"]; -const testfilePath = "./tests/testdata/testfile.pdf"; +const testFilePath = "./tests/testdata/testfile.pdf"; +const zeroBytesFilePath = "./tests/testdata/zerobytes.txt"; jest.setTimeout(60000); const delay = async (ms: number) => @@ -51,7 +52,7 @@ it("Folder create/delete", async () => { expect(respDelete.success).toBeTruthy(); expect(respDelete.result.count).toBe(1); } catch (e) { - console.log(e); + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); expect(false).toBeTruthy(); } }); @@ -65,17 +66,77 @@ it("Put file. Multipart transfer_method", async () => { transfer_method: TransferMethod.MULTIPART, }, { - file: testfilePath, + file: testFilePath, name: name, } ); expect(respPut.success).toBeTruthy(); + + let respGet = await client.getItem({ + id: respPut.result.object.id, + transfer_method: TransferMethod.MULTIPART, + }); + + expect(respGet.success).toBeTruthy(); + expect(respGet.result.dest_url).toBeUndefined(); + expect(respGet.attachedFiles.length).toBe(1); + expect(respGet.attachedFiles[0]).toBeDefined(); + respGet.attachedFiles[0]?.save("./download/"); + + respGet = await client.getItem({ + id: respPut.result.object.id, + transfer_method: TransferMethod.DEST_URL, + }); + + expect(respGet.success).toBeTruthy(); + expect(respGet.attachedFiles.length).toBe(0); + expect(respGet.result.dest_url).toBeDefined(); } catch (e) { - console.log(e); + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); expect(false).toBeTruthy(); } }); +it("Put zero bytes file. Multipart transfer_method", async () => { + try { + const name = TIME + "_file_zero_bytes_multipart"; + const respPut = await client.put( + { + name: name, + transfer_method: TransferMethod.MULTIPART, + }, + { + file: zeroBytesFilePath, + name: name, + } + ); + expect(respPut.success).toBeTruthy(); + + let respGet = await client.getItem({ + id: respPut.result.object.id, + transfer_method: TransferMethod.MULTIPART, + }); + + expect(respGet.success).toBeTruthy(); + expect(respGet.result.dest_url).toBeUndefined(); + expect(respGet.attachedFiles.length).toBe(1); + expect(respGet.attachedFiles[0]).toBeDefined(); + respGet.attachedFiles[0]?.save("./download/"); + + respGet = await client.getItem({ + id: respPut.result.object.id, + transfer_method: TransferMethod.DEST_URL, + }); + + expect(respGet.success).toBeTruthy(); + expect(respGet.attachedFiles.length).toBe(0); + expect(respGet.result.dest_url).toBeUndefined(); + } catch (e) { + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + throw e; + } +}); + it("Put file. post-url transfer_method", async () => { try { const name = TIME + "_file_post_url"; @@ -85,14 +146,54 @@ it("Put file. post-url transfer_method", async () => { transfer_method: TransferMethod.POST_URL, }, { - file: testfilePath, + file: testFilePath, name: name, } ); expect(respPut.success).toBeTruthy(); + + let respGet = await client.getItem({ + id: respPut.result.object.id, + transfer_method: TransferMethod.MULTIPART, + }); + + expect(respGet.success).toBeTruthy(); + expect(respGet.result.dest_url).toBeUndefined(); + expect(respGet.attachedFiles.length).toBe(1); + expect(respGet.attachedFiles[0]).toBeDefined(); + respGet.attachedFiles[0]?.save("./download/"); + + respGet = await client.getItem({ + id: respPut.result.object.id, + transfer_method: TransferMethod.DEST_URL, + }); + + expect(respGet.success).toBeTruthy(); + expect(respGet.attachedFiles.length).toBe(0); + expect(respGet.result.dest_url).toBeDefined(); } catch (e) { - console.log(e); - expect(false).toBeTruthy(); + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + throw e; + } +}); + +it("Put zero bytes file. post-url transfer_method", async () => { + try { + const name = TIME + "_file_zero_bytes_post_url"; + const respPut = await client.put( + { + name: name, + transfer_method: TransferMethod.POST_URL, + }, + { + file: zeroBytesFilePath, + name: name, + } + ); + expect(respPut.success).toBeTruthy(); + } catch (e) { + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + throw e; } }); @@ -106,7 +207,7 @@ it("get url and put upload", async () => { }; response = await client.requestUploadURL(request); } catch (e) { - console.log(e); + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); throw e; } @@ -116,7 +217,7 @@ it("get url and put upload", async () => { await uploader.uploadFile( url, { - file: testfilePath, + file: testFilePath, name: name, }, { @@ -143,7 +244,7 @@ it("get url and post upload", async () => { let response; const name = TIME + "_file_split_post_url"; try { - const params = getFileUploadParams(testfilePath); + const params = getFileUploadParams(testFilePath); const request: Share.PutRequest = { transfer_method: TransferMethod.POST_URL, @@ -155,7 +256,7 @@ it("get url and post upload", async () => { response = await client.requestUploadURL(request); } catch (e) { - console.log(e); + e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); throw e; } @@ -166,7 +267,7 @@ it("get url and post upload", async () => { await uploader.uploadFile( url, { - file: testfilePath, + file: testFilePath, name: name, file_details: file_details, }, @@ -206,7 +307,7 @@ it("Item life cycle", async () => { transfer_method: TransferMethod.MULTIPART, }, { - file: testfilePath, + file: testFilePath, name: path1, } ); @@ -230,7 +331,7 @@ it("Item life cycle", async () => { tags: TAGS, }, { - file: testfilePath, + file: testFilePath, name: path1, } ); @@ -279,7 +380,7 @@ it("Item life cycle", async () => { expect(respGetArchive1.result.dest_url).toBeUndefined(); expect(respGetArchive1.attachedFiles.length).toBe(1); respGetArchive1.attachedFiles.forEach((file) => { - file.save("./"); + file.save("./download/"); }); const respGetArchive2 = await client.getArchive({ @@ -294,7 +395,7 @@ it("Item life cycle", async () => { // Download file const url = respGetArchive2.result.dest_url ?? ""; let downloadedFile = await client.downloadFile(url); - downloadedFile.save("./"); + downloadedFile.save("./download/"); expect(downloadedFile.file.length).toBeGreaterThan(0); // Create share link diff --git a/packages/pangea-node-sdk/tests/testdata/zerobytes.txt b/packages/pangea-node-sdk/tests/testdata/zerobytes.txt new file mode 100644 index 000000000..e69de29bb From 089f463b1ba5446f15bbabea12a66bd460d0649d Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 15:39:46 -0300 Subject: [PATCH 24/36] fixup! run prettier --- packages/pangea-node-sdk/src/file_uploader.ts | 11 +++- .../pangea-node-sdk/src/services/share.ts | 37 +++++++++++--- .../tests/integration/share.test.ts | 50 ++++++++++++++----- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/packages/pangea-node-sdk/src/file_uploader.ts b/packages/pangea-node-sdk/src/file_uploader.ts index dcaadd3f1..8ed923618 100644 --- a/packages/pangea-node-sdk/src/file_uploader.ts +++ b/packages/pangea-node-sdk/src/file_uploader.ts @@ -13,7 +13,11 @@ export class FileUploader { return this.request_; } - this.request_ = new PangeaRequest(this.serviceName, "notatoken", new PangeaConfig()); + this.request_ = new PangeaRequest( + this.serviceName, + "notatoken", + new PangeaConfig() + ); return this.request_; } @@ -25,7 +29,10 @@ export class FileUploader { transfer_method?: TransferMethod; } ) { - if (!options.transfer_method || options.transfer_method === TransferMethod.PUT_URL) { + if ( + !options.transfer_method || + options.transfer_method === TransferMethod.PUT_URL + ) { await this.request.putPresignedURL(url, fileData); } else if (options.transfer_method === TransferMethod.POST_URL) { await this.request.postPresignedURL(url, fileData); diff --git a/packages/pangea-node-sdk/src/services/share.ts b/packages/pangea-node-sdk/src/services/share.ts index 13988407b..0cfb90ab7 100644 --- a/packages/pangea-node-sdk/src/services/share.ts +++ b/packages/pangea-node-sdk/src/services/share.ts @@ -1,7 +1,12 @@ import PangeaResponse from "@src/response.js"; import BaseService from "./base.js"; import PangeaConfig from "@src/config.js"; -import { Share, FileData, TransferMethod, FileUploadParams } from "@src/types.js"; +import { + Share, + FileData, + TransferMethod, + FileUploadParams, +} from "@src/types.js"; import { PangeaErrors } from "@src/errors.js"; import { getFileUploadParams } from "@src/index.js"; import { getFileSize } from "@src/utils/utils.js"; @@ -27,7 +32,9 @@ class ShareService extends BaseService { * const response = await client.delete(request); * ``` */ - delete(request: Share.DeleteRequest): Promise> { + delete( + request: Share.DeleteRequest + ): Promise> { return this.post("v1beta/delete", request); } @@ -90,7 +97,9 @@ class ShareService extends BaseService { * const response = await client.getArchive(request); * ``` */ - getArchive(request: Share.GetArchiveRequest): Promise> { + getArchive( + request: Share.GetArchiveRequest + ): Promise> { return this.post("v1beta/get_archive", request); } @@ -106,7 +115,9 @@ class ShareService extends BaseService { * const response = await client.list(request); * ``` */ - list(request: Share.ListRequest = {}): Promise> { + list( + request: Share.ListRequest = {} + ): Promise> { return this.post("v1beta/list", request); } @@ -138,10 +149,16 @@ class ShareService extends BaseService { * const response = await client.put(request, fileData); * ``` */ - put(request: Share.PutRequest, fileData: FileData): Promise> { + put( + request: Share.PutRequest, + fileData: FileData + ): Promise> { let fsData = {} as FileUploadParams; - if (!request.transfer_method || request.transfer_method === TransferMethod.POST_URL) { + if ( + !request.transfer_method || + request.transfer_method === TransferMethod.POST_URL + ) { fsData = getFileUploadParams(fileData.file); request.crc32c = fsData.crc32c; request.sha256 = fsData.sha256; @@ -185,7 +202,9 @@ class ShareService extends BaseService { * const response = await client.requestUploadURL(request); * ``` */ - requestUploadURL(request: Share.PutRequest): Promise> { + requestUploadURL( + request: Share.PutRequest + ): Promise> { if ( request.transfer_method === TransferMethod.POST_URL && (!request.size || !request.crc32c || !request.sha256) @@ -219,7 +238,9 @@ class ShareService extends BaseService { * const response = await client.update(request); * ``` */ - update(request: Share.UpdateRequest): Promise> { + update( + request: Share.UpdateRequest + ): Promise> { return this.post("v1beta/update", request); } diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index 4bbd44548..ef7581754 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -13,7 +13,10 @@ import { FileUploader } from "../../src/file_uploader.js"; const testEnvironment = TestEnvironment.DEVELOP; const token = getTestToken(testEnvironment); const testHost = getTestDomain(testEnvironment); -const config = new PangeaConfig({ domain: testHost, customUserAgent: "sdk-test" }); +const config = new PangeaConfig({ + domain: testHost, + customUserAgent: "sdk-test", +}); const client = new ShareService(token, config); const TIME = Math.round(Date.now() / 1000); @@ -52,7 +55,9 @@ it("Folder create/delete", async () => { expect(respDelete.success).toBeTruthy(); expect(respDelete.result.count).toBe(1); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); expect(false).toBeTruthy(); } }); @@ -92,7 +97,9 @@ it("Put file. Multipart transfer_method", async () => { expect(respGet.attachedFiles.length).toBe(0); expect(respGet.result.dest_url).toBeDefined(); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); expect(false).toBeTruthy(); } }); @@ -132,7 +139,9 @@ it("Put zero bytes file. Multipart transfer_method", async () => { expect(respGet.attachedFiles.length).toBe(0); expect(respGet.result.dest_url).toBeUndefined(); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); throw e; } }); @@ -172,7 +181,9 @@ it("Put file. post-url transfer_method", async () => { expect(respGet.attachedFiles.length).toBe(0); expect(respGet.result.dest_url).toBeDefined(); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); throw e; } }); @@ -192,7 +203,9 @@ it("Put zero bytes file. post-url transfer_method", async () => { ); expect(respPut.success).toBeTruthy(); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); throw e; } }); @@ -207,7 +220,9 @@ it("get url and put upload", async () => { }; response = await client.requestUploadURL(request); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); throw e; } @@ -256,7 +271,9 @@ it("get url and post upload", async () => { response = await client.requestUploadURL(request); } catch (e) { - e instanceof PangeaErrors.APIError ? console.log(e.toString()) : console.log(e); + e instanceof PangeaErrors.APIError + ? console.log(e.toString()) + : console.log(e); throw e; } @@ -400,7 +417,10 @@ it("Item life cycle", async () => { // Create share link const authenticators: Share.Authenticator[] = [ - { auth_type: Share.AuthenticatorType.PASSWORD, auth_context: "somepassword" }, + { + auth_type: Share.AuthenticatorType.PASSWORD, + auth_context: "somepassword", + }, ]; const linkList: Share.ShareLinkCreateItem[] = [ { @@ -423,7 +443,9 @@ it("Item life cycle", async () => { expect(link?.access_count).toBe(0); expect(link?.max_access_count).toBe(3); expect(link?.authenticators.length).toBe(1); - expect(link?.authenticators[0]?.auth_type).toBe(Share.AuthenticatorType.PASSWORD); + expect(link?.authenticators[0]?.auth_type).toBe( + Share.AuthenticatorType.PASSWORD + ); expect(link?.link).toBeDefined(); expect(link?.id).toBeDefined(); expect(link?.targets.length).toBe(1); @@ -451,8 +473,12 @@ it("Item life cycle", async () => { expect(respGetLink.result.share_link_object.link).toBe(link?.link); expect(respGetLink.result.share_link_object.access_count).toBe(0); expect(respGetLink.result.share_link_object.max_access_count).toBe(3); - expect(respGetLink.result.share_link_object.created_at).toBe(link?.created_at); - expect(respGetLink.result.share_link_object.expires_at).toBe(link?.expires_at); + expect(respGetLink.result.share_link_object.created_at).toBe( + link?.created_at + ); + expect(respGetLink.result.share_link_object.expires_at).toBe( + link?.expires_at + ); // List share link const respListLink = await client.shareLinkList(); From 70d05518842756904de2d2b9cc267e66eac4791a Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 15:49:30 -0300 Subject: [PATCH 25/36] fixup! update share examples package.json --- examples/share/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/share/package.json b/examples/share/package.json index f83cd85ac..309a74fe7 100644 --- a/examples/share/package.json +++ b/examples/share/package.json @@ -9,6 +9,6 @@ "author": "Pangea", "license": "MIT", "dependencies": { - "pangea-node-sdk": "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.7.0.tgz" + "pangea-node-sdk": "file:../../packages/pangea-node-sdk" } } From 3c4ad13fba11ab0ccdbf4c88cc3c493434deb111 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 15:49:57 -0300 Subject: [PATCH 26/36] fixup! yarn.lock --- examples/share/yarn.lock | 179 ++++++++++++--------------------------- 1 file changed, 56 insertions(+), 123 deletions(-) diff --git a/examples/share/yarn.lock b/examples/share/yarn.lock index 715db968c..f3065a1bc 100644 --- a/examples/share/yarn.lock +++ b/examples/share/yarn.lock @@ -2,126 +2,67 @@ # yarn lockfile v1 -"@emnapi/core@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-0.45.0.tgz#e58dc1f70ab3af3f6708991ba56d988088daf204" - integrity sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw== +"@aws-crypto/crc32c@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" + integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== dependencies: - tslib "^2.4.0" - -"@emnapi/runtime@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-0.45.0.tgz#e754de04c683263f34fd0c7f32adfe718bbe4ddd" - integrity sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w== - dependencies: - tslib "^2.4.0" - -"@napi-rs/wasm-runtime@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.1.tgz#ec090e2f46bee2ed0c8486dd9d97ddca836ae30e" - integrity sha512-ATj9ua659JgrkICjJscaeZdmPr44cb/KFjNWuD0N6pux0SpzaM7+iOuuK11mAnQM2N9q0DT4REu6NkL8ZEhopw== + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== dependencies: - "@emnapi/core" "^0.45.0" - "@emnapi/runtime" "^0.45.0" - "@tybys/wasm-util" "^0.8.1" - -"@node-rs/crc32-android-arm-eabi@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-android-arm-eabi/-/crc32-android-arm-eabi-1.10.0.tgz#7beace1e2755bdd444d67233e8448afa8d184192" - integrity sha512-IRas7ylc8nB3988nnaT4PC5ZuaK3VOrLbTyg1Y/5ZHlxsYpqLpCb7VMf/oRrHxkSzSTlluD+inv3J8UE3i5Ojg== - -"@node-rs/crc32-android-arm64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-android-arm64/-/crc32-android-arm64-1.10.0.tgz#8a926ad4ba8609c026b52d9b7dfa97e7591bfaa5" - integrity sha512-4vX1gB+rf3sYma/LLycmYsuFKolWdZX7tQOwLQ6PDwE7dAoN3mWAgS3RBw2G6PerGD9r90vSXWXPLJnF3OAhlw== - -"@node-rs/crc32-darwin-arm64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-darwin-arm64/-/crc32-darwin-arm64-1.10.0.tgz#fba0b3cf8f17c6f5768604d6d84decb44f332396" - integrity sha512-nAAdxZqxFBxqhI3ZMEGi7QDwg44N4laYO4iGIGhjLvsUDqJlYeIlqZ39Lh2gOK3D2uF/TaT4b0bU5EPHWnKMOQ== - -"@node-rs/crc32-darwin-x64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-darwin-x64/-/crc32-darwin-x64-1.10.0.tgz#3301fba789c59e75fcae1563c1bc635b57ee8325" - integrity sha512-0YhLJFDY7VAKlJ4+SdfZFY+u0X18tkuD3NCtPp1SYh1o9pWpFVBbTKWvdjjx/Ihqw0ozkfc3iewFJU7vlrDQJg== - -"@node-rs/crc32-freebsd-x64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-freebsd-x64/-/crc32-freebsd-x64-1.10.0.tgz#f6d5065905a9179e1b3f289ff305e1d02b42bf6b" - integrity sha512-BE0IeHn59GzaebTM85Dpe+ErPV8E+WuXd/sNyLLS8jZUuNoOJwFUKotm8CUFG+MI40N0U9PzvZjQSwaeMsyMsQ== - -"@node-rs/crc32-linux-arm-gnueabihf@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm-gnueabihf/-/crc32-linux-arm-gnueabihf-1.10.0.tgz#42c12d5aebd398409b28e84623e645002d13aaac" - integrity sha512-R3mN3uSZaslJtXW3NXdropB9tHCnOgbrvq7MtmCRpHi2Ie3E46Ohi8cW0HgHjihptafTf8NWsoYzErm39BTY0Q== - -"@node-rs/crc32-linux-arm64-gnu@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm64-gnu/-/crc32-linux-arm64-gnu-1.10.0.tgz#4a1849b882737c73541cf7141557ea12269161ee" - integrity sha512-2zZ2RQLVhjCWRWiLvz/CoP5BFld/zE/uD2Z9Nk+Y5zmJ11CD1RC3lqKG1M3MgEiQq9CnWJxwyy5kM2q4jDeXkg== - -"@node-rs/crc32-linux-arm64-musl@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm64-musl/-/crc32-linux-arm64-musl-1.10.0.tgz#0847466e4bc812abf9de31e509eceec90d29c45f" - integrity sha512-WEIavGFHMAFe8NIKhbYnM6k2x7y6M/NQewXE8cqeV03Q8mLzCDBr34i/MzpW+M42NvEYgcM8c3Ayn2FijHb64Q== - -"@node-rs/crc32-linux-x64-gnu@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-x64-gnu/-/crc32-linux-x64-gnu-1.10.0.tgz#02c5bc402d783cce8d922240ebaba58c4ed07052" - integrity sha512-K/7aY/h8QngsLk0KsalQ3AlZ8ljXRisZgc20RcbB4UZkjl5AN6TeHQlVbx9U2MSBE5f6ViiZEr8c8CcID3W2Mg== - -"@node-rs/crc32-linux-x64-musl@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-x64-musl/-/crc32-linux-x64-musl-1.10.0.tgz#07d0a1914652a3cf058e7cde5bc85f75a4106bbf" - integrity sha512-GyCSm+Dp96qUvqrsxKgfd3TFrE8v5sRUYiMgNKK6G1m7nQb/VXvab9UoBSKeFw131odt3LlIuBAuhMnbb4za5w== - -"@node-rs/crc32-wasm32-wasi@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-wasm32-wasi/-/crc32-wasm32-wasi-1.10.0.tgz#6718e4b3a7f9e207b400abf3968e008ec804808c" - integrity sha512-C+2IK5HwNUz2aiMGiN0RTijb80X5V1jo/o8bsHqi8ukoRyO6HLMhVn+xptqY+RRSf4VUzzNR5eHqD+WLcLId0g== + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/types@^3.222.0": + version "3.533.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.533.0.tgz#4c4ade8f41f153295c69f1dea812dcd6154613e3" + integrity sha512-mFb0701oLRcJ7Y2unlrszzk9rr2P6nt2A4Bdz4K5WOsY4f4hsdbcYkrzA1NPmIUTEttU9JT0YG+8z0XxLEX4Aw== dependencies: - "@napi-rs/wasm-runtime" "^0.1.1" - -"@node-rs/crc32-win32-arm64-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-arm64-msvc/-/crc32-win32-arm64-msvc-1.10.0.tgz#8641440a45511fdc04eac13338c9cf8336092c29" - integrity sha512-RaVo4edbEM3DyQkvXGKdPizUmr2A4NjLMk/1x9b/tz/k2rdd+QaPAauDwWAzs7SKoDBV9H4qc3hNFuKGjjRhjA== - -"@node-rs/crc32-win32-ia32-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-ia32-msvc/-/crc32-win32-ia32-msvc-1.10.0.tgz#236ffc75ced7a29e2b732fe1050cdaa17d3e4cca" - integrity sha512-5KqJFdzRXELpXcdNgahafjkc9MxZJfKDVkFPBMkQIjjkv8PQ49DVw15/7yuhAN0pyYccNaUil4vtVoo7WTIVgQ== - -"@node-rs/crc32-win32-x64-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-x64-msvc/-/crc32-win32-x64-msvc-1.10.0.tgz#9fe9a4f4020a330dc500421d7c8da805bab08fdd" - integrity sha512-6b99QpwNCQube1xleD+9IcF6foEWHYQYjuZrHAR5diuP/uqM7i+KCgMU9fbCFLs5zmssYHO3CQSZ8G+V0eC59g== - -"@node-rs/crc32@^1.7.2": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@node-rs/crc32/-/crc32-1.10.0.tgz#31d3562b1bc615bd784e8dc07f690482686bb91f" - integrity sha512-SFvU8PGZexRMRPUhi+4a9LW4oqFuK5CLEElysrKoRtNkJ+LcRFL+b3wfuzbcDq/ea0rS0nzRLFZwVsNVyWaGew== - optionalDependencies: - "@node-rs/crc32-android-arm-eabi" "1.10.0" - "@node-rs/crc32-android-arm64" "1.10.0" - "@node-rs/crc32-darwin-arm64" "1.10.0" - "@node-rs/crc32-darwin-x64" "1.10.0" - "@node-rs/crc32-freebsd-x64" "1.10.0" - "@node-rs/crc32-linux-arm-gnueabihf" "1.10.0" - "@node-rs/crc32-linux-arm64-gnu" "1.10.0" - "@node-rs/crc32-linux-arm64-musl" "1.10.0" - "@node-rs/crc32-linux-x64-gnu" "1.10.0" - "@node-rs/crc32-linux-x64-musl" "1.10.0" - "@node-rs/crc32-wasm32-wasi" "1.10.0" - "@node-rs/crc32-win32-arm64-msvc" "1.10.0" - "@node-rs/crc32-win32-ia32-msvc" "1.10.0" - "@node-rs/crc32-win32-x64-msvc" "1.10.0" + "@smithy/types" "^2.11.0" + tslib "^2.5.0" "@sindresorhus/is@^5.2.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== +"@smithy/is-array-buffer@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz#07b4c77ae67ed58a84400c76edd482271f9f957b" + integrity sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ== + dependencies: + tslib "^2.5.0" + +"@smithy/types@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.11.0.tgz#d40c27302151be243d3a7319a154b7d7d5775021" + integrity sha512-AR0SXO7FuAskfNhyGfSTThpLRntDI5bOrU0xrpVYU0rZyjl3LBXInZFMTP/NNSd7IS6Ksdtar0QvnrPRIhVrLQ== + dependencies: + tslib "^2.5.0" + +"@smithy/util-buffer-from@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz#f9346bf8b23c5ba6f6bdb61dd9db779441ba8d08" + integrity sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg== + dependencies: + "@smithy/is-array-buffer" "^2.1.1" + tslib "^2.5.0" + +"@smithy/util-utf8@^2.0.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.2.0.tgz#e352a81adc0491fbdc0086a00950d7e8333e211f" + integrity sha512-hBsKr5BqrDrKS8qy+YcV7/htmMGxriA1PREOf/8AGBhHIZnfilVv1Waf1OyKhSbFW15U/8+gcMUQ9/Kk5qwpHQ== + dependencies: + "@smithy/util-buffer-from" "^2.1.1" + tslib "^2.5.0" + "@szmarczak/http-timer@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" @@ -129,13 +70,6 @@ dependencies: defer-to-connect "^2.0.1" -"@tybys/wasm-util@^0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.8.1.tgz#10360976b7f679497ea8526791006417ff304abb" - integrity sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q== - dependencies: - tslib "^2.4.0" - "@types/http-cache-semantics@^4.0.2": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" @@ -303,11 +237,10 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -"pangea-node-sdk@file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz": - version "3.5.0" - resolved "file:../../packages/pangea-node-sdk/pangea-node-sdk-3.5.0.tgz#5c15df1deb8a29cd05733c30eb6b6dfa7aba959e" +"pangea-node-sdk@file:../../packages/pangea-node-sdk": + version "3.7.0" dependencies: - "@node-rs/crc32" "^1.7.2" + "@aws-crypto/crc32c" "^5.2.0" crypto-js "^4.2.0" form-data "^4.0.0" got "^13.0.0" @@ -330,7 +263,7 @@ responselike@^3.0.0: dependencies: lowercase-keys "^3.0.0" -tslib@^2.4.0: +tslib@^2.5.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== From 900897f99cb80000a1f760adc5fbc566b741a612 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 15:51:52 -0300 Subject: [PATCH 27/36] fixup! enable share tests in pipeline --- examples/.examples-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/.examples-ci.yml b/examples/.examples-ci.yml index 64e4b9835..b3e4ca4ee 100644 --- a/examples/.examples-ci.yml +++ b/examples/.examples-ci.yml @@ -14,7 +14,7 @@ examples-tests: - "intel" - "redact" - "vault" - # - "share" # FIXME: Uncomment once SDK is released + - "share" rules: - changes: - examples/**/* From 698a8df11fb58dde52a67de5b6e028d1a451f0b4 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 16:56:00 -0300 Subject: [PATCH 28/36] fixup! use ShareService instead of StoreService --- examples/share/folder_create_and_delete.mjs | 4 ++-- examples/share/item_life_cycle.mjs | 4 ++-- examples/share/put_split_upload.mjs | 4 ++-- examples/share/put_transfer_method_multipart.mjs | 4 ++-- examples/share/put_transfer_method_post_url.mjs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/share/folder_create_and_delete.mjs b/examples/share/folder_create_and_delete.mjs index e37e9d89e..a93ab4642 100644 --- a/examples/share/folder_create_and_delete.mjs +++ b/examples/share/folder_create_and_delete.mjs @@ -1,13 +1,13 @@ /* eslint-disable no-console */ -import { PangeaConfig, StoreService } from "pangea-node-sdk"; +import { PangeaConfig, ShareService } from "pangea-node-sdk"; // Load Pangea token and domain from environment variables const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client -const client = new StoreService(token, config); +const client = new ShareService(token, config); // Create unique folder path const time = Math.round(Date.now() / 1000); diff --git a/examples/share/item_life_cycle.mjs b/examples/share/item_life_cycle.mjs index 213d68d6a..96bc50cd0 100644 --- a/examples/share/item_life_cycle.mjs +++ b/examples/share/item_life_cycle.mjs @@ -2,7 +2,7 @@ import { PangeaConfig, - StoreService, + ShareService, TransferMethod, Store, } from "pangea-node-sdk"; @@ -13,7 +13,7 @@ const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client -const client = new StoreService(token, config); +const client = new ShareService(token, config); // Create unique folder path const time = Math.round(Date.now() / 1000); diff --git a/examples/share/put_split_upload.mjs b/examples/share/put_split_upload.mjs index 67cf84cbd..d3688f0a5 100644 --- a/examples/share/put_split_upload.mjs +++ b/examples/share/put_split_upload.mjs @@ -2,7 +2,7 @@ import { PangeaConfig, - StoreService, + ShareService, TransferMethod, StoreUploader, } from "pangea-node-sdk"; @@ -13,7 +13,7 @@ const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client -const client = new StoreService(token, config); +const client = new ShareService(token, config); // Create unique folder path const time = Math.round(Date.now() / 1000); diff --git a/examples/share/put_transfer_method_multipart.mjs b/examples/share/put_transfer_method_multipart.mjs index 4e49254f8..c0cbd364b 100644 --- a/examples/share/put_transfer_method_multipart.mjs +++ b/examples/share/put_transfer_method_multipart.mjs @@ -1,6 +1,6 @@ /* eslint-disable no-console */ -import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; +import { PangeaConfig, ShareService, TransferMethod } from "pangea-node-sdk"; import * as fs from "fs"; // Load Pangea token and domain from environment variables @@ -8,7 +8,7 @@ const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client -const client = new StoreService(token, config); +const client = new ShareService(token, config); // Create unique folder path const time = Math.round(Date.now() / 1000); diff --git a/examples/share/put_transfer_method_post_url.mjs b/examples/share/put_transfer_method_post_url.mjs index 92aa8cae8..bc2cbbd8e 100644 --- a/examples/share/put_transfer_method_post_url.mjs +++ b/examples/share/put_transfer_method_post_url.mjs @@ -1,6 +1,6 @@ /* eslint-disable no-console */ -import { PangeaConfig, StoreService, TransferMethod } from "pangea-node-sdk"; +import { PangeaConfig, ShareService, TransferMethod } from "pangea-node-sdk"; import * as fs from "fs"; // Load Pangea token and domain from environment variables @@ -8,7 +8,7 @@ const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); // Create Store client -const client = new StoreService(token, config); +const client = new ShareService(token, config); // Create unique folder path const time = Math.round(Date.now() / 1000); From 4b665b9181c67bf8c1243ea844f23f2de018127f Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 17:24:43 -0300 Subject: [PATCH 29/36] fixup! Update to use FileUploader --- examples/share/put_split_upload.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/share/put_split_upload.mjs b/examples/share/put_split_upload.mjs index d3688f0a5..7df702353 100644 --- a/examples/share/put_split_upload.mjs +++ b/examples/share/put_split_upload.mjs @@ -4,7 +4,7 @@ import { PangeaConfig, ShareService, TransferMethod, - StoreUploader, + FileUploader, } from "pangea-node-sdk"; import * as fs from "fs"; @@ -45,8 +45,8 @@ const delay = async (ms) => const url = response.accepted_result?.put_url || ""; console.log(`Got URL: ${url}`); - // Create StoreUploader client - const uploader = new StoreUploader(); + // Create FileUploader client + const uploader = new FileUploader(); // Read file content as buffer const data = fs.readFileSync(filepath); From 00d5b14f4217dd81393c09b0fac07da83839ee87 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 17:31:02 -0300 Subject: [PATCH 30/36] fixup! enable Share tests in pipeline --- packages/pangea-node-sdk/.gitlab-ci.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/pangea-node-sdk/.gitlab-ci.yml b/packages/pangea-node-sdk/.gitlab-ci.yml index 3f7ce3f10..f39a22d54 100644 --- a/packages/pangea-node-sdk/.gitlab-ci.yml +++ b/packages/pangea-node-sdk/.gitlab-ci.yml @@ -30,6 +30,7 @@ pangea-node-sdk-integration-tests: SERVICE_USER_INTEL_ENV: LVE SERVICE_REDACT_ENV: LVE SERVICE_VAULT_ENV: LVE + SERVICE_SHARE_ENV: LVE NODE_OPTIONS: "--experimental-vm-modules --openssl-legacy-provider" before_script: - echo $ENV @@ -83,7 +84,7 @@ pangea-node-sdk-integration-tests: - CLOUD: [AWS, GCP] ENV: ${SERVICE_FILE_INTEL_ENV} TEST: intel.file - - CLOUD: [AWS] + - CLOUD: [AWS, GCP] ENV: ${SERVICE_FILE_SCAN_ENV} TEST: file_scan - CLOUD: [AWS, GCP] @@ -92,6 +93,9 @@ pangea-node-sdk-integration-tests: - CLOUD: [AWS, GCP] ENV: ${SERVICE_VAULT_ENV} TEST: vault + - CLOUD: [AWS, GCP] + ENV: ${SERVICE_SHARE_ENV} + TEST: share rules: - if: '$CI_COMMIT_BRANCH || $CI_PIPELINE_SOURCE == "push"' changes: @@ -100,15 +104,6 @@ pangea-node-sdk-integration-tests: script: - npx jest --testPathPattern=tests/integration/${TEST}.test.ts -pangea-node-sdk-integration-tests-may-fail: - extends: pangea-node-sdk-integration-tests - parallel: - matrix: - - CLOUD: [GCP] - ENV: ${SERVICE_FILE_SCAN_ENV} - TEST: file_scan - allow_failure: true - .pangea-node-sdk-publish: before_script: - cd packages/pangea-node-sdk From 29fce8c7b8c5455f4b51a93b0e22bf9e302209f2 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 17:45:21 -0300 Subject: [PATCH 31/36] fixup! remove Store references --- examples/share/folder_create_and_delete.mjs | 2 +- examples/share/item_life_cycle.mjs | 12 ++++++------ examples/share/put_split_upload.mjs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/share/folder_create_and_delete.mjs b/examples/share/folder_create_and_delete.mjs index a93ab4642..1e9ea821f 100644 --- a/examples/share/folder_create_and_delete.mjs +++ b/examples/share/folder_create_and_delete.mjs @@ -6,7 +6,7 @@ import { PangeaConfig, ShareService } from "pangea-node-sdk"; const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); -// Create Store client +// Create Share client const client = new ShareService(token, config); // Create unique folder path diff --git a/examples/share/item_life_cycle.mjs b/examples/share/item_life_cycle.mjs index 96bc50cd0..8caec0eab 100644 --- a/examples/share/item_life_cycle.mjs +++ b/examples/share/item_life_cycle.mjs @@ -4,7 +4,7 @@ import { PangeaConfig, ShareService, TransferMethod, - Store, + Share, } from "pangea-node-sdk"; import * as fs from "fs"; @@ -12,7 +12,7 @@ import * as fs from "fs"; const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); -// Create Store client +// Create Share client const client = new ShareService(token, config); // Create unique folder path @@ -109,7 +109,7 @@ const filepath = "./testfile.pdf"; // Get archive const respGetArchive1 = await client.getArchive({ ids: [folderID], - format: Store.ArchiveFormat.ZIP, + format: Share.ArchiveFormat.ZIP, transfer_method: TransferMethod.MULTIPART, }); @@ -127,7 +127,7 @@ const filepath = "./testfile.pdf"; console.log("\nGetting archive with dest-url transfer method..."); const respGetArchive2 = await client.getArchive({ ids: [folderID], - format: Store.ArchiveFormat.TAR, + format: Share.ArchiveFormat.TAR, transfer_method: TransferMethod.DEST_URL, }); @@ -147,7 +147,7 @@ const filepath = "./testfile.pdf"; // Create authenticators list to access share link const authenticators = [ { - auth_type: Store.AuthenticatorType.PASSWORD, + auth_type: Share.AuthenticatorType.PASSWORD, auth_context: "somepassword", }, ]; @@ -156,7 +156,7 @@ const filepath = "./testfile.pdf"; const linkList = [ { targets: [folderID], - link_type: Store.LinkType.EDITOR, + link_type: Share.LinkType.EDITOR, max_access_count: 3, authenticators: authenticators, }, diff --git a/examples/share/put_split_upload.mjs b/examples/share/put_split_upload.mjs index 7df702353..2c45a801e 100644 --- a/examples/share/put_split_upload.mjs +++ b/examples/share/put_split_upload.mjs @@ -12,7 +12,7 @@ import * as fs from "fs"; const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); -// Create Store client +// Create Share client const client = new ShareService(token, config); // Create unique folder path From ba48c153749404c95764f7b94aafd842bdfd955d Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 17:46:13 -0300 Subject: [PATCH 32/36] fixup! examples updates --- examples/share/put_transfer_method_multipart.mjs | 2 +- examples/share/put_transfer_method_post_url.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/share/put_transfer_method_multipart.mjs b/examples/share/put_transfer_method_multipart.mjs index c0cbd364b..77100bc14 100644 --- a/examples/share/put_transfer_method_multipart.mjs +++ b/examples/share/put_transfer_method_multipart.mjs @@ -7,7 +7,7 @@ import * as fs from "fs"; const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); -// Create Store client +// Create Share client const client = new ShareService(token, config); // Create unique folder path diff --git a/examples/share/put_transfer_method_post_url.mjs b/examples/share/put_transfer_method_post_url.mjs index bc2cbbd8e..ee1f58462 100644 --- a/examples/share/put_transfer_method_post_url.mjs +++ b/examples/share/put_transfer_method_post_url.mjs @@ -7,7 +7,7 @@ import * as fs from "fs"; const token = process.env.PANGEA_SHARE_TOKEN; const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN }); -// Create Store client +// Create Share client const client = new ShareService(token, config); // Create unique folder path @@ -24,7 +24,7 @@ const filepath = "./testfile.pdf"; const data = fs.readFileSync(filepath); // Send Put request setting transfer_method to post-url - // SDK will request an upload url, post the file to that url and then poll the upload result to Store service + // SDK will request an upload url, post the file to that url and then poll the upload result to Share service const respPut = await client.put( { name: name, From 5ce18551df5d10ac88374d50e0e0630df4e00ec9 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Thu, 14 Mar 2024 17:49:13 -0300 Subject: [PATCH 33/36] fixup! fix share tests --- packages/pangea-node-sdk/tests/integration/share.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index ef7581754..df492a9d6 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -9,10 +9,11 @@ import { import { ShareService, PangeaErrors } from "../../src/index.js"; import { Share, TransferMethod } from "../../src/types.js"; import { FileUploader } from "../../src/file_uploader.js"; +import { loadTestEnvironment } from "./utils.js"; -const testEnvironment = TestEnvironment.DEVELOP; -const token = getTestToken(testEnvironment); -const testHost = getTestDomain(testEnvironment); +const environment = loadTestEnvironment("redact", TestEnvironment.LIVE); +const token = getTestToken(environment); +const testHost = getTestDomain(environment); const config = new PangeaConfig({ domain: testHost, customUserAgent: "sdk-test", From 68b33ec6e255b3fc6e444224e1afea79b3be7e49 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Mon, 18 Mar 2024 09:12:00 -0300 Subject: [PATCH 34/36] fixup! typo --- packages/pangea-node-sdk/tests/integration/share.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index df492a9d6..f4949f778 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -11,7 +11,7 @@ import { Share, TransferMethod } from "../../src/types.js"; import { FileUploader } from "../../src/file_uploader.js"; import { loadTestEnvironment } from "./utils.js"; -const environment = loadTestEnvironment("redact", TestEnvironment.LIVE); +const environment = loadTestEnvironment("share", TestEnvironment.LIVE); const token = getTestToken(environment); const testHost = getTestDomain(environment); const config = new PangeaConfig({ From bc77ad863fd28a086fc0191f48387a3580e85d16 Mon Sep 17 00:00:00 2001 From: "H. Andres Tournour" Date: Tue, 19 Mar 2024 09:49:25 -0300 Subject: [PATCH 35/36] fixup! increase timeout in share tests --- packages/pangea-node-sdk/tests/integration/share.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/pangea-node-sdk/tests/integration/share.test.ts b/packages/pangea-node-sdk/tests/integration/share.test.ts index f4949f778..e9a785dc1 100644 --- a/packages/pangea-node-sdk/tests/integration/share.test.ts +++ b/packages/pangea-node-sdk/tests/integration/share.test.ts @@ -30,7 +30,7 @@ const ADD_TAGS = ["tag3"]; const testFilePath = "./tests/testdata/testfile.pdf"; const zeroBytesFilePath = "./tests/testdata/zerobytes.txt"; -jest.setTimeout(60000); +jest.setTimeout(120000); const delay = async (ms: number) => new Promise((resolve) => { @@ -278,6 +278,7 @@ it("get url and post upload", async () => { throw e; } + console.log(response.request_id); const url = response.accepted_result?.post_url || ""; const file_details = response.accepted_result?.post_form_data; From 8f4355e4a02967925e790b99e8f14aa2dc457c71 Mon Sep 17 00:00:00 2001 From: Kenan Yildirim Date: Tue, 19 Mar 2024 11:12:04 -0400 Subject: [PATCH 36/36] pangea-node-sdk: add more Secure Share docs (GEA-11739) (#878) --- .../pangea-node-sdk/src/services/share.ts | 29 +- packages/pangea-node-sdk/src/types.ts | 395 +++++++++++++++++- 2 files changed, 388 insertions(+), 36 deletions(-) diff --git a/packages/pangea-node-sdk/src/services/share.ts b/packages/pangea-node-sdk/src/services/share.ts index 0cfb90ab7..ae15fb83d 100644 --- a/packages/pangea-node-sdk/src/services/share.ts +++ b/packages/pangea-node-sdk/src/services/share.ts @@ -22,7 +22,7 @@ class ShareService extends BaseService { /** * @summary Delete - * @description Delete object by ID or path. If both are supplied, the path must match that of the object represented by the ID. + * @description Delete object by ID or path. If both are supplied, the path must match that of the object represented by the ID. Beta API. * @operationId share_post_v1beta_delete * @param {Share.DeleteRequest} request * @returns {Promise} - A promise representing an async call to the delete endpoint. @@ -40,7 +40,7 @@ class ShareService extends BaseService { /** * @summary Create a folder - * @description Create a folder, either by name or path and parent_id. + * @description Create a folder, either by name or path and parent_id. Beta API. * @operationId share_post_v1beta_folder_create * @param {Share.FolderCreateRequest} request * @returns {Promise} - A promise representing an async call to the folder create endpoint. @@ -67,7 +67,7 @@ class ShareService extends BaseService { /** * @summary Get an object - * @description Get object. If both ID and Path are supplied, the call will fail if the target object doesn't match both properties. + * @description Get object. If both ID and path are supplied, the call will fail if the target object doesn't match both properties. Beta API. * @operationId share_post_v1beta_get * @param {Share.GetRequest} request * @returns {Promise} - A promise representing an async call to the get item endpoint. @@ -87,7 +87,7 @@ class ShareService extends BaseService { /** * @summary Get archive - * @description Get an archive file of multiple objects. + * @description Get an archive file of multiple objects. Beta API. * @operationId share_post_v1beta_get_archive * @param {Share.GetArchiveRequest} request * @returns {Promise} - A promise representing an async call to the get archive endpoint. @@ -105,7 +105,7 @@ class ShareService extends BaseService { /** * @summary List - * @description List or filter/search records. + * @description List or filter/search records. Beta API. * @operationId share_post_v1beta_list * @param {Share.ListRequest} request * @returns {Promise} - A promise representing an async call to the list endpoint. @@ -122,8 +122,8 @@ class ShareService extends BaseService { } /** - * @summary Upload a file [beta] - * @description Upload a file. + * @summary Upload a file + * @description Upload a file. Beta API. * @operationId share_post_v1beta_put * @param {Share.PutRequest} request * @param {FileData} fileData @@ -176,10 +176,9 @@ class ShareService extends BaseService { /** * @summary Request upload URL - * @description Request an upload URL. + * @description Request an upload URL. Beta API. * @operationId share_post_v1beta_put 2 * @param {Share.PutRequest} request - * @param {FileData} fileData * @returns {Promise} - A promise representing an async call to the put endpoint. * @example * ```js @@ -219,7 +218,7 @@ class ShareService extends BaseService { /** * @summary Update a file - * @description Update a file. + * @description Update a file. Beta API. * @operationId share_post_v1beta_update * @param {Share.UpdateRequest} request * @returns {Promise} - A promise representing an async call to the update endpoint. @@ -246,7 +245,7 @@ class ShareService extends BaseService { /** * @summary Create share links - * @description Create a share link. + * @description Create a share link. Beta API. * @operationId share_post_v1beta_share_link_create * @param {Share.ShareLinkCreateRequest} request * @returns {Promise} - A promise representing an async call to the share link create endpoint. @@ -273,7 +272,7 @@ class ShareService extends BaseService { /** * @summary Get share link - * @description Get a share link. + * @description Get a share link. Beta API. * @operationId share_post_v1beta_share_link_get * @param {Share.ShareLinkGetRequest} request * @returns {Promise} - A promise representing an async call to the share link get endpoint. @@ -291,7 +290,7 @@ class ShareService extends BaseService { /** * @summary List share links - * @description Look up share links by filter options. + * @description Look up share links by filter options. Beta API. * @operationId share_post_v1beta_share_link_list * @param {Share.ShareLinkListRequest} request * @returns {Promise} - A promise representing an async call to the share link list endpoint. @@ -309,7 +308,7 @@ class ShareService extends BaseService { /** * @summary Delete share links - * @description Delete share links. + * @description Delete share links. Beta API. * @operationId share_post_v1beta_share_link_delete * @param {Share.ShareLinkDeleteRequest} request * @returns {Promise} - A promise representing an async call to the delete share links endpoint. @@ -327,7 +326,7 @@ class ShareService extends BaseService { /** * @summary Send share links - * @description Send share links. + * @description Send share links. Beta API. * @operationId share_post_v1beta_share_link_send * @param {Share.ShareLinkDeleteRequest} request * @returns {Promise} - A promise representing an async call to the send share links endpoint. diff --git a/packages/pangea-node-sdk/src/types.ts b/packages/pangea-node-sdk/src/types.ts index 9e7576dc1..1d859df12 100644 --- a/packages/pangea-node-sdk/src/types.ts +++ b/packages/pangea-node-sdk/src/types.ts @@ -2192,42 +2192,112 @@ export namespace Share { export type Tags = string[]; export interface ItemData { + /** + * The number of billable bytes (includes Metadata, Tags, etc.) for the + * object. + */ + billable_size?: number; + + /** The date and time the object was created. */ + created_at: string; + + /** The ID of a stored object. */ id: string; - type: string; + + /** The MD5 hash of the file contents. */ + md5?: string; + + /** + * A set of string-based key/value pairs used to provide additional data + * about an object. + */ + metadata?: Metadata; name: string; - created_at: string; - updated_at: string; parent_id: string; - size?: number; - billable_size?: number; - location?: string; - tags?: Tags; - metadata?: Metadata; - md5?: string; + + /** The SHA256 hash of the file contents. */ sha256?: string; + + /** The SHA512 hash of the file contents. */ sha512?: string; + + /** The size of the object in bytes. */ + size?: number; + + /** A list of user-defined tags. */ + tags?: Tags; + + /** The type of the item (file or dir). */ + type: string; + + /** The date and time the object was last updated. */ + updated_at: string; } export interface DeleteRequest { + /** + * The ID of the object to delete. + */ id?: string; + + /** + * If true, delete a folder even if it's not empty. Deletes the contents of + * folder as well. + */ force?: boolean; + + /** + * The path of the object to delete. + */ path?: string; } export interface Authenticator { + /** + * An authentication mechanism. + */ auth_type: AuthenticatorType; + + /** + * An email address. + */ auth_context: string; } export interface DeleteResult { + /** + * Number of objects deleted. + */ count: number; } export interface FolderCreateRequest { + /** + * The name of an object. + */ name?: string; + + /** + * A set of string-based key/value pairs used to provide additional data + * about an object. + */ metadata?: Metadata; + + /** + * The ID of a stored object. + */ parent_id?: string; + + /** + * A case-sensitive path to an object. Contains a sequence of path segments + * delimited by the the `/` character. Any path ending in a `/` character + * refers to a folder. + */ path?: string; + + /** + * A list of user-defined tags. + */ tags?: Tags; } @@ -2236,30 +2306,97 @@ export namespace Share { } export interface GetRequest { + /** The ID of the object to retrieve. */ id?: string; + + /** The path of the object to retrieve. */ path?: string; + + /** The requested transfer method for the file data. */ transfer_method?: TransferMethod; } export interface GetResult { object: ItemData; + + /** A URL where the file can be downloaded from. */ dest_url?: string; } export interface PutRequest { - name?: string; + /** + * The hexadecimal-encoded CRC32C hash of the file data, which will be + * verified by the server if provided. + */ + crc32c?: string; + + /** + * The format of the file, which will be verified by the server if provided. + * Uploads not matching the supplied format will be rejected. + */ format?: FileFormat; + + /** + * The hexadecimal-encoded MD5 hash of the file data, which will be verified + * by the server if provided. + */ + md5?: string; + + /** + * A set of string-based key/value pairs used to provide additional data + * about an object. + */ metadata?: Metadata; + + /** + * The MIME type of the file, which will be verified by the server if + * provided. Uploads not matching the supplied MIME type will be rejected. + */ mimetype?: string; + + /** The name of the object to store. */ + name?: string; + + /** + * The parent ID of the object (a folder). Leave blank to keep in the root + * folder. + */ parent_id?: string; + + /** + * An optional path where the file should be placed. It will auto-create + * directories if necessary. + */ path?: string; - crc32c?: string; - md5?: string; + + /** + * The hexadecimal-encoded SHA1 hash of the file data, which will be + * verified by the server if provided. + */ sha1?: string; + + /** + * The SHA256 hash of the file data, which will be verified by the server + * if provided. + */ sha256?: string; + + /** + * The hexadecimal-encoded SHA512 hash of the file data, which will be + * verified by the server if provided. + */ sha512?: string; + + /** + * The size (in bytes) of the file. If the upload doesn't match, the call + * will fail. + */ size?: number; + + /** A list of user-defined tags */ tags?: Tags; + + /** The transfer method used to upload the file data. */ transfer_method?: TransferMethod; } @@ -2268,15 +2405,51 @@ export namespace Share { } export interface UpdateRequest { + /** + * A list of metadata key/values to set in the object. If a provided key + * exists, the value will be replaced. + */ + add_metadata?: Metadata; + + /** + * A list of tags to add. It is not an error to provide a tag which already + * exists. + */ + add_tags?: Tags; + + /** An identifier for the file to update. */ id: string; + + /** Set the object's metadata. */ + metadata?: Metadata; + + /** Set the parent (folder) of the object. */ + parent_id?: string; + + /** An alternative to ID for identifying the target file. */ path?: string; - add_metadata?: Metadata; + + /** + * A list of metadata key/values to remove in the object. It is not an + * error for a provided key to not exist. If a provided key exists but + * doesn't match the provided value, it will not be removed. + */ remove_metadata?: Metadata; - metadata?: Metadata; - add_tags?: Tags; + + /** + * A list of tags to remove. It is not an error to provide a tag which is + * not present. + */ remove_tags?: Tags; + + /** Set the object's tags. */ tags?: Tags; - parent_id?: string; + + /** + * The date and time the object was last updated. If included, the update + * will fail if this doesn't match the date and time of the last update for + * the object. + */ updated_at?: string; } @@ -2285,50 +2458,103 @@ export namespace Share { } export interface ListFilter { + /** + * Only records where the object exists in the supplied parent folder path + * name. + */ folder?: string; - folder__contains?: string[]; - folder__in?: string[]; } export interface ListRequest { filter?: ListFilter; + + /** + * Reflected value from a previous response to obtain the next page of + * results. + */ last?: string; + + /** Order results asc(ending) or desc(ending). */ order?: ItemOrder; + + /** Which field to order results by. */ order_by?: ItemOrderBy; + + /** Maximum results to include in the response. */ size?: number; } export interface ListResult { + /** The total number of objects matched by the list request. */ count: number; + + /** + * Used to fetch the next page of the current listing when provided in a + * repeated request's last parameter. + */ last?: string; objects: ItemData[]; } export interface GetArchiveRequest { + /** + * The IDs of the objects to include in the archive. Folders include all + * children. + */ ids: string[]; + + /** The format to use to build the archive. */ format?: ArchiveFormat; + + /** The requested transfer method for the file data. */ transfer_method?: TransferMethod; } export interface GetArchiveResult { + /** A location where the archive can be downloaded from. */ dest_url?: string; + + /** Number of objects included in the archive. */ count: number; } export interface Authenticator { + /** An authentication mechanism. */ auth_type: AuthenticatorType; + + /** An email address. */ auth_context: string; } export interface ShareLinkCreateItem { + /** List of storage IDs. */ targets: string[]; + + /** Type of link. */ link_type?: LinkType; + + /** The date and time the share link expires. */ expires_at?: string; + + /** + * The maximum number of times a user can be authenticated to access the + * share link. + */ max_access_count?: number; + + /** A list of authenticators. */ authenticators: Authenticator[]; + + /** An optional message to use in accessing shares. */ message?: string; + + /** An optional title to use in accessing shares. */ title?: string; + + /** An email address. */ notify_email?: string; + + /** A list of user-defined tags. */ tags?: Tags; } @@ -2337,20 +2563,53 @@ export namespace Share { } export interface ShareLinkItem { + /** The ID of a share link. */ id: string; + + /** The ID of a bucket resource. */ storage_pool_id: string; + + /** List of storage IDs. */ targets: string[]; + + /** Type of link. */ link_type: string; + + /** + * The number of times a user has authenticated to access the share link. + */ access_count: number; + + /** + * The maximum number of times a user can be authenticated to access the + * share link. + */ max_access_count: number; + + /** The date and time the share link was created. */ created_at: string; + /** The date and time the share link expires. */ expires_at: string; + + /** The date and time the share link was last accessed. */ last_accessed_at?: string; + + /** A list of authenticators */ authenticators: Authenticator[]; + + /** A URL to access the file/folders shared with a link. */ link: string; + + /** An optional message to use in accessing shares. */ message?: string; + + /** An optional title to use in accessing shares. */ title?: string; + + /** An email address. */ notify_email?: string; + + /** A list of user-defined tags. */ tags?: Tags; } @@ -2359,6 +2618,7 @@ export namespace Share { } export interface ShareLinkGetRequest { + /** The ID of a share link. */ id: string; } @@ -2367,57 +2627,145 @@ export namespace Share { } export interface ShareLinkListFilter { + /** Only records where id equals this value. */ id?: string; + + /** Only records where id includes each substring. */ id__contains?: string[]; + + /** Only records where id equals one of the provided substrings. */ id__in?: string[]; + + /** Only records where storage_pool_id equals this value. */ storage_pool_id?: string; + + /** Only records where storage_pool_id includes each substring. */ storage_pool_id__contains?: string[]; + + /** Only records where storage_pool_id equals one of the provided substrings. */ storage_pool_id__in?: string[]; - target?: string; - target__contains?: string[]; - target__in?: string[]; + + /** Only records where target_id equals this value. */ + target_id?: string; + + /** Only records where target_id includes each substring. */ + target_id__contains?: string[]; + + /** Only records where target_id equals one of the provided substrings. */ + target_id__in?: string[]; + + /** Only records where link_type equals this value. */ link_type?: string; + + /** Only records where link_type includes each substring. */ link_type__contains?: string[]; + + /** Only records where link_type equals one of the provided substrings. */ link_type__in?: string[]; + + /** Only records where access_count equals this value. */ access_count?: number; + + /** Only records where access_count is greater than this value. */ access_count__gt?: number; + + /** Only records where access_count is greater than or equal to this value. */ access_count__gte?: number; + + /** Only records where access_count is less than this value. */ access_count__lt?: number; + + /** Only records where access_count is less than or equal to this value. */ access_count__lte?: number; + + /** Only records where max_access_count equals this value. */ max_access_count?: number; + + /** Only records where max_access_count is greater than this value. */ max_access_count__gt?: number; + + /** Only records where max_access_count is greater than or equal to this value. */ max_access_count__gte?: number; + + /** Only records where max_access_count is less than this value. */ max_access_count__lt?: number; + + /** Only records where max_access_count is less than or equal to this value. */ max_access_count__lte?: number; + + /** Only records where created_at equals this value. */ created_at?: string; + + /** Only records where created_at is greater than this value. */ created_at__gt?: string; + + /** Only records where created_at is greater than or equal to this value. */ created_at__gte?: string; + + /** Only records where created_at is less than this value. */ created_at__lt?: string; + + /** Only records where created_at is less than or equal to this value. */ created_at__lte?: string; + + /** Only records where expires_at equals this value. */ expires_at?: string; + + /** Only records where expires_at is greater than this value. */ expires_at__gt?: string; + + /** Only records where expires_at is greater than or equal to this value. */ expires_at__gte?: string; + + /** Only records where expires_at is less than this value. */ expires_at__lt?: string; + + /** Only records where expires_at is less than or equal to this value. */ expires_at__lte?: string; + + /** Only records where last_accessed_at equals this value. */ last_accessed_at?: string; + + /** Only records where last_accessed_at is greater than this value. */ last_accessed_at__gt?: string; + + /** Only records where last_accessed_at is greater than or equal to this value. */ last_accessed_at__gte?: string; + + /** Only records where last_accessed_at is less than this value. */ last_accessed_at__lt?: string; + + /** Only records where last_accessed_at is less than or equal to this value. */ last_accessed_at__lte?: string; + + /** Only records where link equals this value. */ link?: string; + + /** Only records where link includes each substring. */ link__contains?: string[]; + + /** Only records where link equals one of the provided substrings. */ link__in?: string[]; } export interface ShareLinkListRequest { filter?: ShareLinkListFilter; + + /** Reflected value from a previous response to obtain the next page of results. */ last?: string; + + /** Order results asc(ending) or desc(ending). */ order?: ItemOrder; + + /** Which field to order results by. */ order_by?: ItemOrderBy; + + /** Maximum results to include in the response. */ size?: number; } export interface ShareLinkListResult { + /** The total number of share links matched by the list request. */ count: number; share_link_objects: ShareLinkItem[]; } @@ -2431,12 +2779,17 @@ export namespace Share { } export interface ShareLinkSendItem { + /** The ID of a share link. */ id: string; + + /** An email address. */ email: string; } export interface ShareLinkSendRequest { links: ShareLinkSendItem[]; + + /** An email address. */ sender_email: string; sender_name?: string; }