first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.awsElb",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/aws/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.awselb/"
}
]
}
}
@@ -0,0 +1,460 @@
import type {
IExecuteFunctions,
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
JsonObject,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
import { awsApiRequestSOAP, awsApiRequestSOAPAllItems } from './GenericFunctions';
import {
listenerCertificateFields,
listenerCertificateOperations,
} from './ListenerCertificateDescription';
import { loadBalancerFields, loadBalancerOperations } from './LoadBalancerDescription';
import { awsNodeAuthOptions, awsNodeCredentials } from '../utils';
export class AwsElb implements INodeType {
description: INodeTypeDescription = {
displayName: 'AWS ELB',
name: 'awsElb',
icon: 'file:elb.svg',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Sends data to AWS ELB API',
defaults: {
name: 'AWS ELB',
},
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: awsNodeCredentials,
properties: [
awsNodeAuthOptions,
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Listener Certificate',
value: 'listenerCertificate',
},
{
name: 'Load Balancer',
value: 'loadBalancer',
},
],
default: 'loadBalancer',
},
...loadBalancerOperations,
...loadBalancerFields,
...listenerCertificateOperations,
...listenerCertificateFields,
],
};
methods = {
loadOptions: {
async getLoadBalancers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const params = ['Version=2015-12-01'];
const data = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=DescribeLoadBalancers&' + params.join('&'),
);
let loadBalancers =
data.DescribeLoadBalancersResponse.DescribeLoadBalancersResult.LoadBalancers.member;
if (!Array.isArray(loadBalancers)) {
loadBalancers = [loadBalancers];
}
for (const loadBalancer of loadBalancers) {
const loadBalancerArn = loadBalancer.LoadBalancerArn as string;
const loadBalancerName = loadBalancer.LoadBalancerName as string;
returnData.push({
name: loadBalancerName,
value: loadBalancerArn,
});
}
return returnData;
},
async getLoadBalancerListeners(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const loadBalancerId = this.getCurrentNodeParameter('loadBalancerId') as string;
const params = ['Version=2015-12-01', 'LoadBalancerArn=' + loadBalancerId];
const data = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=DescribeListeners&' + params.join('&'),
);
let listeners = data.DescribeListenersResponse.DescribeListenersResult.Listeners.member;
if (!Array.isArray(listeners)) {
listeners = [listeners];
}
for (const listener of listeners) {
const listenerArn = listener.ListenerArn as string;
const listenerName = listener.ListenerArn as string;
returnData.push({
name: listenerArn,
value: listenerName,
});
}
return returnData;
},
async getSecurityGroups(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const body = ['Version=2016-11-15', 'Action=DescribeSecurityGroups'].join('&');
const data = await awsApiRequestSOAP.call(
this,
'ec2',
'POST',
'/',
body,
{},
{
'Content-Type': 'application/x-www-form-urlencoded',
charset: 'utf-8',
'User-Agent': 'aws-cli/1.18.124',
},
);
let securityGroups = data.DescribeSecurityGroupsResponse.securityGroupInfo.item;
if (!Array.isArray(securityGroups)) {
securityGroups = [securityGroups];
}
for (const securityGroup of securityGroups) {
const securityGroupId = securityGroup.groupId as string;
const securityGroupName = securityGroup.groupName as string;
returnData.push({
name: securityGroupName,
value: securityGroupId,
});
}
return returnData;
},
async getSubnets(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const body = ['Version=2016-11-15', 'Action=DescribeSubnets'].join('&');
const data = await awsApiRequestSOAP.call(
this,
'ec2',
'POST',
'/',
body,
{},
{
'Content-Type': 'application/x-www-form-urlencoded',
charset: 'utf-8',
'User-Agent': 'aws-cli/1.18.124',
},
);
let subnets = data.DescribeSubnetsResponse.subnetSet.item;
if (!Array.isArray(subnets)) {
subnets = [subnets];
}
for (const subnet of subnets) {
const subnetId = subnet.subnetId as string;
const subnetName = subnet.subnetId as string;
returnData.push({
name: subnetName,
value: subnetId,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
let responseData;
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'listenerCertificate') {
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_AddListenerCertificates.html
if (operation === 'add') {
const params = ['Version=2015-12-01'];
params.push(
'Certificates.member.1.CertificateArn=' +
(this.getNodeParameter('certificateId', i) as string),
);
params.push('ListenerArn=' + (this.getNodeParameter('listenerId', i) as string));
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=AddListenerCertificates&' + params.join('&'),
);
responseData =
responseData.AddListenerCertificatesResponse.AddListenerCertificatesResult
.Certificates.member;
}
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_DescribeListenerCertificates.html
if (operation === 'getMany') {
const params = ['Version=2015-12-01'];
const returnAll = this.getNodeParameter('returnAll', 0);
const listenerId = this.getNodeParameter('listenerId', i) as string;
params.push(`ListenerArn=${listenerId}`);
if (returnAll) {
responseData = await awsApiRequestSOAPAllItems.call(
this,
'DescribeListenerCertificatesResponse.DescribeListenerCertificatesResult.Certificates.member',
'elasticloadbalancing',
'GET',
'/?Action=DescribeListenerCertificates&' + params.join('&'),
);
} else {
params.push('PageSize=' + (this.getNodeParameter('limit', 0) as unknown as string));
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=DescribeListenerCertificates&' + params.join('&'),
);
responseData =
responseData.DescribeListenerCertificatesResponse.DescribeListenerCertificatesResult
.Certificates.member;
}
}
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_RemoveListenerCertificates.html
if (operation === 'remove') {
const params = ['Version=2015-12-01'];
params.push(
'Certificates.member.1.CertificateArn=' +
(this.getNodeParameter('certificateId', i) as string),
);
params.push('ListenerArn=' + (this.getNodeParameter('listenerId', i) as string));
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=RemoveListenerCertificates&' + params.join('&'),
);
responseData = { sucess: true };
}
}
if (resource === 'loadBalancer') {
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_CreateLoadBalancer.html
if (operation === 'create') {
const ipAddressType = this.getNodeParameter('ipAddressType', i) as string;
const name = this.getNodeParameter('name', i) as string;
const schema = this.getNodeParameter('schema', i) as string;
const type = this.getNodeParameter('type', i) as string;
const subnets = this.getNodeParameter('subnets', i) as string[];
const additionalFields = this.getNodeParameter('additionalFields', i);
const params = ['Version=2015-12-01'];
params.push(`IpAddressType=${ipAddressType}`);
params.push(`Name=${name}`);
params.push(`Scheme=${schema}`);
params.push(`Type=${type}`);
for (let index = 1; index <= subnets.length; index++) {
params.push(`Subnets.member.${index}=${subnets[index - 1]}`);
}
if (additionalFields.securityGroups) {
const securityGroups = additionalFields.securityGroups as string[];
for (let index = 1; index <= securityGroups.length; index++) {
params.push(`SecurityGroups.member.${index}=${securityGroups[index - 1]}`);
}
}
if (additionalFields.tagsUi) {
const tags = (additionalFields.tagsUi as IDataObject).tagValues as IDataObject[];
if (tags) {
for (let index = 1; index <= tags.length; index++) {
params.push(`Tags.member.${index}.Key=${tags[index - 1].key}`);
params.push(`Tags.member.${index}.Value=${tags[index - 1].value}`);
}
}
}
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=CreateLoadBalancer&' + params.join('&'),
);
responseData =
responseData.CreateLoadBalancerResponse.CreateLoadBalancerResult.LoadBalancers.member;
}
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_DeleteLoadBalancer.html
if (operation === 'delete') {
const params = ['Version=2015-12-01'];
params.push(
'LoadBalancerArn=' + (this.getNodeParameter('loadBalancerId', i) as string),
);
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=DeleteLoadBalancer&' + params.join('&'),
);
responseData = { success: true };
}
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_DescribeLoadBalancers.html
if (operation === 'getMany') {
const params = ['Version=2015-12-01'];
const returnAll = this.getNodeParameter('returnAll', 0);
if (returnAll) {
const filters = this.getNodeParameter('filters', i);
if (filters.names) {
const names = (filters.names as string).split(',');
for (let index = 1; index <= names.length; index++) {
params.push(`Names.member.${index}=${names[index - 1]}`);
}
}
responseData = await awsApiRequestSOAPAllItems.call(
this,
'DescribeLoadBalancersResponse.DescribeLoadBalancersResult.LoadBalancers.member',
'elasticloadbalancing',
'GET',
'/?Action=DescribeLoadBalancers&' + params.join('&'),
);
} else {
params.push('PageSize=' + this.getNodeParameter('limit', 0).toString());
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=DescribeLoadBalancers&' + params.join('&'),
);
responseData =
responseData.DescribeLoadBalancersResponse.DescribeLoadBalancersResult.LoadBalancers
.member;
}
}
//https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_DescribeLoadBalancers.html
if (operation === 'get') {
const params = ['Version=2015-12-01'];
params.push(
'LoadBalancerArns.member.1=' + (this.getNodeParameter('loadBalancerId', i) as string),
);
responseData = await awsApiRequestSOAP.call(
this,
'elasticloadbalancing',
'GET',
'/?Action=DescribeLoadBalancers&' + params.join('&'),
);
responseData =
responseData.DescribeLoadBalancersResponse.DescribeLoadBalancersResult.LoadBalancers
.member;
}
}
returnData.push(
...this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(responseData as IDataObject),
{
itemData: { item: i },
},
),
);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: (error as JsonObject).toString() });
continue;
}
throw error;
}
}
return [returnData as INodeExecutionData[]];
}
}
@@ -0,0 +1,160 @@
import get from 'lodash/get';
import type {
IDataObject,
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
IWebhookFunctions,
IHttpRequestOptions,
JsonObject,
IHttpRequestMethods,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
import { parseString } from 'xml2js';
import { getAwsCredentials } from '../GenericFunctions';
export async function awsApiRequest(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
service: string,
method: IHttpRequestMethods,
path: string,
body?: string | Buffer,
query: IDataObject = {},
headers?: object,
_option: IDataObject = {},
_region?: string,
) {
const { credentials, credentialsType } = await getAwsCredentials(this);
const requestOptions = {
qs: {
...query,
service,
path,
},
headers,
method,
url: '',
body,
region: credentials?.region as string,
} as IHttpRequestOptions;
try {
return await this.helpers.requestWithAuthentication.call(this, credentialsType, requestOptions);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function awsApiRequestREST(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
service: string,
method: IHttpRequestMethods,
path: string,
body?: string,
query: IDataObject = {},
headers?: object,
options: IDataObject = {},
region?: string,
) {
const response = await awsApiRequest.call(
this,
service,
method,
path,
body,
query,
headers,
options,
region,
);
try {
return JSON.parse(response as string);
} catch (e) {
return response;
}
}
export async function awsApiRequestSOAP(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
service: string,
method: IHttpRequestMethods,
path: string,
body?: string | Buffer,
query: IDataObject = {},
headers?: object,
option: IDataObject = {},
region?: string,
) {
const response = await awsApiRequest.call(
this,
service,
method,
path,
body,
query,
headers,
option,
region,
);
try {
return await new Promise((resolve, reject) => {
parseString(response as string, { 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: IHttpRequestMethods,
path: string,
body?: string,
query: IDataObject = {},
headers: IDataObject = {},
option: IDataObject = {},
region?: string,
) {
const returnData: IDataObject[] = [];
let responseData;
const propertyNameArray = propertyName.split('.');
do {
responseData = await awsApiRequestSOAP.call(
this,
service,
method,
path,
body,
query,
headers,
option,
region,
);
if (get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextMarker'])) {
query.Marker = get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextMarker']);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
returnData.push.apply(returnData, get(responseData, propertyName) as IDataObject[]);
} else {
returnData.push(get(responseData, propertyName) as IDataObject);
}
}
} while (
get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextMarker']) !== undefined
);
return returnData;
}
@@ -0,0 +1,223 @@
import type { INodeProperties } from 'n8n-workflow';
export const listenerCertificateOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['listenerCertificate'],
},
},
options: [
{
name: 'Add',
value: 'add',
description:
'Add the specified SSL server certificate to the certificate list for the specified HTTPS or TLS listener',
action: 'Add a listener certificate',
},
{
name: 'Get Many',
value: 'getMany',
description: 'Get many listener certificates',
action: 'Get many listener certificates',
},
{
name: 'Remove',
value: 'remove',
description:
'Remove the specified certificate from the certificate list for the specified HTTPS or TLS listener',
action: 'Remove a listener certificate',
},
],
default: 'add',
},
];
export const listenerCertificateFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* listenerCertificate:add */
/* -------------------------------------------------------------------------- */
{
displayName: 'Load Balancer ARN Name or ID',
name: 'loadBalancerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLoadBalancers',
},
required: true,
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['add'],
},
},
default: '',
description:
'Unique identifier for a particular loadBalancer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Listener ARN Name or ID',
name: 'listenerId',
type: 'options',
required: true,
typeOptions: {
loadOptionsMethod: 'getLoadBalancerListeners',
loadOptionsDependsOn: ['loadBalancerId'],
},
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['add'],
},
},
default: '',
description:
'Unique identifier for a particular loadBalancer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Certificate ARN',
name: 'certificateId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['add'],
},
},
default: '',
description: 'Unique identifier for a particular loadBalancer',
},
/* -------------------------------------------------------------------------- */
/* listenerCertificate:getMany */
/* -------------------------------------------------------------------------- */
{
displayName: 'Load Balancer ARN Name or ID',
name: 'loadBalancerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLoadBalancers',
},
required: true,
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['getMany'],
},
},
default: '',
description:
'Unique identifier for a particular loadBalancer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Listener ARN Name or ID',
name: 'listenerId',
type: 'options',
required: true,
typeOptions: {
loadOptionsMethod: 'getLoadBalancerListeners',
loadOptionsDependsOn: ['loadBalancerId'],
},
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['getMany'],
},
},
default: '',
description:
'Unique identifier for a particular loadBalancer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['getMany'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
description: 'Max number of results to return',
default: 100,
typeOptions: {
maxValue: 400,
minValue: 1,
},
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['getMany'],
returnAll: [false],
},
},
},
/* -------------------------------------------------------------------------- */
/* listenerCertificate:remove */
/* -------------------------------------------------------------------------- */
{
displayName: 'Load Balancer ARN Name or ID',
name: 'loadBalancerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLoadBalancers',
},
required: true,
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['remove'],
},
},
default: '',
description:
'Unique identifier for a particular loadBalancer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Listener ARN Name or ID',
name: 'listenerId',
type: 'options',
required: true,
typeOptions: {
loadOptionsMethod: 'getLoadBalancerListeners',
loadOptionsDependsOn: ['loadBalancerId'],
},
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['remove'],
},
},
default: '',
description:
'Unique identifier for a particular loadBalancer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Certificate ARN',
name: 'certificateId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['listenerCertificate'],
operation: ['remove'],
},
},
default: '',
description: 'Unique identifier for a particular loadBalancer',
},
];
@@ -0,0 +1,304 @@
import type { INodeProperties } from 'n8n-workflow';
export const loadBalancerOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a load balancer',
action: 'Create a load balancer',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a load balancer',
action: 'Delete a load balancer',
},
{
name: 'Get',
value: 'get',
description: 'Get a load balancer',
action: 'Get a load balancer',
},
{
name: 'Get Many',
value: 'getMany',
description: 'Get many load balancers',
action: 'Get many load balancers',
},
],
default: 'create',
},
];
export const loadBalancerFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* loadBalancer:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'IP Address Type',
name: 'ipAddressType',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['create'],
},
},
options: [
{
name: 'Ipv4',
value: 'ipv4',
},
{
name: 'Dualstack',
value: 'dualstack',
},
],
default: 'ipv4',
description: 'The type of IP addresses used by the subnets for your load balancer',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['create'],
},
},
default: '',
description:
'This name must be unique per region per account, can have a maximum of 32 characters',
},
{
displayName: 'Schema',
name: 'schema',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['create'],
},
},
options: [
{
name: 'Internal',
value: 'internal',
},
{
name: 'Internet Facing',
value: 'internet-facing',
},
],
default: 'internet-facing',
},
{
displayName: 'Type',
name: 'type',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['create'],
},
},
options: [
{
name: 'Application',
value: 'application',
},
{
name: 'Network',
value: 'network',
},
],
default: 'application',
},
{
displayName: 'Subnet ID Names or IDs',
name: 'subnets',
type: 'multiOptions',
description:
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['create'],
},
},
typeOptions: {
loadOptionsMethod: 'getSubnets',
},
required: true,
default: [],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: ['create'],
resource: ['loadBalancer'],
},
},
default: {},
options: [
{
displayName: 'Security Group IDs',
name: 'securityGroups',
type: 'multiOptions',
description:
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getSecurityGroups',
},
default: [],
},
{
displayName: 'Tags',
name: 'tagsUi',
placeholder: 'Add Tag',
type: 'fixedCollection',
default: {},
typeOptions: {
multipleValues: true,
},
options: [
{
name: 'tagValues',
displayName: 'Tag',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'The key of the tag',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value of the tag',
},
],
},
],
},
],
},
/* -------------------------------------------------------------------------- */
/* loadBalancer:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Load Balancer ARN',
name: 'loadBalancerId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['get'],
},
},
default: '',
description: 'Unique identifier for a particular loadBalancer',
},
/* -------------------------------------------------------------------------- */
/* loadBalancer:getMany */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['getMany'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
description: 'Max number of results to return',
default: 100,
typeOptions: {
maxValue: 400,
minValue: 1,
},
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['getMany'],
returnAll: [false],
},
},
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
displayOptions: {
show: {
operation: ['getMany'],
resource: ['loadBalancer'],
returnAll: [true],
},
},
default: {},
options: [
{
displayName: 'Names',
name: 'names',
type: 'string',
default: '',
description:
'The names of the load balancers. Multiples can be defined separated by comma.',
},
],
},
/* -------------------------------------------------------------------------- */
/* loadBalancer:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Load Balancer ARN',
name: 'loadBalancerId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['loadBalancer'],
operation: ['delete'],
},
},
default: '',
description: 'ID of loadBalancer to delete',
},
];
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 74.375 85"><style>.B{fill:#f58536}.C{fill:#9d5025}</style><use xlink:href="#A" x="2.188" y="2.5"/><symbol id="A" overflow="visible"><g stroke="none"><path d="M3.511 14.898 0 16.56v46.88l3.511 1.662L17.423 40z" class="C"/><path d="m11.694 63.285-8.183 1.817V14.898l8.183 1.769z" class="B"/><path d="m7.382 13.061 4.312-2.031L21.899 40 11.694 68.97l-4.312-2.031z" class="C"/><path d="M21.899 66.239 11.694 68.97V11.03l10.205 2.741z" class="B"/><path d="m16.499 8.747 5.4-2.556L55.616 40 21.899 73.8l-5.4-2.546z" class="C"/><path d="M58.357 61.031 21.899 73.8V6.191l36.458 12.614z" class="B"/><path fill="#6b3a19" d="m53.634 33.693-6.807.418-18.819-1.506L34.99 0l18.645 33.693z"/><path d="M34.99 31.934V0l-6.982 3.304v29.3z" class="C"/><path d="M53.635 33.693V8.824L34.99 0v31.934z" class="B"/><path fill="#fbbf93" d="m53.634 46.094-6.448-.389-19.179 1.536L34.99 80l18.645-33.907z"/><path d="M28.008 47.24v29.465L34.99 80V47.842z" class="C"/><path d="M34.99 47.842V80l18.645-8.824V46.093z" class="B"/><path fill="#fbbf93" d="m70 44.558-4.004-.156-10.38.865 4.394 22.896z"/><path d="M60.01 45.491v22.672L70 63.431V44.558z" class="B"/><path fill="#6b3a19" d="m70 35.248-4.004.155-10.38-.875 4.394-22.692z"/><path d="M70 35.248V16.57l-9.99-4.733v22.459z" class="B"/><path d="m55.616 67.065 4.394 1.098V45.491l-4.394-.224zm0-54.13 4.394-1.098v22.459l-4.394.233z" class="C"/></g></symbol></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,295 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('AWS ELB', () => {
const credentials = {
aws: {
region: 'us-east-1',
accessKeyId: 'test-access-key',
secretAccessKey: 'test-secret-key',
},
};
describe('Load Balancer Operations', () => {
describe('Create Load Balancer', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock
.get(
'/?Action=CreateLoadBalancer&Version=2015-12-01&IpAddressType=ipv4&Name=test-lb&Scheme=internet-facing&Type=application&Subnets.member.1=subnet-12345&Subnets.member.2=subnet-67890&SecurityGroups.member.1=sg-12345&Tags.member.1.Key=Environment&Tags.member.1.Value=test',
)
.reply(
200,
`
<CreateLoadBalancerResponse>
<CreateLoadBalancerResult>
<LoadBalancers>
<member>
<LoadBalancerArn>arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456</LoadBalancerArn>
<LoadBalancerName>test-lb</LoadBalancerName>
<Scheme>internet-facing</Scheme>
<Type>application</Type>
<State>
<Code>provisioning</Code>
</State>
</member>
</LoadBalancers>
</CreateLoadBalancerResult>
</CreateLoadBalancerResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['create-load-balancer.workflow.json'],
});
});
describe('Get Load Balancer', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock
.get(
'/?Action=DescribeLoadBalancers&Version=2015-12-01&LoadBalancerArns.member.1=arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456',
)
.reply(
200,
`
<DescribeLoadBalancersResponse>
<DescribeLoadBalancersResult>
<LoadBalancers>
<member>
<LoadBalancerArn>arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456</LoadBalancerArn>
<LoadBalancerName>test-lb</LoadBalancerName>
<Scheme>internet-facing</Scheme>
<Type>application</Type>
<State>
<Code>active</Code>
</State>
</member>
</LoadBalancers>
</DescribeLoadBalancersResult>
</DescribeLoadBalancersResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['get-load-balancer.workflow.json'],
});
});
describe('Get Many Load Balancers', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock.get('/?Action=DescribeLoadBalancers&Version=2015-12-01&PageSize=10').reply(
200,
`
<DescribeLoadBalancersResponse>
<DescribeLoadBalancersResult>
<LoadBalancers>
<member>
<LoadBalancerArn>arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb-1/1234567890123456</LoadBalancerArn>
<LoadBalancerName>test-lb-1</LoadBalancerName>
<Scheme>internet-facing</Scheme>
<Type>application</Type>
</member>
<member>
<LoadBalancerArn>arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/test-lb-2/2345678901234567</LoadBalancerArn>
<LoadBalancerName>test-lb-2</LoadBalancerName>
<Scheme>internal</Scheme>
<Type>network</Type>
</member>
</LoadBalancers>
</DescribeLoadBalancersResult>
</DescribeLoadBalancersResponse>
`,
);
mock
.get(
'/?Action=DescribeLoadBalancers&Version=2015-12-01&Names.member.1=test-lb-1&Names.member.2=test-lb-2',
)
.reply(
200,
`
<DescribeLoadBalancersResponse>
<DescribeLoadBalancersResult>
<LoadBalancers>
<member>
<LoadBalancerArn>arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb-1/1234567890123456</LoadBalancerArn>
<LoadBalancerName>test-lb-1</LoadBalancerName>
<Scheme>internet-facing</Scheme>
<Type>application</Type>
</member>
<member>
<LoadBalancerArn>arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/test-lb-2/2345678901234567</LoadBalancerArn>
<LoadBalancerName>test-lb-2</LoadBalancerName>
<Scheme>internal</Scheme>
<Type>network</Type>
</member>
</LoadBalancers>
</DescribeLoadBalancersResult>
</DescribeLoadBalancersResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: [
'get-many-load-balancers.workflow.json',
'get-many-load-balancers-with-names.workflow.json',
],
});
});
describe('Delete Load Balancer', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock
.get(
'/?Action=DeleteLoadBalancer&Version=2015-12-01&LoadBalancerArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456',
)
.reply(
200,
`
<DeleteLoadBalancerResponse>
<DeleteLoadBalancerResult/>
</DeleteLoadBalancerResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['delete-load-balancer.workflow.json'],
});
});
});
describe('Listener Certificate Operations', () => {
describe('Add Listener Certificate', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock
.get(
'/?Action=AddListenerCertificates&Version=2015-12-01&Certificates.member.1.CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012&ListenerArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2',
)
.reply(
200,
`
<AddListenerCertificatesResponse>
<AddListenerCertificatesResult>
<Certificates>
<member>
<CertificateArn>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</CertificateArn>
<IsDefault>false</IsDefault>
</member>
</Certificates>
</AddListenerCertificatesResult>
</AddListenerCertificatesResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['add-listener-certificate.workflow.json'],
});
});
describe('Get Many Listener Certificates', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock
.get(
'/?Action=DescribeListenerCertificates&Version=2015-12-01&ListenerArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2&PageSize=10',
)
.reply(
200,
`
<DescribeListenerCertificatesResponse>
<DescribeListenerCertificatesResult>
<Certificates>
<member>
<CertificateArn>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</CertificateArn>
<IsDefault>false</IsDefault>
</member>
<member>
<CertificateArn>arn:aws:acm:us-east-1:123456789012:certificate/87654321-4321-4321-4321-210987654321</CertificateArn>
<IsDefault>true</IsDefault>
</member>
</Certificates>
</DescribeListenerCertificatesResult>
</DescribeListenerCertificatesResponse>
`,
);
mock
.get(
'/?Action=DescribeListenerCertificates&Version=2015-12-01&ListenerArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2',
)
.reply(
200,
`
<DescribeListenerCertificatesResponse>
<DescribeListenerCertificatesResult>
<Certificates>
<member>
<CertificateArn>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</CertificateArn>
<IsDefault>false</IsDefault>
</member>
<member>
<CertificateArn>arn:aws:acm:us-east-1:123456789012:certificate/87654321-4321-4321-4321-210987654321</CertificateArn>
<IsDefault>true</IsDefault>
</member>
</Certificates>
</DescribeListenerCertificatesResult>
</DescribeListenerCertificatesResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: [
'get-many-listener-certificates.workflow.json',
'get-many-listener-certificates-all.workflow.json',
],
});
});
describe('Remove Listener Certificate', () => {
beforeAll(() => {
const mock = nock('https://elasticloadbalancing.us-east-1.amazonaws.com');
mock
.get(
'/?Action=RemoveListenerCertificates&Version=2015-12-01&Certificates.member.1.CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012&ListenerArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2',
)
.reply(
200,
`
<RemoveListenerCertificatesResponse>
<RemoveListenerCertificatesResult/>
</RemoveListenerCertificatesResponse>
`,
);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['remove-listener-certificate.workflow.json'],
});
});
});
});
@@ -0,0 +1,289 @@
import { mockDeep } from 'jest-mock-extended';
import type { IExecuteFunctions } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
import {
awsApiRequest,
awsApiRequestREST,
awsApiRequestSOAP,
awsApiRequestSOAPAllItems,
} from '../GenericFunctions';
jest.mock('xml2js', () => ({
parseString: jest.fn(),
}));
import { parseString as parseXml } from 'xml2js';
describe('ELB GenericFunctions', () => {
describe('awsApiRequest', () => {
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
jest.clearAllMocks();
mockExecuteFunctions.getNode.mockReturnValue({
id: 'test-node',
name: 'Test ELB Node',
type: 'n8n-nodes-base.awsElb',
typeVersion: 1,
position: [0, 0],
parameters: {},
});
});
it('should make successful API request with basic parameters', async () => {
const mockResponse = { success: true };
const mockRequestWithAuth = mockExecuteFunctions.helpers
.requestWithAuthentication as jest.Mock;
mockRequestWithAuth.mockResolvedValue(mockResponse);
const result = await awsApiRequest.call(
mockExecuteFunctions,
'elasticloadbalancing',
'GET',
'/test-path',
);
expect(result).toEqual(mockResponse);
expect(mockRequestWithAuth).toHaveBeenCalledWith(
'aws',
expect.objectContaining({
qs: expect.objectContaining({
service: 'elasticloadbalancing',
path: '/test-path',
}),
method: 'GET',
}),
);
});
it('should handle API errors', async () => {
const apiError = new Error('API Error');
const mockRequestWithAuth = mockExecuteFunctions.helpers
.requestWithAuthentication as jest.Mock;
mockRequestWithAuth.mockRejectedValue(apiError);
await expect(
awsApiRequest.call(mockExecuteFunctions, 'elasticloadbalancing', 'GET', '/test-path'),
).rejects.toThrow(NodeApiError);
});
});
describe('awsApiRequestREST', () => {
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
jest.clearAllMocks();
});
it('should parse valid JSON response', async () => {
const jsonResponse = '{"result": "success", "data": [1,2,3]}';
const expectedResult = { result: 'success', data: [1, 2, 3] };
const mockRequestWithAuth = mockExecuteFunctions.helpers
.requestWithAuthentication as jest.Mock;
mockRequestWithAuth.mockResolvedValue(jsonResponse);
const result = await awsApiRequestREST.call(
mockExecuteFunctions,
'elasticloadbalancing',
'GET',
'/test-path',
);
expect(result).toEqual(expectedResult);
});
it('should return raw response when JSON parsing fails', async () => {
const rawResponse = 'not json data';
const mockRequestWithAuth = mockExecuteFunctions.helpers
.requestWithAuthentication as jest.Mock;
mockRequestWithAuth.mockResolvedValue(rawResponse);
const result = await awsApiRequestREST.call(
mockExecuteFunctions,
'elasticloadbalancing',
'GET',
'/test-path',
);
expect(result).toBe(rawResponse);
});
});
describe('awsApiRequestSOAP', () => {
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
const mockParseXml = parseXml as jest.MockedFunction<typeof parseXml>;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
jest.clearAllMocks();
});
it('should parse XML response correctly', async () => {
const xmlResponse =
'<ListBucketsResult><Buckets><Bucket><Name>test-bucket</Name></Bucket></Buckets></ListBucketsResult>';
const expectedParsedData = {
ListBucketsResult: {
Buckets: {
Bucket: {
Name: 'test-bucket',
},
},
},
};
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
xmlResponse,
);
mockParseXml.mockImplementation((_xml, _options, callback) => {
callback(null, expectedParsedData);
});
const result = await awsApiRequestSOAP.call(
mockExecuteFunctions,
'elasticloadbalancing',
'GET',
'/test-path',
);
expect(result).toEqual(expectedParsedData);
});
it('should handle XML parsing errors', async () => {
const xmlResponse = 'invalid xml';
const xmlError = new Error('Invalid XML');
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
xmlResponse,
);
mockParseXml.mockImplementation((_xml, _options, callback) => {
callback(xmlError, null);
});
const result = await awsApiRequestSOAP.call(
mockExecuteFunctions,
'elasticloadbalancing',
'GET',
'/test-path',
);
expect(result).toBeInstanceOf(Error);
});
});
describe('awsApiRequestSOAPAllItems', () => {
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
const mockParseXml = parseXml as jest.MockedFunction<typeof parseXml>;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
jest.clearAllMocks();
});
describe('pagination patterns', () => {
it('should handle DescribeLoadBalancers pagination', async () => {
const mockCredentials = { region: 'us-east-1' };
const xmlResponse = {
DescribeLoadBalancersResponse: {
DescribeLoadBalancersResult: {
LoadBalancerDescriptions: [
{ LoadBalancerName: 'elb-1' },
{ LoadBalancerName: 'elb-2' },
],
},
},
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
'<xml>response</xml>',
);
mockParseXml.mockImplementation((_xml, _options, callback) => {
callback(null, xmlResponse);
});
const result = await awsApiRequestSOAPAllItems.call(
mockExecuteFunctions,
'DescribeLoadBalancersResponse.DescribeLoadBalancersResult.LoadBalancerDescriptions',
'elasticloadbalancing',
'POST',
'/describe-load-balancers',
);
expect(result).toEqual([{ LoadBalancerName: 'elb-1' }, { LoadBalancerName: 'elb-2' }]);
});
it('should handle DescribeTargetGroups with nested properties', async () => {
const mockCredentials = { region: 'us-east-1' };
const firstResponse = {
DescribeTargetGroupsResponse: {
DescribeTargetGroupsResult: {
TargetGroups: [
{ TargetGroupName: 'tg-1', Protocol: 'HTTP' },
{ TargetGroupName: 'tg-2', Protocol: 'HTTPS' },
],
NextMarker: 'marker123',
},
},
};
const secondResponse = {
DescribeTargetGroupsResponse: {
DescribeTargetGroupsResult: {
TargetGroups: [{ TargetGroupName: 'tg-3', Protocol: 'TCP' }],
},
},
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock)
.mockResolvedValueOnce('<xml>first-response</xml>')
.mockResolvedValueOnce('<xml>second-response</xml>');
mockParseXml
.mockImplementationOnce((_xml, _options, callback) => callback(null, firstResponse))
.mockImplementationOnce((_xml, _options, callback) => callback(null, secondResponse));
const result = await awsApiRequestSOAPAllItems.call(
mockExecuteFunctions,
'DescribeTargetGroupsResponse.DescribeTargetGroupsResult.TargetGroups',
'elasticloadbalancing',
'POST',
'/describe-target-groups',
);
expect(result).toEqual([
{ TargetGroupName: 'tg-1', Protocol: 'HTTP' },
{ TargetGroupName: 'tg-2', Protocol: 'HTTPS' },
{ TargetGroupName: 'tg-3', Protocol: 'TCP' },
]);
});
});
describe('error handling', () => {
it('should handle service error responses', async () => {
const mockCredentials = { region: 'us-east-1' };
const elbError = new Error(
'LoadBalancerNotFound: The specified load balancer does not exist',
);
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
elbError,
);
await expect(
awsApiRequestSOAPAllItems.call(
mockExecuteFunctions,
'DescribeLoadBalancersResponse.DescribeLoadBalancersResult.LoadBalancerDescriptions',
'elasticloadbalancing',
'POST',
'/describe-load-balancers',
),
).rejects.toThrow('LoadBalancerNotFound: The specified load balancer does not exist');
});
});
});
});
@@ -0,0 +1,29 @@
# AWS ELB Test Implementation Notes
This document explains intentional test data adjustments made to match the current implementation behavior.
## Known Implementation Issues
### 1. Typo in Remove Listener Certificate Operation
**File**: `remove-listener-certificate.workflow.json`
**Issue**: The response contains `"sucess": true` instead of `"success": true`
**Root Cause**: Spelling error in the node implementation
**Test Adjustment**: The pinData uses the misspelled version to match actual output
**TODO**: Fix the typo in the AWS ELB node implementation and update test accordingly
### 2. Boolean Values Returned as Strings
**File**: `get-many-listener-certificates-all.workflow.json`
**Issue**: Boolean values like `IsDefault` are returned as strings ("true"/"false") instead of actual booleans
**Root Cause**: AWS API response parsing returns string values rather than converting to boolean types
**Test Adjustment**: The pinData uses string values ("true"/"false") instead of boolean values (true/false)
**Note**: This reflects how the AWS API response is actually parsed and returned by the node
## Test Data Maintenance
When updating these test files:
1. Verify the actual node implementation output before changing expected results
2. If implementation bugs are fixed, update both the node code and corresponding test data
3. Consider backward compatibility when making changes that affect output format
@@ -0,0 +1,60 @@
{
"name": "AWS ELB Add Listener Certificate Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "listenerCertificate",
"operation": "add",
"loadBalancerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456",
"listenerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2",
"certificateId": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Add Listener Certificate",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Add Listener Certificate": [
{
"json": {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012",
"IsDefault": "false"
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Add Listener Certificate",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,78 @@
{
"name": "AWS ELB Create Load Balancer Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "loadBalancer",
"operation": "create",
"ipAddressType": "ipv4",
"name": "test-lb",
"schema": "internet-facing",
"type": "application",
"subnets": ["subnet-12345", "subnet-67890"],
"additionalFields": {
"securityGroups": ["sg-12345"],
"tagsUi": {
"tagValues": [
{
"key": "Environment",
"value": "test"
}
]
}
}
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Create Load Balancer",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Create Load Balancer": [
{
"json": {
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456",
"LoadBalancerName": "test-lb",
"Scheme": "internet-facing",
"Type": "application",
"State": {
"Code": "provisioning"
}
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Create Load Balancer",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,57 @@
{
"name": "AWS ELB Delete Load Balancer Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "loadBalancer",
"operation": "delete",
"loadBalancerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456"
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Delete Load Balancer",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Delete Load Balancer": [
{
"json": {
"success": true
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Delete Load Balancer",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,63 @@
{
"name": "AWS ELB Get Load Balancer Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "loadBalancer",
"operation": "get",
"loadBalancerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456"
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Get Load Balancer",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Get Load Balancer": [
{
"json": {
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456",
"LoadBalancerName": "test-lb",
"Scheme": "internet-facing",
"Type": "application",
"State": {
"Code": "active"
}
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Get Load Balancer",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,66 @@
{
"name": "AWS ELB Get Many Listener Certificates All Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "listenerCertificate",
"operation": "getMany",
"loadBalancerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456",
"listenerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2",
"returnAll": true
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Get Many Listener Certificates All",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Get Many Listener Certificates All": [
{
"json": {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012",
"IsDefault": "false"
}
},
{
"json": {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/87654321-4321-4321-4321-210987654321",
"IsDefault": "true"
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Get Many Listener Certificates All",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,67 @@
{
"name": "AWS ELB Get Many Listener Certificates Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "listenerCertificate",
"operation": "getMany",
"loadBalancerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456",
"listenerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2",
"returnAll": false,
"limit": 10
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Get Many Listener Certificates",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Get Many Listener Certificates": [
{
"json": {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012",
"IsDefault": "false"
}
},
{
"json": {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/87654321-4321-4321-4321-210987654321",
"IsDefault": "true"
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Get Many Listener Certificates",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,71 @@
{
"name": "AWS ELB Get Many Load Balancers with Names Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "loadBalancer",
"operation": "getMany",
"returnAll": true,
"filters": {
"names": "test-lb-1,test-lb-2"
}
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Get Many Load Balancers with Names",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Get Many Load Balancers with Names": [
{
"json": {
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb-1/1234567890123456",
"LoadBalancerName": "test-lb-1",
"Scheme": "internet-facing",
"Type": "application"
}
},
{
"json": {
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/test-lb-2/2345678901234567",
"LoadBalancerName": "test-lb-2",
"Scheme": "internal",
"Type": "network"
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Get Many Load Balancers with Names",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,69 @@
{
"name": "AWS ELB Get Many Load Balancers Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "loadBalancer",
"operation": "getMany",
"returnAll": false,
"limit": 10
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Get Many Load Balancers",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Get Many Load Balancers": [
{
"json": {
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb-1/1234567890123456",
"LoadBalancerName": "test-lb-1",
"Scheme": "internet-facing",
"Type": "application"
}
},
{
"json": {
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/test-lb-2/2345678901234567",
"LoadBalancerName": "test-lb-2",
"Scheme": "internal",
"Type": "network"
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Get Many Load Balancers",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,59 @@
{
"name": "AWS ELB Remove Listener Certificate Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"resource": "listenerCertificate",
"operation": "remove",
"loadBalancerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-lb/1234567890123456",
"listenerId": "arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/test-lb/1234567890123456/f2f7dc8efc522ab2",
"certificateId": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
},
"type": "n8n-nodes-base.awsElb",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Remove Listener Certificate",
"credentials": {
"aws": {
"id": "credential-id",
"name": "Test AWS Credentials"
}
}
}
],
"pinData": {
"Remove Listener Certificate": [
{
"json": {
"sucess": true
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Remove Listener Certificate",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}