AWS S3 Integration

This commit is contained in:
ricardo 2020-04-15 18:42:37 -04:00
parent c042909d89
commit 611c79204c
8 changed files with 2234 additions and 3 deletions

View file

@ -28,7 +28,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
uri: `https://${endpoint}${signOpts.path}`, uri: `https://${endpoint}${signOpts.path}`,
body: signOpts.body, body: signOpts.body,
}; };
console.log('aja')
try { try {
return await this.helpers.request!(options); return await this.helpers.request!(options);
} catch (error) { } catch (error) {
@ -46,7 +46,6 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
} }
} }
export async function awsApiRequestREST(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any export async function awsApiRequestREST(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any
const response = await awsApiRequest.call(this, service, method, path, body, headers); const response = await awsApiRequest.call(this, service, method, path, body, headers);
try { try {
@ -56,7 +55,6 @@ export async function awsApiRequestREST(this: IHookFunctions | IExecuteFunctions
} }
} }
export async function awsApiRequestSOAP(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any export async function awsApiRequestSOAP(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any
const response = await awsApiRequest.call(this, service, method, path, body, headers); const response = await awsApiRequest.call(this, service, method, path, body, headers);
try { try {

View file

@ -0,0 +1,568 @@
import {
IExecuteFunctions,
BINARY_ENCODING,
} from 'n8n-core';
import {
IBinaryKeyData,
IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import {
bucketFields,
bucketOperations,
} from './BucketDescription';
import {
folderOperations,
folderFields,
} from './FolderDescription';
import {
fileOperations,
fileFields,
} from './FileDescription';
import {
awsApiRequestREST,
awsApiRequestSOAP,
awsApiRequestSOAPAllItems,
} from './GenericFunctions';
import {
snakeCase,
paramCase,
} from 'change-case';
import {
createHash,
} from 'crypto';
import * as js2xmlparser from 'js2xmlparser';
export class AwsS3 implements INodeType {
description: INodeTypeDescription = {
displayName: 'AWS S3',
name: 'awsS3',
icon: 'file:s3.png',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Sends data to AWS S3',
defaults: {
name: 'AWS S3',
color: '#d05b4b',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'aws',
required: true,
}
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Bucket',
value: 'bucket',
},
{
name: 'Folder',
value: 'folder',
},
{
name: 'File',
value: 'file',
},
],
default: 'bucket',
description: 'The operation to perform.',
},
// BUCKET
...bucketOperations,
...bucketFields,
// FOLDER
...folderOperations,
...folderFields,
// UPLOAD
...fileOperations,
...fileFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const qs: IDataObject = {};
const headers: IDataObject = {};
let responseData;
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < items.length; i++) {
if (resource === 'bucket') {
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html
if (operation === 'create') {
const credentials = this.getCredentials('aws');
const name = this.getNodeParameter('name', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.acl) {
headers['x-amz-acl'] = paramCase(additionalFields.acl as string);
}
if (additionalFields.bucketObjectLockEnabled) {
headers['x-amz-bucket-object-lock-enabled'] = additionalFields.bucketObjectLockEnabled as boolean;
}
if (additionalFields.grantFullControl) {
headers['x-amz-grant-full-control'] = additionalFields.grantFullControl;
}
if (additionalFields.grantRead) {
headers['x-amz-grant-read'] = additionalFields.grantRead;
}
if (additionalFields.grantReadAcp) {
headers['x-amz-grant-read-acp'] = additionalFields.grantReadAcp;
}
if (additionalFields.grantWrite) {
headers['x-amz-grant-write'] = additionalFields.grantWrite;
}
if (additionalFields.grantWriteAcp) {
headers['x-amz-grant-write-acp']=additionalFields.grantWriteAcp;
}
const body: IDataObject = {
'@': {
xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/',
},
LocationConstraint: [credentials!.region],
};
const data = js2xmlparser.parse('CreateBucketConfiguration', body);
responseData = await awsApiRequestSOAP.call(this, `${name}.s3`, 'PUT', '', data, qs, headers);
returnData.push({ success: true });
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
if (returnAll) {
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListAllMyBucketsResult.Buckets.Bucket', 's3', 'GET', '');
} else {
qs.limit = this.getNodeParameter('limit', 0) as number;
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListAllMyBucketsResult.Buckets.Bucket', 's3', 'GET', '', '', qs);
responseData = responseData.slice(0, qs.limit);
}
returnData.push.apply(returnData, responseData);
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html
if (operation === 'search') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
const additionalFields = this.getNodeParameter('additionalFields', 0) as IDataObject;
if (additionalFields.prefix) {
qs['prefix'] = additionalFields.prefix as string;
}
if (additionalFields.encodingType) {
qs['encoding-type'] = additionalFields.encodingType as string;
}
if (additionalFields.delmiter) {
qs['delimiter'] = additionalFields.delmiter as string;
}
if (additionalFields.fetchOwner) {
qs['fetch-owner'] = additionalFields.fetchOwner as string;
}
if (additionalFields.startAfter) {
qs['start-after'] = additionalFields.startAfter as string;
}
if (additionalFields.requesterPays) {
qs['x-amz-request-payer'] = 'requester';
}
qs['list-type'] = 2;
if (returnAll) {
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListBucketResult.Contents', `${bucketName}.s3`, 'GET', '', '', qs);
} else {
qs['max-keys'] = this.getNodeParameter('limit', 0) as number;
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'GET', '', '', qs);
responseData = responseData.ListBucketResult.Contents;
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData);
} else {
returnData.push(responseData);
}
}
}
if (resource === 'folder') {
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
if (operation === 'create') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const folderName = this.getNodeParameter('folderName', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
let path = `/${folderName}/`;
if (additionalFields.requesterPays) {
headers['x-amz-request-payer'] = 'requester';
}
if (additionalFields.parentFolderKey) {
path = `/${additionalFields.parentFolderKey}${folderName}/`;
}
if (additionalFields.storageClass) {
headers['x-amz-storage-class'] = (snakeCase(additionalFields.storageClass as string)).toUpperCase();
}
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'PUT', path, '', qs, headers);
returnData.push({ success: true });
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html
if (operation === 'delete') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const folderKey = this.getNodeParameter('folderKey', i) as string;
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListBucketResult.Contents', `${bucketName}.s3`, 'GET', '/', '', { 'list-type': 2, prefix: folderKey });
// folder empty then just delete it
if (responseData.length === 0) {
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'DELETE', `/${folderKey}`, '', qs);
responseData = { deleted: [ { 'Key': folderKey } ] };
} else {
// delete everything inside the folder
const body: IDataObject = {
'@': {
xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/',
},
Object: [],
};
for (const childObject of responseData) {
(body.Object as IDataObject[]).push({
Key: childObject.Key as string
});
}
const data = js2xmlparser.parse('Delete', body);
headers['Content-MD5'] = createHash('md5').update(data).digest('base64');
headers['Content-Type'] = 'application/xml';
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'POST', '/', data, { delete: '' } , headers);
responseData = { deleted: responseData.DeleteResult.Deleted };
}
returnData.push(responseData);
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html
if (operation === 'getAll') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
const options = this.getNodeParameter('options', 0) as IDataObject;
if (options.folderKey) {
qs['prefix'] = options.folderKey as string;
}
if (options.fetchOwner) {
qs['fetch-owner'] = options.fetchOwner as string;
}
qs['list-type'] = 2;
if (returnAll) {
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListBucketResult.Contents', `${bucketName}.s3`, 'GET', '', '', qs);
} else {
qs.limit = this.getNodeParameter('limit', 0) as number;
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListBucketResult.Contents', `${bucketName}.s3`, 'GET', '', '', qs);
}
if (Array.isArray(responseData)) {
responseData = responseData.filter((e: IDataObject) => (e.Key as string).endsWith('/') && e.Size === '0' && e.Key !== options.folderKey);
if (qs.limit) {
responseData = responseData.splice(0, qs.limit as number);
}
returnData.push.apply(returnData, responseData);
}
}
}
if (resource === 'file') {
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html
if (operation === 'copy') {
const source = this.getNodeParameter('source', i) as string;
const bucketName = this.getNodeParameter('bucketName', i) as string;
const destination = this.getNodeParameter('destination', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
headers['x-amz-copy-source'] = source;
if (additionalFields.requesterPays) {
headers['x-amz-request-payer'] = 'requester';
}
if (additionalFields.storageClass) {
headers['x-amz-storage-class'] = (snakeCase(additionalFields.storageClass as string)).toUpperCase();
}
if (additionalFields.acl) {
headers['x-amz-acl'] = paramCase(additionalFields.acl as string);
}
if (additionalFields.grantFullControl) {
headers['x-amz-grant-full-control'] = '';
}
if (additionalFields.grantRead) {
headers['x-amz-grant-read'] = '';
}
if (additionalFields.grantReadAcp) {
headers['x-amz-grant-read-acp'] = '';
}
if (additionalFields.grantWriteAcp) {
headers['x-amz-grant-write-acp'] = '';
}
if (additionalFields.lockLegalHold) {
headers['x-amz-object-lock-legal-hold'] = (additionalFields.lockLegalHold as boolean) ? 'ON' : 'OFF';
}
if (additionalFields.lockMode) {
headers['x-amz-object-lock-mode'] = (additionalFields.lockMode as string).toUpperCase();
}
if (additionalFields.lockRetainUntilDate) {
headers['x-amz-object-lock-retain-until-date'] = additionalFields.lockRetainUntilDate as string;
}
if (additionalFields.serverSideEncryption) {
headers['x-amz-server-side-encryption'] = additionalFields.serverSideEncryption as string;
}
if (additionalFields.encryptionAwsKmsKeyId) {
headers['x-amz-server-side-encryption-aws-kms-key-id'] = additionalFields.encryptionAwsKmsKeyId as string;
}
if (additionalFields.serverSideEncryptionContext) {
headers['x-amz-server-side-encryption-context'] = additionalFields.serverSideEncryptionContext as string;
}
if (additionalFields.serversideEncryptionCustomerAlgorithm) {
headers['x-amz-server-side-encryption-customer-algorithm'] = additionalFields.serversideEncryptionCustomerAlgorithm as string;
}
if (additionalFields.serversideEncryptionCustomerKey) {
headers['x-amz-server-side-encryption-customer-key'] = additionalFields.serversideEncryptionCustomerKey as string;
}
if (additionalFields.serversideEncryptionCustomerKeyMD5) {
headers['x-amz-server-side-encryption-customer-key-MD5'] = additionalFields.serversideEncryptionCustomerKeyMD5 as string;
}
if (additionalFields.taggingDirective) {
headers['x-amz-tagging-directive'] = (additionalFields.taggingDirective as string).toUpperCase();
}
if (additionalFields.metadataDirective) {
headers['x-amz-metadata-directive'] = (additionalFields.metadataDirective as string).toUpperCase();
}
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'PUT', `${destination}`, '', qs, headers);
returnData.push(responseData.CopyObjectResult);
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html
if (operation === 'download') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const fileKey = this.getNodeParameter('fileKey', i) as string;
const fileName = fileKey.split('/')[fileKey.split('/').length - 1];
if (fileKey.substring(fileKey.length - 1) === '/') {
throw new Error('Downloding a whole directory is not yet supported, please provide a file key');
}
const response = await awsApiRequestREST.call(this, `${bucketName}.s3`, 'GET', `/${fileKey}`, '', qs, {}, { encoding: null, resolveWithFullResponse: true });
let mimeType: string | undefined;
if (response.headers['content-type']) {
mimeType = response.headers['content-type'];
}
const newItem: INodeExecutionData = {
json: items[i].json,
binary: {},
};
if (items[i].binary !== undefined) {
// Create a shallow copy of the binary data so that the old
// data references which do not get changed still stay behind
// but the incoming data does not get changed.
Object.assign(newItem.binary, items[i].binary);
}
items[i] = newItem;
const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string;
const data = Buffer.from(response.body as string, 'utf8');
items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(data as unknown as Buffer, fileName, mimeType);
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html
if (operation === 'delete') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const fileKey = this.getNodeParameter('fileKey', i) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
if (options.versionId) {
qs.versionId = options.versionId as string;
}
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'DELETE', `/${fileKey}`, '', qs);
returnData.push({ success: true });
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html
if (operation === 'getAll') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
const options = this.getNodeParameter('options', 0) as IDataObject;
if (options.folderKey) {
qs['prefix'] = options.folderKey as string;
}
if (options.fetchOwner) {
qs['fetch-owner'] = options.fetchOwner as string;
}
qs['delimiter'] = '/';
qs['list-type'] = 2;
if (returnAll) {
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListBucketResult.Contents', `${bucketName}.s3`, 'GET', '', '', qs);
} else {
qs.limit = this.getNodeParameter('limit', 0) as number;
responseData = await awsApiRequestSOAPAllItems.call(this, 'ListBucketResult.Contents', `${bucketName}.s3`, 'GET', '', '', qs);
responseData = responseData.splice(0, qs.limit);
}
if (Array.isArray(responseData)) {
responseData = responseData.filter((e: IDataObject) => !(e.Key as string).endsWith('/') && e.Size !== '0');
if (qs.limit) {
responseData = responseData.splice(0, qs.limit as number);
}
returnData.push.apply(returnData, responseData);
}
}
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
if (operation === 'upload') {
const bucketName = this.getNodeParameter('bucketName', i) as string;
const fileName = this.getNodeParameter('fileName', i) as string;
const isBinaryData = this.getNodeParameter('binaryData', i) as boolean;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const tagsValues = (this.getNodeParameter('tagsUi', i) as IDataObject).tagsValues as IDataObject[];
let path = '/';
let body;
if (additionalFields.requesterPays) {
headers['x-amz-request-payer'] = 'requester';
}
if (additionalFields.parentFolderKey) {
path = `/${additionalFields.parentFolderKey}/`;
}
if (additionalFields.storageClass) {
headers['x-amz-storage-class'] = (snakeCase(additionalFields.storageClass as string)).toUpperCase();
}
if (additionalFields.acl) {
headers['x-amz-acl'] = paramCase(additionalFields.acl as string);
}
if (additionalFields.grantFullControl) {
headers['x-amz-grant-full-control'] = '';
}
if (additionalFields.grantRead) {
headers['x-amz-grant-read'] = '';
}
if (additionalFields.grantReadAcp) {
headers['x-amz-grant-read-acp'] = '';
}
if (additionalFields.grantWriteAcp) {
headers['x-amz-grant-write-acp'] = '';
}
if (additionalFields.lockLegalHold) {
headers['x-amz-object-lock-legal-hold'] = (additionalFields.lockLegalHold as boolean) ? 'ON' : 'OFF';
}
if (additionalFields.lockMode) {
headers['x-amz-object-lock-mode'] = (additionalFields.lockMode as string).toUpperCase();
}
if (additionalFields.lockRetainUntilDate) {
headers['x-amz-object-lock-retain-until-date'] = additionalFields.lockRetainUntilDate as string;
}
if (additionalFields.serverSideEncryption) {
headers['x-amz-server-side-encryption'] = additionalFields.serverSideEncryption as string;
}
if (additionalFields.encryptionAwsKmsKeyId) {
headers['x-amz-server-side-encryption-aws-kms-key-id'] = additionalFields.encryptionAwsKmsKeyId as string;
}
if (additionalFields.serverSideEncryptionContext) {
headers['x-amz-server-side-encryption-context'] = additionalFields.serverSideEncryptionContext as string;
}
if (additionalFields.serversideEncryptionCustomerAlgorithm) {
headers['x-amz-server-side-encryption-customer-algorithm'] = additionalFields.serversideEncryptionCustomerAlgorithm as string;
}
if (additionalFields.serversideEncryptionCustomerKey) {
headers['x-amz-server-side-encryption-customer-key'] = additionalFields.serversideEncryptionCustomerKey as string;
}
if (additionalFields.serversideEncryptionCustomerKeyMD5) {
headers['x-amz-server-side-encryption-customer-key-MD5'] = additionalFields.serversideEncryptionCustomerKeyMD5 as string;
}
if (tagsValues) {
const tags: string[] = [];
tagsValues.forEach((o: IDataObject) => { tags.push(`${o.key}=${o.value}`); });
headers['x-amz-tagging'] = tags.join('&');
}
if (isBinaryData) {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0) as string;
if (items[i].binary === undefined) {
throw new Error('No binary data exists on item!');
}
if ((items[i].binary as IBinaryKeyData)[binaryPropertyName] === undefined) {
throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
}
const binaryData = (items[i].binary as IBinaryKeyData)[binaryPropertyName];
body = Buffer.from(binaryData.data, BINARY_ENCODING) as Buffer;
headers['Content-Type'] = binaryData.mimeType;
headers['Content-MD5'] = createHash('md5').update(body).digest('base64');
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'PUT', `${path}${fileName || binaryData.fileName}`, body, qs, headers);
} else {
const fileContent = this.getNodeParameter('fileContent', i) as string;
body = Buffer.from(fileContent, 'utf8');
headers['Content-Type'] = 'text/html';
headers['Content-MD5'] = createHash('md5').update(fileContent).digest('base64');
responseData = await awsApiRequestSOAP.call(this, `${bucketName}.s3`, 'PUT', `${path}${fileName}`, body, qs, headers);
}
returnData.push({ success: true });
}
}
}
if (resource === 'file' && operation === 'download') {
// For file downloads the files get attached to the existing items
return this.prepareOutputData(items);
} else {
return [this.helpers.returnJsonArray(returnData)];
}
}
}

View file

@ -0,0 +1,320 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const bucketOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'bucket',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create an bucket',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all buckets',
},
{
name: 'Search',
value: 'search',
description: 'Search withim a bucket',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const bucketFields = [
/* -------------------------------------------------------------------------- */
/* bucket:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Name',
name: 'name',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'bucket',
],
operation: [
'create',
],
},
},
description: 'A succinct description of the nature, symptoms, cause, or effect of the bucket.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
resource: [
'bucket',
],
operation: [
'create',
],
},
},
default: {},
options: [
{
displayName: 'ACL',
name: 'acl',
type: 'options',
options: [
{
name: 'Authenticated Read',
value: 'authenticatedRead',
},
{
name: 'Private',
value: 'Private',
},
{
name: 'Public Read',
value: 'publicRead',
},
{
name: 'Public Read Write',
value: 'publicReadWrite',
},
],
default: '',
description: 'The canned ACL to apply to the bucket.',
},
{
displayName: 'Bucket Object Lock Enabled',
name: 'bucketObjectLockEnabled',
type: 'boolean',
default: false,
description: 'Specifies whether you want S3 Object Lock to be enabled for the new bucket.',
},
{
displayName: 'Grant Full Control',
name: 'grantFullControl',
type: 'boolean',
default: false,
description: 'Allows grantee the read, write, read ACP, and write ACP permissions on the bucket.',
},
{
displayName: 'Grant Read',
name: 'grantRead',
type: 'boolean',
default: false,
description: 'Allows grantee to list the objects in the bucket.',
},
{
displayName: 'Grant Read ACP',
name: 'grantReadAcp',
type: 'boolean',
default: false,
description: 'Allows grantee to read the bucket ACL.',
},
{
displayName: 'Grant Write',
name: 'grantWrite',
type: 'boolean',
default: false,
description: 'Allows grantee to create, overwrite, and delete any object in the bucket.',
},
{
displayName: 'Grant Write ACP',
name: 'grantWriteAcp',
type: 'boolean',
default: false,
description: 'Allows grantee to write the ACL for the applicable bucket.',
},
],
},
/* -------------------------------------------------------------------------- */
/* bucket:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'bucket',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'bucket',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
/* -------------------------------------------------------------------------- */
/* bucket:search */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'bucket',
],
operation: [
'search',
],
},
},
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'search',
],
resource: [
'bucket',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'search',
],
resource: [
'bucket',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
resource: [
'bucket',
],
operation: [
'search',
],
},
},
default: {},
options: [
{
displayName: 'Delimiter',
name: 'delimiter',
type: 'string',
default: '',
description: 'A delimiter is a character you use to group keys.',
},
{
displayName: 'Encoding Type',
name: 'encodingType',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
],
default: '',
description: 'Encoding type used by Amazon S3 to encode object keys in the response.',
},
{
displayName: 'Fetch Owner',
name: 'fetchOwner',
type: 'boolean',
default: false,
description: 'The owner field is not present in listV2 by default, if you want to return owner field with each key in the result then set the fetch owner field to true.',
},
{
displayName: 'Prefix',
name: 'prefix',
type: 'string',
default: '',
description: 'Limits the response to keys that begin with the specified prefix.',
},
{
displayName: 'Requester Pays',
name: 'requesterPays',
type: 'boolean',
default: false,
description: 'Weather the requester will pay for requests and data transfer. While Requester Pays is enabled, anonymous access to this bucket is disabled.',
},
{
displayName: 'Start After',
name: 'startAfter',
type: 'string',
default: '',
description: 'StartAfter is where you want Amazon S3 to start listing from. Amazon S3 starts listing after this specified key',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,940 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const fileOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'file',
],
},
},
options: [
{
name: 'Copy',
value: 'copy',
description: 'Copy a file',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a file',
},
{
name: 'Download',
value: 'download',
description: 'Download a file',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all files',
},
{
name: 'Upload',
value: 'upload',
description: 'Upload a file',
},
],
default: 'copy',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const fileFields = [
/* -------------------------------------------------------------------------- */
/* file:copy */
/* -------------------------------------------------------------------------- */
{
displayName: 'Source',
name: 'source',
type: 'string',
required: true,
default: '',
placeholder: '/bucket/my-image.jpg',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'copy',
],
},
},
description: 'The name of the source bucket and key name of the source object, separated by a slash (/)',
},
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'copy',
],
},
},
description: 'The name of the destination bucket.',
},
{
displayName: 'Destination',
name: 'destination',
type: 'string',
required: true,
default: '',
placeholder: '/my-second-image.jpg',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'copy',
],
},
},
description: 'The key of the destination object.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'copy',
],
},
},
default: {},
options: [
{
displayName: 'ACL',
name: 'acl',
type: 'options',
options: [
{
name: 'Authenticated Read',
value: 'authenticatedRead'
},
{
name: 'AWS Exec Read',
value: 'awsExecRead'
},
{
name: 'Bucket Owner Full Control',
value: 'bucketOwnerFullControl'
},
{
name: 'Bucket Owner Read',
value: 'bucketOwnerRead'
},
{
name: 'Private',
value: 'private',
},
{
name: 'Public Read',
value: 'publicRead'
},
{
name: 'Public Read Write',
value: 'publicReadWrite'
},
],
default: 'private',
description: 'The canned ACL to apply to the object.'
},
{
displayName: 'Grant Full Control',
name: 'grantFullControl',
type: 'boolean',
default: false,
description: 'Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.',
},
{
displayName: 'Grant Read',
name: 'grantRead',
type: 'boolean',
default: false,
description: 'Allows grantee to read the object data and its metadata.',
},
{
displayName: 'Grant Read ACP',
name: 'grantReadAcp',
type: 'boolean',
default: false,
description: 'Allows grantee to read the object ACL.',
},
{
displayName: 'Grant Write ACP',
name: 'grantWriteAcp',
type: 'boolean',
default: false,
description: 'Allows grantee to write the ACL for the applicable object.',
},
{
displayName: 'Lock Legal Hold',
name: 'lockLegalHold',
type: 'boolean',
default: false,
description: 'Specifies whether a legal hold will be applied to this object',
},
{
displayName: 'Lock Mode',
name: 'lockMode',
type: 'options',
options: [
{
name: 'Governance',
value: 'governance',
},
{
name: 'Compliance',
value: 'compliance',
},
],
default: '',
description: 'The Object Lock mode that you want to apply to this object.',
},
{
displayName: 'Lock Retain Until Date',
name: 'lockRetainUntilDate',
type: 'dateTime',
default: '',
description: `The date and time when you want this object's Object Lock to expire.`,
},
{
displayName: 'Metadata Directive',
name: 'metadataDirective',
type: 'options',
options: [
{
name: 'Copy',
value: 'copy',
},
{
name: 'Replace',
value: 'replace',
},
],
default: '',
description: 'Specifies whether the metadata is copied from the source object or replaced with metadata provided in the request.',
},
{
displayName: 'Requester Pays',
name: 'requesterPays',
type: 'boolean',
default: false,
description: 'Weather the requester will pay for requests and data transfer. While Requester Pays is enabled, anonymous access to this bucket is disabled.',
},
{
displayName: 'Server Side Encryption',
name: 'serverSideEncryption',
type: 'options',
options: [
{
name: 'AES256',
value: 'AES256',
},
{
name: 'AWS:KMS',
value: 'aws:kms',
},
],
default: '',
description: 'The server-side encryption algorithm used when storing this object in Amazon S3',
},
{
displayName: 'Server Side Encryption Context',
name: 'serverSideEncryptionContext',
type: 'string',
default: '',
description: 'Specifies the AWS KMS Encryption Context to use for object encryption',
},
{
displayName: 'Server Side Encryption AWS KMS Key ID',
name: 'encryptionAwsKmsKeyId',
type: 'string',
default: '',
description: 'If x-amz-server-side-encryption is present and has the value of aws:kms',
},
{
displayName: 'Server Side Encryption Customer Algorithm',
name: 'serversideEncryptionCustomerAlgorithm',
type: 'string',
default: '',
description: 'Specifies the algorithm to use to when encrypting the object (for example, AES256).',
},
{
displayName: 'Server Side Encryption Customer Key',
name: 'serversideEncryptionCustomerKey',
type: 'string',
default: '',
description: 'Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data',
},
{
displayName: 'Server Side Encryption Customer Key MD5',
name: 'serversideEncryptionCustomerKeyMD5',
type: 'string',
default: '',
description: 'Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321.',
},
{
displayName: 'Storage Class',
name: 'storageClass',
type: 'options',
options: [
{
name: 'Deep Archive',
value: 'deepArchive',
},
{
name: 'Intelligent Tiering',
value: 'intelligentTiering',
},
{
name: 'One Zone IA',
value: 'onezoneIA',
},
{
name: 'Glacier',
value: 'glacier',
},
{
name: 'Standard',
value: 'standard',
},
{
name: 'Standard IA',
value: 'standardIA',
},
],
default: 'standard',
description: 'Amazon S3 storage classes.',
},
{
displayName: 'Tagging Directive',
name: 'taggingDirective',
type: 'options',
options: [
{
name: 'Copy',
value: 'copy',
},
{
name: 'Replace',
value: 'replace',
},
],
default: '',
description: 'Specifies whether the metadata is copied from the source object or replaced with metadata provided in the request.',
},
],
},
/* -------------------------------------------------------------------------- */
/* file:upload */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'upload',
],
},
},
},
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
placeholder: 'hello.txt',
required: true,
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'upload',
],
binaryData: [
false,
],
},
},
},
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'upload',
],
binaryData: [
true,
],
},
},
description: 'If not set the binary data filename will be used.',
},
{
displayName: 'Binary Data',
name: 'binaryData',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
},
},
description: 'If the data to upload should be taken from binary field.',
},
{
displayName: 'File Content',
name: 'fileContent',
type: 'string',
default: '',
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
binaryData: [
false
],
},
},
placeholder: '',
description: 'The text content of the file to upload.',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
binaryData: [
true
],
},
},
placeholder: '',
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'upload',
],
},
},
default: {},
options: [
{
displayName: 'ACL',
name: 'acl',
type: 'options',
options: [
{
name: 'Authenticated Read',
value: 'authenticatedRead'
},
{
name: 'AWS Exec Read',
value: 'awsExecRead'
},
{
name: 'Bucket Owner Full Control',
value: 'bucketOwnerFullControl'
},
{
name: 'Bucket Owner Read',
value: 'bucketOwnerRead'
},
{
name: 'Private',
value: 'private',
},
{
name: 'Public Read',
value: 'publicRead'
},
{
name: 'Public Read Write',
value: 'publicReadWrite'
},
],
default: 'private',
description: 'The canned ACL to apply to the object.'
},
{
displayName: 'Grant Full Control',
name: 'grantFullControl',
type: 'boolean',
default: false,
description: 'Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.',
},
{
displayName: 'Grant Read',
name: 'grantRead',
type: 'boolean',
default: false,
description: 'Allows grantee to read the object data and its metadata.',
},
{
displayName: 'Grant Read ACP',
name: 'grantReadAcp',
type: 'boolean',
default: false,
description: 'Allows grantee to read the object ACL.',
},
{
displayName: 'Grant Write ACP',
name: 'grantWriteAcp',
type: 'boolean',
default: false,
description: 'Allows grantee to write the ACL for the applicable object.',
},
{
displayName: 'Lock Legal Hold',
name: 'lockLegalHold',
type: 'boolean',
default: false,
description: 'Specifies whether a legal hold will be applied to this object',
},
{
displayName: 'Lock Mode',
name: 'lockMode',
type: 'options',
options: [
{
name: 'Governance',
value: 'governance',
},
{
name: 'Compliance',
value: 'compliance',
},
],
default: '',
description: 'The Object Lock mode that you want to apply to this object.',
},
{
displayName: 'Lock Retain Until Date',
name: 'lockRetainUntilDate',
type: 'dateTime',
default: '',
description: `The date and time when you want this object's Object Lock to expire.`,
},
{
displayName: 'Parent Folder Key',
name: 'parentFolderKey',
type: 'string',
default: '',
description: 'Parent file you want to create the file in',
},
{
displayName: 'Requester Pays',
name: 'requesterPays',
type: 'boolean',
default: false,
description: 'Weather the requester will pay for requests and data transfer. While Requester Pays is enabled, anonymous access to this bucket is disabled.',
},
{
displayName: 'Server Side Encryption',
name: 'serverSideEncryption',
type: 'options',
options: [
{
name: 'AES256',
value: 'AES256',
},
{
name: 'AWS:KMS',
value: 'aws:kms',
},
],
default: '',
description: 'The server-side encryption algorithm used when storing this object in Amazon S3',
},
{
displayName: 'Server Side Encryption Context',
name: 'serverSideEncryptionContext',
type: 'string',
default: '',
description: 'Specifies the AWS KMS Encryption Context to use for object encryption',
},
{
displayName: 'Server Side Encryption AWS KMS Key ID',
name: 'encryptionAwsKmsKeyId',
type: 'string',
default: '',
description: 'If x-amz-server-side-encryption is present and has the value of aws:kms',
},
{
displayName: 'Server Side Encryption Customer Algorithm',
name: 'serversideEncryptionCustomerAlgorithm',
type: 'string',
default: '',
description: 'Specifies the algorithm to use to when encrypting the object (for example, AES256).',
},
{
displayName: 'Server Side Encryption Customer Key',
name: 'serversideEncryptionCustomerKey',
type: 'string',
default: '',
description: 'Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data',
},
{
displayName: 'Server Side Encryption Customer Key MD5',
name: 'serversideEncryptionCustomerKeyMD5',
type: 'string',
default: '',
description: 'Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321.',
},
{
displayName: 'Storage Class',
name: 'storageClass',
type: 'options',
options: [
{
name: 'Deep Archive',
value: 'deepArchive',
},
{
name: 'Intelligent Tiering',
value: 'intelligentTiering',
},
{
name: 'One Zone IA',
value: 'onezoneIA',
},
{
name: 'Glacier',
value: 'glacier',
},
{
name: 'Standard',
value: 'standard',
},
{
name: 'Standard IA',
value: 'standardIA',
},
],
default: 'standard',
description: 'Amazon S3 storage classes.',
},
],
},
{
displayName: 'Tags',
name: 'tagsUi',
placeholder: 'Add Tag',
type: 'fixedCollection',
default: '',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'upload',
],
},
},
options: [
{
name: 'tagsValues',
displayName: 'Tag',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: '',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: '',
},
],
}
],
description: 'Optional extra headers to add to the message (most headers are allowed).',
},
/* -------------------------------------------------------------------------- */
/* file:download */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'download',
],
},
},
},
{
displayName: 'File Key',
name: 'fileKey',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'download',
],
},
},
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
required: true,
default: 'data',
displayOptions: {
show: {
operation: [
'download'
],
resource: [
'file',
],
},
},
description: 'Name of the binary property to which to<br />write the data of the read file.',
},
/* -------------------------------------------------------------------------- */
/* file:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'delete',
],
},
},
},
{
displayName: 'File Key',
name: 'fileKey',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'delete',
],
},
},
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'delete',
],
},
},
options: [
{
displayName: 'Version ID',
name: 'versionId',
type: 'string',
default: '',
},
],
},
/* -------------------------------------------------------------------------- */
/* file:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'getAll',
],
},
},
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'file',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'file',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Fetch Owner',
name: 'fetchOwner',
type: 'boolean',
default: false,
description: 'The owner field is not present in listV2 by default, if you want to return owner field with each key in the result then set the fetch owner field to true.',
},
{
displayName: 'Folder Key',
name: 'folderKey',
type: 'string',
default: '',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,278 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const folderOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'folder',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a folder',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a folder',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all folders',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const folderFields = [
/* -------------------------------------------------------------------------- */
/* folder:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'create',
],
},
},
},
{
displayName: 'Folder Name',
name: 'folderName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'create',
],
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'create',
],
},
},
default: {},
options: [
{
displayName: 'Parent Folder Key',
name: 'parentFolderKey',
type: 'string',
default: '',
description: 'Parent folder you want to create the folder in'
},
{
displayName: 'Requester Pays',
name: 'requesterPays',
type: 'boolean',
default: false,
description: 'Weather the requester will pay for requests and data transfer. While Requester Pays is enabled, anonymous access to this bucket is disabled.',
},
{
displayName: 'Storage Class',
name: 'storageClass',
type: 'options',
options: [
{
name: 'Deep Archive',
value: 'deepArchive',
},
{
name: 'Intelligent Tiering',
value: 'intelligentTiering',
},
{
name: 'One Zone IA',
value: 'onezoneIA',
},
{
name: 'Glacier',
value: 'glacier',
},
{
name: 'Reduced Redundancy',
value: 'RecudedRedundancy',
},
{
name: 'Standard',
value: 'standard',
},
{
name: 'Standard IA',
value: 'standardIA',
},
],
default: 'standard',
description: 'Amazon S3 storage classes.'
},
],
},
/* -------------------------------------------------------------------------- */
/* folder:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'delete',
],
},
},
},
{
displayName: 'Folder Key',
name: 'folderKey',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'delete',
],
},
},
},
/* -------------------------------------------------------------------------- */
/* folder:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'getAll',
],
},
},
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'folder',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'folder',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'folder',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Fetch Owner',
name: 'fetchOwner',
type: 'boolean',
default: false,
description: 'The owner field is not present in listV2 by default, if you want to return owner field with each key in the result then set the fetch owner field to true.',
},
{
displayName: 'Folder Key',
name: 'folderKey',
type: 'string',
default: '',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,125 @@
import {
sign,
} from 'aws4';
import {
OptionsWithUri,
} from 'request';
import {
parseString,
} from 'xml2js';
import {
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
IWebhookFunctions,
} from 'n8n-core';
import {
IDataObject,
} from 'n8n-workflow';
import {
get,
} from 'lodash';
export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, service: string, method: string, path: string, body?: string | Buffer, query: IDataObject = {}, headers?: object, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('aws');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
const endpoint = `${service}.${credentials.region}.amazonaws.com`;
// Sign AWS API request with the user credentials
const signOpts = {headers: headers || {}, host: endpoint, method, path: `${path}?${queryToString(query).replace(/\+/g, '%2B')}`, body};
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`, secretAccessKey: `${credentials.secretAccessKey}`});
const options: OptionsWithUri = {
headers: signOpts.headers,
method,
qs: query,
uri: `https://${endpoint}${signOpts.path}`,
body: signOpts.body,
};
if (Object.keys(option).length !== 0) {
Object.assign(options, option);
}
try {
return await this.helpers.request!(options);
} catch (error) {
const errorMessage = error.response.body.message || error.response.body.Message || error.message;
if (error.statusCode === 403) {
if (errorMessage === 'The security token included in the request is invalid.') {
throw new Error('The AWS credentials are not valid!');
} else if (errorMessage.startsWith('The request signature we calculated does not match the signature you provided')) {
throw new Error('The AWS credentials are not valid!');
}
}
throw new Error(`AWS error response [${error.statusCode}]: ${errorMessage}`);
}
}
export async function awsApiRequestREST(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, service: string, method: string, path: string, body?: string, query: IDataObject = {}, headers?: object, options: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const response = await awsApiRequest.call(this, service, method, path, body, query, headers, options);
try {
return JSON.parse(response);
} catch (e) {
return response;
}
}
export async function awsApiRequestSOAP(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, service: string, method: string, path: string, body?: string | Buffer, query: IDataObject = {}, headers?: object): Promise<any> { // tslint:disable-line:no-any
const response = await awsApiRequest.call(this, service, method, path, body, query, headers);
try {
return await new Promise((resolve, reject) => {
parseString(response, { explicitArray: false }, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
} catch (e) {
return e;
}
}
export async function awsApiRequestSOAPAllItems(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, propertyName: string, service: string, method: string, path: string, body?: string, query: IDataObject = {}, headers: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
do {
responseData = await awsApiRequestSOAP.call(this, service, method, path, body, query, headers);
//https://forums.aws.amazon.com/thread.jspa?threadID=55746
if (get(responseData, `${propertyName.split('.')[0]}.NextContinuationToken`)) {
query['continuation-token'] = get(responseData, `${propertyName.split('.')[0]}.NextContinuationToken`);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
returnData.push.apply(returnData, get(responseData, propertyName));
} else {
returnData.push(get(responseData, propertyName));
}
}
if (query.limit && query.limit <= returnData.length) {
return returnData;
}
} while (
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== undefined &&
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== 'false'
);
return returnData;
}
function queryToString(params: IDataObject) {
return Object.keys(params).map(key => key + '=' + params[key]).join('&');
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -121,6 +121,7 @@
"dist/nodes/Affinity/Affinity.node.js", "dist/nodes/Affinity/Affinity.node.js",
"dist/nodes/Affinity/AffinityTrigger.node.js", "dist/nodes/Affinity/AffinityTrigger.node.js",
"dist/nodes/Aws/AwsLambda.node.js", "dist/nodes/Aws/AwsLambda.node.js",
"dist/nodes/Aws/S3/AwsS3.node.js",
"dist/nodes/Aws/AwsSes.node.js", "dist/nodes/Aws/AwsSes.node.js",
"dist/nodes/Aws/AwsSns.node.js", "dist/nodes/Aws/AwsSns.node.js",
"dist/nodes/Aws/AwsSnsTrigger.node.js", "dist/nodes/Aws/AwsSnsTrigger.node.js",
@ -283,6 +284,7 @@
"gm": "^1.23.1", "gm": "^1.23.1",
"googleapis": "~46.0.0", "googleapis": "~46.0.0",
"imap-simple": "^4.3.0", "imap-simple": "^4.3.0",
"js2xmlparser": "^4.0.1",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.set": "^4.3.2", "lodash.set": "^4.3.2",
"lodash.unset": "^4.5.2", "lodash.unset": "^4.5.2",