mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(Azure Storage Node): New node (#12536)
This commit is contained in:
committed by
GitHub
parent
d550382a4a
commit
727f6f3c0e
582
packages/nodes-base/nodes/Microsoft/Storage/GenericFunctions.ts
Normal file
582
packages/nodes-base/nodes/Microsoft/Storage/GenericFunctions.ts
Normal file
@@ -0,0 +1,582 @@
|
||||
import { camelCase } from 'change-case';
|
||||
import type {
|
||||
JsonObject,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
IHttpRequestMethods,
|
||||
IHttpRequestOptions,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
IN8nHttpFullResponse,
|
||||
INodeListSearchResult,
|
||||
INodeListSearchItems,
|
||||
INodeParameterResourceLocator,
|
||||
ICredentialDataDecryptedObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
import { Parser } from 'xml2js';
|
||||
import { firstCharLowerCase, parseBooleans, parseNumbers } from 'xml2js/lib/processors';
|
||||
|
||||
import { compareHeader } from './compare-header';
|
||||
|
||||
export const XMsVersion = '2021-12-02';
|
||||
|
||||
export const HeaderConstants = {
|
||||
AUTHORIZATION: 'authorization',
|
||||
CONTENT_ENCODING: 'content-encoding',
|
||||
CONTENT_LANGUAGE: 'content-language',
|
||||
CONTENT_LENGTH: 'content-length',
|
||||
CONTENT_MD5: 'content-md5',
|
||||
CONTENT_TYPE: 'content-Type',
|
||||
DATE: 'date',
|
||||
ETAG: 'etag',
|
||||
IF_MATCH: 'if-match',
|
||||
IF_MODIFIED_SINCE: 'if-Modified-since',
|
||||
IF_NONE_MATCH: 'if-none-match',
|
||||
IF_UNMODIFIED_SINCE: 'if-unmodified-since',
|
||||
ORIGIN: 'origin',
|
||||
RANGE: 'range',
|
||||
X_MS_COPY_SOURCE: 'x-ms-copy-source',
|
||||
X_MS_DATE: 'x-ms-date',
|
||||
X_MS_VERSION: 'x-ms-version',
|
||||
X_MS_BLOB_TYPE: 'x-ms-blob-type',
|
||||
X_MS_BLOB_CONTENT_DISPOSITION: 'x-ms-blob-content-disposition',
|
||||
X_MS_BLOB_PUBLIC_ACCESS: 'x-ms-blob-public-access',
|
||||
X_MS_HAS_IMMUTABILITY_POLICY: 'x-ms-has-immutability-policy',
|
||||
X_MS_HAS_LEGAL_HOLD: 'x-ms-has-legal-hold',
|
||||
X_MS_CONTENT_CRC64: 'x-ms-content-crc64',
|
||||
X_MS_REQUEST_SERVER_ENCRYPTED: 'x-ms-request-server-encrypted',
|
||||
X_MS_ENCRYPTION_SCOPE: 'x-ms-encryption-scope',
|
||||
X_MS_VERSION_ID: 'x-ms-version-id',
|
||||
X_MS_TAG_COUNT: 'x-ms-tag-count',
|
||||
X_MS_COPY_PROGRESS: 'x-ms-copy-progress',
|
||||
X_MS_INCREMENTAL_COPY: 'x-ms-incremental-copy',
|
||||
X_MS_BLOB_SEQUENCE_NUMBER: 'x-ms-blob-sequence-number',
|
||||
X_MS_BLOB_COMMITTED_BLOCK_COUNT: 'x-ms-blob-committed-block-count',
|
||||
X_MS_SERVER_ENCRYPTED: 'x-ms-server-encrypted',
|
||||
X_MS_ENCRYPTION_CONTEXT: 'x-ms-encryption-context',
|
||||
X_MS_BLOB_CONTENT_MD5: 'x-ms-blob-content-md5',
|
||||
X_MS_BLOB_SEALED: 'x-ms-blob-sealed',
|
||||
X_MS_IMMUTABILITY_POLICY_UNTIL_DATE: 'x-ms-immutability-policy-until-date',
|
||||
X_MS_IMMUTABILITY_POLICY_MODE: 'x-ms-immutability-policy-mode',
|
||||
X_MS_LEGAL_HOLD: 'x-ms-legal-hold',
|
||||
X_MS_DELETE_TYPE_PERMANENT: 'x-ms-delete-type-permanent',
|
||||
X_MS_ACCESS_TIER: 'x-ms-access-tier',
|
||||
X_MS_BLOB_CACHE_CONTROL: 'x-ms-blob-cache-control',
|
||||
X_MS_LEASE_ID: 'x-ms-lease-id',
|
||||
X_MS_BLOB_CONTENT_ENCODING: 'x-ms-blob-content-encoding',
|
||||
X_MS_BLOB_CONTENT_LANGUAGE: 'x-ms-blob-content-language',
|
||||
X_MS_BLOB_CONTENT_TYPE: 'x-ms-blob-content-type',
|
||||
X_MS_EXPIRY_OPTION: 'x-ms-expiry-option',
|
||||
X_MS_EXPIRY_TIME: 'x-ms-expiry-time',
|
||||
X_MS_TAGS: 'x-ms-tags',
|
||||
X_MS_UPN: 'x-ms-upn',
|
||||
PREFIX_X_MS: 'x-ms-',
|
||||
PREFIX_X_MS_META: 'x-ms-meta-',
|
||||
};
|
||||
|
||||
export async function azureStorageApiRequest(
|
||||
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
qs?: IDataObject,
|
||||
headers?: IDataObject,
|
||||
url?: string,
|
||||
): Promise<string> {
|
||||
const authentication = this.getNodeParameter('authentication', 0) as 'oAuth2' | 'sharedKey';
|
||||
const credentialsType =
|
||||
authentication === 'oAuth2' ? 'azureStorageOAuth2Api' : 'azureStorageSharedKeyApi';
|
||||
const credentials = await this.getCredentials<{
|
||||
baseUrl: string;
|
||||
}>(credentialsType);
|
||||
|
||||
const options: IHttpRequestOptions = {
|
||||
method,
|
||||
url: url ?? `${credentials.baseUrl}${endpoint}`,
|
||||
headers,
|
||||
body,
|
||||
qs,
|
||||
};
|
||||
|
||||
options.headers ??= {};
|
||||
options.headers[HeaderConstants.X_MS_DATE] = new Date().toUTCString();
|
||||
options.headers[HeaderConstants.X_MS_VERSION] = XMsVersion;
|
||||
|
||||
// XML response
|
||||
const response = (await this.helpers.requestWithAuthentication.call(
|
||||
this,
|
||||
credentialsType,
|
||||
options,
|
||||
)) as string;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function handleErrorPostReceive(
|
||||
this: IExecuteSingleFunctions,
|
||||
data: INodeExecutionData[],
|
||||
response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
if (String(response.statusCode).startsWith('4') || String(response.statusCode).startsWith('5')) {
|
||||
const resource = this.getNodeParameter('resource') as string;
|
||||
const operation = this.getNodeParameter('operation') as string;
|
||||
|
||||
const parser = new Parser({
|
||||
explicitArray: false,
|
||||
tagNameProcessors: [firstCharLowerCase],
|
||||
});
|
||||
const { error } =
|
||||
((await parser.parseStringPromise(data[0].json as unknown as string)) as {
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
headerName?: string;
|
||||
headerValue?: string;
|
||||
};
|
||||
}) ?? {};
|
||||
|
||||
if (
|
||||
error?.code === 'InvalidAuthenticationInfo' &&
|
||||
((error as IDataObject)?.authenticationErrorDetail as string) ===
|
||||
'Lifetime validation failed. The token is expired.'
|
||||
) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: 'Lifetime validation failed. The token is expired.',
|
||||
description: 'Please check your credentials and try again',
|
||||
});
|
||||
}
|
||||
|
||||
if (resource === 'blob') {
|
||||
if (error?.code === 'ContainerNotFound') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: "The required container doesn't match any existing one",
|
||||
description: "Double-check the value in the parameter 'Container Name' and try again",
|
||||
});
|
||||
}
|
||||
|
||||
if (operation === 'create') {
|
||||
if (
|
||||
this.getNodeParameter('from') === 'url' &&
|
||||
((error?.code === 'InvalidHeaderValue' &&
|
||||
error?.headerName === HeaderConstants.X_MS_COPY_SOURCE) ||
|
||||
error?.code === 'CannotVerifyCopySource')
|
||||
) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: 'The provided URL is invalid',
|
||||
description: "Double-check the value in the parameter 'URL' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'delete') {
|
||||
if (error?.code === 'BlobNotFound') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: "The required blob doesn't match any existing one",
|
||||
description: "Double-check the value in the parameter 'Blob to Delete' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'get') {
|
||||
if (error?.code === 'BlobNotFound') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: "The required blob doesn't match any existing one",
|
||||
description: "Double-check the value in the parameter 'Blob to Get' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'getAll') {
|
||||
if (
|
||||
error?.code === 'InvalidQueryParameterValue' &&
|
||||
(this.getNodeParameter('fields', []) as string[]).includes('permissions')
|
||||
) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message:
|
||||
'Permissions field is only supported for accounts with a hierarchical namespace enabled',
|
||||
description: "Exclude 'Permissions' from 'Fields' and try again",
|
||||
});
|
||||
}
|
||||
if (
|
||||
error?.code === 'UnsupportedQueryParameter' &&
|
||||
(this.getNodeParameter('fields', []) as string[]).includes('deleted') &&
|
||||
(this.getNodeParameter('filter', []) as string[]).includes('deleted')
|
||||
) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: "Including 'Deleted' field and filter is not supported",
|
||||
description: "Exclude 'Deleted' from 'Fields' or 'Filter' and try again",
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (resource === 'container') {
|
||||
if (operation === 'create') {
|
||||
if (error?.code === 'ContainerAlreadyExists') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: 'The specified container already exists',
|
||||
description: "Use a unique value for 'Container Name' and try again",
|
||||
});
|
||||
}
|
||||
if (error?.code === 'PublicAccessNotPermitted') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: 'Public access is not permitted on this storage account',
|
||||
description: "Check the 'Access Level' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'delete') {
|
||||
if (error?.code === 'ContainerNotFound') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: "The required container doesn't match any existing one",
|
||||
description:
|
||||
"Double-check the value in the parameter 'Container to Delete' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'get') {
|
||||
if (error?.code === 'ContainerNotFound') {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, {
|
||||
message: "The required container doesn't match any existing one",
|
||||
description: "Double-check the value in the parameter 'Container to Get' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'getAll') {
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
|
||||
message: error.code,
|
||||
description: error.message,
|
||||
});
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
|
||||
parseXml: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export function getCanonicalizedHeadersString(requestOptions: IHttpRequestOptions): string {
|
||||
let headersArray: Array<{ name: string; value: string }> = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
for (const [name, value] of Object.entries(requestOptions.headers!) as unknown as [
|
||||
string,
|
||||
string,
|
||||
]) {
|
||||
if (name.toLowerCase().startsWith(HeaderConstants.PREFIX_X_MS)) {
|
||||
headersArray.push({ name, value });
|
||||
}
|
||||
}
|
||||
|
||||
headersArray.sort((a, b): number => {
|
||||
return compareHeader(a.name.toLowerCase(), b.name.toLowerCase());
|
||||
});
|
||||
|
||||
// Remove duplicate headers
|
||||
headersArray = headersArray.filter((value, index, array) => {
|
||||
if (index > 0 && value.name.toLowerCase() === array[index - 1].name.toLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
let canonicalizedHeadersStringToSign: string = '';
|
||||
headersArray.forEach((header) => {
|
||||
canonicalizedHeadersStringToSign += `${header.name
|
||||
.toLowerCase()
|
||||
.trimEnd()}:${header.value.trimStart()}\n`;
|
||||
});
|
||||
|
||||
return canonicalizedHeadersStringToSign;
|
||||
}
|
||||
|
||||
export function getCanonicalizedResourceString(
|
||||
requestOptions: IHttpRequestOptions,
|
||||
credentials: ICredentialDataDecryptedObject,
|
||||
): string {
|
||||
const path: string = new URL(requestOptions.baseURL + requestOptions.url).pathname || '/';
|
||||
let canonicalizedResourceString = `/${credentials.account as string}${path}`;
|
||||
if (requestOptions.qs && Object.keys(requestOptions.qs).length > 0) {
|
||||
canonicalizedResourceString +=
|
||||
'\n' +
|
||||
Object.keys(requestOptions.qs)
|
||||
.sort()
|
||||
.map(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
(key) => `${key.toLowerCase()}:${decodeURIComponent(requestOptions.qs![key] as string)}`,
|
||||
)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
return canonicalizedResourceString;
|
||||
}
|
||||
|
||||
export function parseHeaders(headers: IDataObject) {
|
||||
const parseBooleanHeaders = [
|
||||
HeaderConstants.X_MS_DELETE_TYPE_PERMANENT,
|
||||
HeaderConstants.X_MS_INCREMENTAL_COPY,
|
||||
HeaderConstants.X_MS_SERVER_ENCRYPTED,
|
||||
HeaderConstants.X_MS_BLOB_SEALED,
|
||||
HeaderConstants.X_MS_REQUEST_SERVER_ENCRYPTED,
|
||||
HeaderConstants.X_MS_HAS_IMMUTABILITY_POLICY,
|
||||
HeaderConstants.X_MS_HAS_LEGAL_HOLD,
|
||||
];
|
||||
const parseNumberHeaders = [
|
||||
HeaderConstants.X_MS_TAG_COUNT,
|
||||
HeaderConstants.CONTENT_LENGTH,
|
||||
HeaderConstants.X_MS_BLOB_SEQUENCE_NUMBER,
|
||||
HeaderConstants.X_MS_COPY_PROGRESS,
|
||||
HeaderConstants.X_MS_BLOB_COMMITTED_BLOCK_COUNT,
|
||||
];
|
||||
|
||||
const result: IDataObject = {};
|
||||
|
||||
const metadataKeys = Object.keys(headers).filter((x) =>
|
||||
x.startsWith(HeaderConstants.PREFIX_X_MS_META),
|
||||
);
|
||||
|
||||
for (const key in headers) {
|
||||
if (metadataKeys.includes(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let newKey = key.startsWith(HeaderConstants.PREFIX_X_MS)
|
||||
? camelCase(key.replace(HeaderConstants.PREFIX_X_MS, ''))
|
||||
: camelCase(key);
|
||||
newKey = newKey.replace('-', '');
|
||||
|
||||
const newValue = parseBooleanHeaders.includes(key)
|
||||
? parseBooleans(headers[key] as string)
|
||||
: parseNumberHeaders.includes(key)
|
||||
? parseNumbers(headers[key] as string)
|
||||
: headers[key];
|
||||
|
||||
result[newKey] = newValue;
|
||||
}
|
||||
|
||||
if (metadataKeys.length > 0) {
|
||||
result.metadata = {};
|
||||
for (const key of metadataKeys) {
|
||||
(result.metadata as IDataObject)[key.replace(HeaderConstants.PREFIX_X_MS_META, '')] =
|
||||
headers[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function parseBlobList(
|
||||
xml: string,
|
||||
): Promise<{ blobs: IDataObject[]; maxResults?: number; nextMarker?: string }> {
|
||||
const parser = new Parser({
|
||||
explicitArray: false,
|
||||
tagNameProcessors: [firstCharLowerCase, (name) => name.replace('-', '')],
|
||||
valueProcessors: [
|
||||
function (value, name) {
|
||||
if (
|
||||
[
|
||||
'deleted',
|
||||
'isCurrentVersion',
|
||||
'serverEncrypted',
|
||||
'incrementalCopy',
|
||||
'accessTierInferred',
|
||||
'isSealed',
|
||||
'legalHold',
|
||||
].includes(name)
|
||||
) {
|
||||
return parseBooleans(value);
|
||||
} else if (
|
||||
[
|
||||
'maxResults',
|
||||
'contentLength',
|
||||
'blobSequenceNumber',
|
||||
'remainingRetentionDays',
|
||||
'tagCount',
|
||||
'content-Length',
|
||||
].includes(name)
|
||||
) {
|
||||
return parseNumbers(value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
],
|
||||
});
|
||||
const data = (await parser.parseStringPromise(xml)) as {
|
||||
enumerationResults: {
|
||||
blobs: { blob: IDataObject | IDataObject[] };
|
||||
maxResults: number;
|
||||
nextMarker: string;
|
||||
prefix: string;
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof data.enumerationResults.blobs !== 'object') {
|
||||
// No items
|
||||
return { blobs: [] };
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.enumerationResults.blobs.blob)) {
|
||||
// Single item
|
||||
data.enumerationResults.blobs.blob = [data.enumerationResults.blobs.blob];
|
||||
}
|
||||
|
||||
for (const blob of data.enumerationResults.blobs.blob) {
|
||||
if (blob.tags) {
|
||||
if (!Array.isArray(((blob.tags as IDataObject).tagSet as IDataObject).tag)) {
|
||||
((blob.tags as IDataObject).tagSet as IDataObject).tag = [
|
||||
((blob.tags as IDataObject).tagSet as IDataObject).tag,
|
||||
];
|
||||
}
|
||||
blob.tags = ((blob.tags as IDataObject).tagSet as IDataObject).tag;
|
||||
}
|
||||
}
|
||||
|
||||
for (const container of data.enumerationResults.blobs.blob) {
|
||||
if (container.metadata === '') {
|
||||
delete container.metadata;
|
||||
}
|
||||
if (container.orMetadata === '') {
|
||||
delete container.orMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
blobs: data.enumerationResults.blobs.blob,
|
||||
maxResults: data.enumerationResults.maxResults,
|
||||
nextMarker: data.enumerationResults.nextMarker,
|
||||
};
|
||||
}
|
||||
|
||||
export async function parseContainerList(
|
||||
xml: string,
|
||||
): Promise<{ containers: IDataObject[]; maxResults?: number; nextMarker?: string }> {
|
||||
const parser = new Parser({
|
||||
explicitArray: false,
|
||||
tagNameProcessors: [firstCharLowerCase, (name) => name.replace('-', '')],
|
||||
valueProcessors: [
|
||||
function (value, name) {
|
||||
if (
|
||||
[
|
||||
'deleted',
|
||||
'hasImmutabilityPolicy',
|
||||
'hasLegalHold',
|
||||
'preventEncryptionScopeOverride',
|
||||
'isImmutableStorageWithVersioningEnabled',
|
||||
].includes(name)
|
||||
) {
|
||||
return parseBooleans(value);
|
||||
} else if (['maxResults', 'remainingRetentionDays'].includes(name)) {
|
||||
return parseNumbers(value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
],
|
||||
});
|
||||
const data = (await parser.parseStringPromise(xml)) as {
|
||||
enumerationResults: {
|
||||
containers: { container: IDataObject | IDataObject[] };
|
||||
maxResults: number;
|
||||
nextMarker: string;
|
||||
prefix: string;
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof data.enumerationResults.containers !== 'object') {
|
||||
// No items
|
||||
return { containers: [] };
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.enumerationResults.containers.container)) {
|
||||
// Single item
|
||||
data.enumerationResults.containers.container = [data.enumerationResults.containers.container];
|
||||
}
|
||||
|
||||
for (const container of data.enumerationResults.containers.container) {
|
||||
if (container.metadata === '') {
|
||||
delete container.metadata;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
containers: data.enumerationResults.containers.container,
|
||||
maxResults: data.enumerationResults.maxResults,
|
||||
nextMarker: data.enumerationResults.nextMarker,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getBlobs(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const container = this.getNodeParameter('container') as INodeParameterResourceLocator;
|
||||
|
||||
let response: string;
|
||||
|
||||
const qs: IDataObject = {
|
||||
restype: 'container',
|
||||
comp: 'list',
|
||||
};
|
||||
|
||||
if (paginationToken) {
|
||||
qs.marker = paginationToken;
|
||||
response = await azureStorageApiRequest.call(this, 'GET', `/${container.value}`, {}, qs);
|
||||
} else {
|
||||
qs.maxresults = 5000;
|
||||
if (filter) {
|
||||
qs.prefix = filter;
|
||||
}
|
||||
response = await azureStorageApiRequest.call(this, 'GET', `/${container.value}`, {}, qs);
|
||||
}
|
||||
|
||||
const data = await parseBlobList(response);
|
||||
|
||||
const results: INodeListSearchItems[] = data.blobs
|
||||
.map((c) => ({
|
||||
name: c.name as string,
|
||||
value: c.name as string,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
|
||||
return {
|
||||
results,
|
||||
paginationToken: data.nextMarker,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getContainers(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
let response: string;
|
||||
|
||||
const qs: IDataObject = {
|
||||
comp: 'list',
|
||||
};
|
||||
|
||||
if (paginationToken) {
|
||||
qs.marker = paginationToken;
|
||||
response = await azureStorageApiRequest.call(this, 'GET', '/', {}, qs);
|
||||
} else {
|
||||
qs.maxresults = 5000;
|
||||
if (filter) {
|
||||
qs.prefix = filter;
|
||||
}
|
||||
response = await azureStorageApiRequest.call(this, 'GET', '/', {}, qs);
|
||||
}
|
||||
|
||||
const data = await parseContainerList(response);
|
||||
|
||||
const results: INodeListSearchItems[] = data.containers
|
||||
.map((c) => ({
|
||||
name: c.name as string,
|
||||
value: c.name as string,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
|
||||
return {
|
||||
results,
|
||||
paginationToken: data.nextMarker,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user