mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(n8n AWS Cognito Node): New node (#11767)
Co-authored-by: Stamsy <stams_89@abv.bg> Co-authored-by: Adina Totorean <adinatotorean99@gmail.com> Co-authored-by: Giulio Andreini <g.andreini@gmail.com> Co-authored-by: AdinaTotorean <64439268+adina-hub@users.noreply.github.com> Co-authored-by: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com> Co-authored-by: feelgood-interface <feelgood.interface@gmail.com>
This commit is contained in:
@@ -436,8 +436,6 @@ export class Aws implements ICredentialType {
|
||||
endpointString = credentials.sesEndpoint;
|
||||
} else if (service === 'rekognition' && credentials.rekognitionEndpoint) {
|
||||
endpointString = credentials.rekognitionEndpoint;
|
||||
} else if (service === 'sqs' && credentials.sqsEndpoint) {
|
||||
endpointString = credentials.sqsEndpoint;
|
||||
} else if (service) {
|
||||
endpointString = `https://${service}.${region}.amazonaws.com`;
|
||||
} else if (service === 'ssm' && credentials.ssmEndpoint) {
|
||||
|
||||
18
packages/nodes-base/nodes/Aws/Cognito/AwsCognito.node.json
Normal file
18
packages/nodes-base/nodes/Aws/Cognito/AwsCognito.node.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.awsCognito",
|
||||
"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.awscognito/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
76
packages/nodes-base/nodes/Aws/Cognito/AwsCognito.node.ts
Normal file
76
packages/nodes-base/nodes/Aws/Cognito/AwsCognito.node.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { INodeType, INodeTypeDescription } from 'n8n-workflow';
|
||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||
|
||||
import { group, user, userPool } from './descriptions';
|
||||
import { preSendStringifyBody } from './helpers/utils';
|
||||
import { listSearch } from './methods';
|
||||
|
||||
export class AwsCognito implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'AWS Cognito',
|
||||
name: 'awsCognito',
|
||||
icon: {
|
||||
light: 'file:cognito.svg',
|
||||
dark: 'file:cognito.svg',
|
||||
},
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Sends data to AWS Cognito',
|
||||
defaults: {
|
||||
name: 'AWS Cognito',
|
||||
},
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'aws',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
requestDefaults: {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-amz-json-1.1',
|
||||
},
|
||||
qs: {
|
||||
service: 'cognito-idp',
|
||||
_region: '={{$credentials.region}}',
|
||||
},
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
default: 'user',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendStringifyBody],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Group',
|
||||
value: 'group',
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: 'user',
|
||||
},
|
||||
{
|
||||
name: 'User Pool',
|
||||
value: 'userPool',
|
||||
},
|
||||
],
|
||||
},
|
||||
...group.description,
|
||||
...user.description,
|
||||
...userPool.description,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
listSearch,
|
||||
};
|
||||
}
|
||||
1
packages/nodes-base/nodes/Aws/Cognito/cognito.svg
Normal file
1
packages/nodes-base/nodes/Aws/Cognito/cognito.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="2140" height="2500" viewBox="0 0 256 299" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M208.752 58.061l25.771-6.636.192.283.651 155.607-.843.846-5.31.227-20.159-3.138-.302-.794V58.061M59.705 218.971l.095.007 68.027 19.767.173.133.296.236-.096 59.232-.2.252-68.295-33.178v-46.449" fill="#7A3E65"/><path d="M208.752 204.456l-80.64 19.312-40.488-9.773-27.919 4.976L128 238.878l105.405-28.537 1.118-2.18-25.771-3.705" fill="#CFB2C1"/><path d="M196.295 79.626l-.657-.749-66.904-19.44-.734.283-.672-.343L22.052 89.734l-.575.703.845.463 24.075 3.53.851-.289 80.64-19.311 40.488 9.773 27.919-4.977" fill="#512843"/><path d="M47.248 240.537l-25.771 6.221-.045-.149-1.015-155.026 1.06-1.146 25.771 3.704v146.396" fill="#C17B9E"/><path d="M82.04 180.403l45.96 5.391.345-.515.187-71.887-.532-.589-45.96 5.392v62.208" fill="#7A3E65"/><path d="M173.96 180.403L128 185.794v-72.991l45.96 5.392v62.208M196.295 79.626L128 59.72V0l68.295 33.177v46.449" fill="#C17B9E"/><path d="M128 0L0 61.793v175.011l21.477 9.954V90.437L128 59.72V0" fill="#7A3E65"/><path d="M234.523 51.425v156.736L128 238.878v59.72l128-61.794V61.793l-21.477-10.368" fill="#C17B9E"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,132 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const userPoolResourceLocator: INodeProperties = {
|
||||
displayName: 'User Pool',
|
||||
name: 'userPool',
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'UserPoolId',
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchUserPools',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
|
||||
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. eu-central-1_ab12cdefgh',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const groupResourceLocator: INodeProperties = {
|
||||
displayName: 'Group',
|
||||
name: 'group',
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'GroupName',
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchGroups',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'groupName',
|
||||
type: 'string',
|
||||
hint: 'Enter the group name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The group name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. Admins',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
};
|
||||
|
||||
export const userResourceLocator: INodeProperties = {
|
||||
displayName: 'User',
|
||||
name: 'user',
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchUsers',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
hint: 'Enter the user ID',
|
||||
placeholder: 'e.g. 02bd9fd6-8f93-4758-87c3-1fb73740a315',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w-]+-[0-9a-zA-Z]+$',
|
||||
errorMessage: 'The ID must follow the pattern "xxxxxx-xxxxxxxxxxx"',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Username',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
type: 'resourceLocator',
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as update from './update.operation';
|
||||
import { handleError } from '../../helpers/errorHandler';
|
||||
import { processGroup } from '../../helpers/utils';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
default: 'getAll',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['group'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new group',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.CreateGroup',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Create group',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete an existing group',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.DeleteGroup',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'set',
|
||||
properties: {
|
||||
value: '={{ { "deleted": true } }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Delete group',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve details of an existing group',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.GetGroup',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [handleError, processGroup],
|
||||
},
|
||||
},
|
||||
action: 'Get group',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Retrieve a list of groups',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.ListGroups',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
processGroup,
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'Groups',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Get many groups',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an existing group',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.UpdateGroup',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'set',
|
||||
properties: {
|
||||
value: '={{ { "updated": true } }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Update group',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,106 @@
|
||||
import type { IExecuteSingleFunctions, IHttpRequestOptions, INodeProperties } from 'n8n-workflow';
|
||||
import { NodeApiError, updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { validateArn } from '../../helpers/utils';
|
||||
import { userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
displayName: 'Group Name',
|
||||
name: 'newGroupName',
|
||||
default: '',
|
||||
placeholder: 'e.g. MyNewGroup',
|
||||
description: 'The name of the new group to create',
|
||||
required: true,
|
||||
type: 'string',
|
||||
validateType: 'string',
|
||||
routing: {
|
||||
send: {
|
||||
property: 'GroupName',
|
||||
type: 'body',
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const newGroupName = this.getNodeParameter('newGroupName', '') as string;
|
||||
const groupNameRegex = /^[\p{L}\p{M}\p{S}\p{N}\p{P}]+$/u;
|
||||
if (!groupNameRegex.test(newGroupName)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'Invalid format for Group Name',
|
||||
description: 'Group Name should not contain spaces.',
|
||||
});
|
||||
}
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
default: '',
|
||||
placeholder: 'e.g. New group description',
|
||||
description: 'A description for the new group',
|
||||
type: 'string',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Description',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Precedence',
|
||||
name: 'precedence',
|
||||
default: '',
|
||||
placeholder: 'e.g. 10',
|
||||
description: 'Precedence value for the group. Lower values indicate higher priority.',
|
||||
type: 'number',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Precedence',
|
||||
},
|
||||
},
|
||||
validateType: 'number',
|
||||
},
|
||||
{
|
||||
displayName: 'Role ARN',
|
||||
name: 'arn',
|
||||
default: '',
|
||||
placeholder: 'e.g. arn:aws:iam::123456789012:role/GroupRole',
|
||||
description: 'The role ARN for the group, used for setting claims in tokens',
|
||||
type: 'string',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Arn',
|
||||
preSend: [validateArn],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Option',
|
||||
type: 'collection',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['group'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { groupResourceLocator, userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...groupResourceLocator,
|
||||
description: 'Select the group you want to delete',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['group'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { groupResourceLocator, userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...groupResourceLocator,
|
||||
description: 'Select the group you want to retrieve',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Users',
|
||||
name: 'includeUsers',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to include a list of users in the group',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['group'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,72 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
type: 'boolean',
|
||||
routing: {
|
||||
operations: {
|
||||
pagination: {
|
||||
type: 'generic',
|
||||
properties: {
|
||||
continue: '={{ !!$response.body?.NextToken }}',
|
||||
request: {
|
||||
body: {
|
||||
NextToken: '={{ $response.body?.NextToken }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
required: true,
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 60,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
displayOptions: {
|
||||
show: {
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Limit',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Include Users',
|
||||
name: 'includeUsers',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to include a list of users in the group',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['group'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,107 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
IHttpRequestOptions,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError, updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { validateArn } from '../../helpers/utils';
|
||||
import { groupResourceLocator, userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...groupResourceLocator,
|
||||
description: 'Select the group you want to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
placeholder: 'Add Option',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', {}) as IDataObject;
|
||||
const arn = additionalFields.arn as string | undefined;
|
||||
const description = additionalFields.description as string | undefined;
|
||||
const precedence = additionalFields.precedence as number | undefined;
|
||||
if (!description && !precedence && !arn) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'At least one field must be provided for update.',
|
||||
description: 'Please provide a value for Description, Precedence, or Role ARN.',
|
||||
});
|
||||
}
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
default: '',
|
||||
placeholder: 'e.g. Updated group description',
|
||||
description: 'A new description for the group',
|
||||
type: 'string',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Description',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Precedence',
|
||||
name: 'precedence',
|
||||
default: '',
|
||||
placeholder: 'e.g. 10',
|
||||
description:
|
||||
'The new precedence value for the group. Lower values indicate higher priority.',
|
||||
type: 'number',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Precedence',
|
||||
},
|
||||
},
|
||||
validateType: 'number',
|
||||
},
|
||||
{
|
||||
displayName: 'Role ARN',
|
||||
name: 'arn',
|
||||
default: '',
|
||||
placeholder: 'e.g. arn:aws:iam::123456789012:role/GroupRole',
|
||||
description:
|
||||
'A new role Amazon Resource Name (ARN) for the group. Used for setting claims in tokens.',
|
||||
type: 'string',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Arn',
|
||||
preSend: [validateArn],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['group'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,3 @@
|
||||
export * as group from './group/Group.resource';
|
||||
export * as user from './user/User.resource';
|
||||
export * as userPool from './userPool/UserPool.resource';
|
||||
@@ -0,0 +1,229 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as addToGroup from './addToGroup.operation';
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as removeFromGroup from './removeFromGroup.operation';
|
||||
import * as update from './update.operation';
|
||||
import { handleError } from '../../helpers/errorHandler';
|
||||
import { preSendUserFields, simplifyUser } from '../../helpers/utils';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
default: 'getAll',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add to Group',
|
||||
value: 'addToGroup',
|
||||
description: 'Add an existing user to a group',
|
||||
action: 'Add user to group',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendUserFields],
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminAddUserToGroup',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'set',
|
||||
properties: {
|
||||
value: '={{ { "addedToGroup": true } }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new user',
|
||||
action: 'Create user',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendUserFields],
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminCreateUser',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'User',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a user',
|
||||
action: 'Delete user',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendUserFields],
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminDeleteUser',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'set',
|
||||
properties: {
|
||||
value: '={{ { "deleted": true } }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve information of an existing user',
|
||||
action: 'Get user',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendUserFields],
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminGetUser',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [handleError, simplifyUser],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Retrieve a list of users',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.ListUsers',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
simplifyUser,
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'Users',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Get many users',
|
||||
},
|
||||
{
|
||||
name: 'Remove From Group',
|
||||
value: 'removeFromGroup',
|
||||
description: 'Remove a user from a group',
|
||||
action: 'Remove user from group',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendUserFields],
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminRemoveUserFromGroup',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'set',
|
||||
properties: {
|
||||
value: '={{ { "removedFromGroup": true } }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an existing user',
|
||||
action: 'Update user',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendUserFields],
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminUpdateUserAttributes',
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
handleError,
|
||||
{
|
||||
type: 'set',
|
||||
properties: {
|
||||
value: '={{ { "updated": true } }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
...addToGroup.description,
|
||||
...removeFromGroup.description,
|
||||
];
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
groupResourceLocator,
|
||||
userPoolResourceLocator,
|
||||
userResourceLocator,
|
||||
} from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...userResourceLocator,
|
||||
description: 'Select the user you want to add to the group',
|
||||
},
|
||||
{
|
||||
...groupResourceLocator,
|
||||
description: 'Select the group you want to add the user to',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['addToGroup'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,280 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { preSendAttributes, preSendDesiredDeliveryMediums } from '../../helpers/utils';
|
||||
import { userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to retrieve',
|
||||
},
|
||||
{
|
||||
displayName: 'User Name',
|
||||
name: 'newUserName',
|
||||
default: '',
|
||||
description:
|
||||
'Depending on the user pool settings, this parameter requires the username, the email, or the phone number. No whitespace is allowed.',
|
||||
placeholder: 'e.g. JohnSmith',
|
||||
required: true,
|
||||
routing: {
|
||||
send: {
|
||||
property: 'Username',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
type: 'string',
|
||||
validateType: 'string',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Message Action',
|
||||
name: 'messageAction',
|
||||
default: 'RESEND',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Resend',
|
||||
value: 'RESEND',
|
||||
description:
|
||||
"Resend the invitation message to a user that already exists and reset the expiration limit on the user's account",
|
||||
},
|
||||
{
|
||||
name: 'Suppress',
|
||||
value: 'SUPPRESS',
|
||||
description: 'Suppress sending the message',
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
property: 'MessageAction',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Force Alias Creation',
|
||||
name: 'forceAliasCreation',
|
||||
type: 'boolean',
|
||||
validateType: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether this parameter is used only if the phone_number_verified or email_verified attribute is set to true. Otherwise, it is ignored. If set to true, and the phone number or email address specified in the UserAttributes parameter already exists as an alias with a different user, the alias will be migrated. If set to false, an AliasExistsException error is thrown if the alias already exists.',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'ForceAliasCreation',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'User Attributes',
|
||||
name: 'userAttributes',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Attribute',
|
||||
default: {
|
||||
attributes: [],
|
||||
},
|
||||
required: true,
|
||||
description: 'Attributes to update for the user',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendAttributes],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attributes',
|
||||
name: 'attributes',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Attribute Type',
|
||||
name: 'attributeType',
|
||||
type: 'options',
|
||||
default: 'standard',
|
||||
options: [
|
||||
{
|
||||
name: 'Standard Attribute',
|
||||
value: 'standard',
|
||||
},
|
||||
{
|
||||
name: 'Custom Attribute',
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Standard Attribute',
|
||||
name: 'standardName',
|
||||
type: 'options',
|
||||
default: 'address',
|
||||
options: [
|
||||
{
|
||||
name: 'Address',
|
||||
value: 'address',
|
||||
},
|
||||
{
|
||||
name: 'Birthdate',
|
||||
value: 'birthdate',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
},
|
||||
{
|
||||
name: 'Email Verified',
|
||||
value: 'email_verified',
|
||||
},
|
||||
{
|
||||
name: 'Family Name',
|
||||
value: 'family_name',
|
||||
},
|
||||
{
|
||||
name: 'Gender',
|
||||
value: 'gender',
|
||||
},
|
||||
{
|
||||
name: 'Given Name',
|
||||
value: 'given_name',
|
||||
},
|
||||
{
|
||||
name: 'Locale',
|
||||
value: 'locale',
|
||||
},
|
||||
{
|
||||
name: 'Middle Name',
|
||||
value: 'middle_name',
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
name: 'Nickname',
|
||||
value: 'nickname',
|
||||
},
|
||||
{
|
||||
name: 'Phone Number',
|
||||
value: 'phone_number',
|
||||
},
|
||||
{
|
||||
name: 'Phone Number Verified',
|
||||
value: 'phone_number_verified',
|
||||
},
|
||||
{
|
||||
name: 'Preferred Username',
|
||||
value: 'preferred_username',
|
||||
},
|
||||
{
|
||||
name: 'Profile Picture',
|
||||
value: 'profilepicture',
|
||||
},
|
||||
{
|
||||
name: 'Updated At',
|
||||
value: 'updated_at',
|
||||
},
|
||||
{
|
||||
name: 'User Sub',
|
||||
value: 'sub',
|
||||
},
|
||||
{
|
||||
name: 'Website',
|
||||
value: 'website',
|
||||
},
|
||||
{
|
||||
name: 'Zone Info',
|
||||
value: 'zoneinfo',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
attributeType: ['standard'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attribute Name',
|
||||
name: 'customName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'custom:myAttribute',
|
||||
description: 'The name of the custom attribute (must start with "custom:")',
|
||||
displayOptions: {
|
||||
show: {
|
||||
attributeType: ['custom'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value of the attribute',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Desired Delivery Mediums',
|
||||
name: 'desiredDeliveryMediums',
|
||||
default: ['SMS'],
|
||||
description: 'Specify how to send the welcome message',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'SMS',
|
||||
value: 'SMS',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'EMAIL',
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendDesiredDeliveryMediums],
|
||||
property: 'DesiredDeliveryMediums',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Temporary Password',
|
||||
name: 'temporaryPasswordOptions',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
"The user's temporary password that will be valid only once. If not set, Amazon Cognito will automatically generate one for you.",
|
||||
routing: {
|
||||
send: {
|
||||
property: 'TemporaryPassword',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { userPoolResourceLocator, userResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...userResourceLocator,
|
||||
description: 'Select the user you want to delete',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { userPoolResourceLocator, userResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...userResourceLocator,
|
||||
description: 'Select the user you want to retrieve',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to return a simplified version of the response instead of the raw data',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,174 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
IHttpRequestOptions,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { jsonParse, updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import type { Filters } from '../../helpers/interfaces';
|
||||
import { userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
type: 'boolean',
|
||||
routing: {
|
||||
operations: {
|
||||
pagination: {
|
||||
type: 'generic',
|
||||
properties: {
|
||||
continue: '={{ !!$response.body?.PaginationToken }}',
|
||||
|
||||
request: {
|
||||
body: {
|
||||
PaginationToken: '={{ $response.body?.PaginationToken }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
required: true,
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 60,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
displayOptions: {
|
||||
show: {
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'Limit',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to return a simplified version of the response instead of the raw data',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const filters = this.getNodeParameter('filters', {}) as Filters;
|
||||
const filter = filters.filter;
|
||||
if (!filter?.value) return requestOptions;
|
||||
const { attribute: filterAttribute, value: filterValue } = filter;
|
||||
const body = jsonParse<IDataObject>(String(requestOptions.body), {
|
||||
acceptJSObject: true,
|
||||
errorMessage: 'Invalid request body. Request body must be valid JSON.',
|
||||
});
|
||||
const filterString = filterAttribute ? `"${filterAttribute}"^="${filterValue}"` : '';
|
||||
return {
|
||||
...requestOptions,
|
||||
body: JSON.stringify({ ...body, Filter: filterString }),
|
||||
};
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'filter',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Attribute',
|
||||
name: 'attribute',
|
||||
type: 'options',
|
||||
default: 'email',
|
||||
description: 'The attribute to search for',
|
||||
options: [
|
||||
{
|
||||
name: 'Cognito User Status',
|
||||
value: 'cognito:user_status',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
},
|
||||
{
|
||||
name: 'Family Name',
|
||||
value: 'family_name',
|
||||
},
|
||||
{
|
||||
name: 'Given Name',
|
||||
value: 'given_name',
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
name: 'Phone Number',
|
||||
value: 'phone_number',
|
||||
},
|
||||
{
|
||||
name: 'Preferred Username',
|
||||
value: 'preferred_username',
|
||||
},
|
||||
{
|
||||
name: 'Status (Enabled)',
|
||||
value: 'status',
|
||||
},
|
||||
{
|
||||
name: 'Sub',
|
||||
value: 'sub',
|
||||
},
|
||||
{
|
||||
name: 'Username',
|
||||
value: 'username',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value of the attribute to search for',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
groupResourceLocator,
|
||||
userPoolResourceLocator,
|
||||
userResourceLocator,
|
||||
} from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
{
|
||||
...userResourceLocator,
|
||||
description: 'Select the user you want to remove from the group',
|
||||
},
|
||||
{
|
||||
...groupResourceLocator,
|
||||
description: 'Select the group you want to remove the user from',
|
||||
modes: groupResourceLocator.modes?.map((mode) =>
|
||||
mode.name === 'list'
|
||||
? {
|
||||
...mode,
|
||||
typeOptions: {
|
||||
...mode.typeOptions,
|
||||
searchListMethod: 'searchGroupsForUser',
|
||||
},
|
||||
}
|
||||
: mode,
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['removeFromGroup'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,166 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { preSendAttributes } from '../../helpers/utils';
|
||||
import { userPoolResourceLocator, userResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to use',
|
||||
},
|
||||
userResourceLocator,
|
||||
{
|
||||
displayName: 'User Attributes',
|
||||
name: 'userAttributes',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Attribute',
|
||||
default: {
|
||||
attributes: [],
|
||||
},
|
||||
required: true,
|
||||
description: 'Attributes to update for the user',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [preSendAttributes],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attributes',
|
||||
name: 'attributes',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Attribute Type',
|
||||
name: 'attributeType',
|
||||
type: 'options',
|
||||
default: 'standard',
|
||||
options: [
|
||||
{
|
||||
name: 'Standard Attribute',
|
||||
value: 'standard',
|
||||
},
|
||||
{
|
||||
name: 'Custom Attribute',
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Standard Attribute',
|
||||
name: 'standardName',
|
||||
type: 'options',
|
||||
default: 'address',
|
||||
options: [
|
||||
{
|
||||
name: 'Address',
|
||||
value: 'address',
|
||||
},
|
||||
{
|
||||
name: 'Birthdate',
|
||||
value: 'birthdate',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
},
|
||||
{
|
||||
name: 'Family Name',
|
||||
value: 'family_name',
|
||||
},
|
||||
{
|
||||
name: 'Gender',
|
||||
value: 'gender',
|
||||
},
|
||||
{
|
||||
name: 'Given Name',
|
||||
value: 'given_name',
|
||||
},
|
||||
{
|
||||
name: 'Locale',
|
||||
value: 'locale',
|
||||
},
|
||||
{
|
||||
name: 'Middle Name',
|
||||
value: 'middle_name',
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
name: 'Nickname',
|
||||
value: 'nickname',
|
||||
},
|
||||
{
|
||||
name: 'Phone Number',
|
||||
value: 'phone_number',
|
||||
},
|
||||
{
|
||||
name: 'Preferred Username',
|
||||
value: 'preferred_username',
|
||||
},
|
||||
{
|
||||
name: 'Profile Picture',
|
||||
value: 'profilepicture',
|
||||
},
|
||||
{
|
||||
name: 'Updated At',
|
||||
value: 'updated_at',
|
||||
},
|
||||
{
|
||||
name: 'User Sub',
|
||||
value: 'sub',
|
||||
},
|
||||
{
|
||||
name: 'Website',
|
||||
value: 'website',
|
||||
},
|
||||
{
|
||||
name: 'Zone Info',
|
||||
value: 'zoneinfo',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
attributeType: ['standard'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attribute Name',
|
||||
name: 'customName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'custom:myAttribute',
|
||||
description: 'The name of the custom attribute (must start with "custom:")',
|
||||
displayOptions: {
|
||||
show: {
|
||||
attributeType: ['custom'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value of the attribute',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as get from './get.operation';
|
||||
import { simplifyUserPool } from '../../helpers/utils';
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userPool'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
action: 'Get user pool',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Amz-Target': 'AWSCognitoIdentityProviderService.DescribeUserPool',
|
||||
},
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
simplifyUserPool,
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'UserPool',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
|
||||
...get.description,
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { updateDisplayOptions } from 'n8n-workflow';
|
||||
|
||||
import { userPoolResourceLocator } from '../common.description';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
...userPoolResourceLocator,
|
||||
description: 'Select the user pool to retrieve',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to return a simplified version of the response instead of the raw data',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['userPool'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
64
packages/nodes-base/nodes/Aws/Cognito/helpers/constants.ts
Normal file
64
packages/nodes-base/nodes/Aws/Cognito/helpers/constants.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
export const HeaderConstants = {
|
||||
AUTHORIZATION: 'authorization',
|
||||
X_MS_CONTINUATION: 'x-ms-continuation',
|
||||
X_MS_COSMOS_OFFER_AUTOPILOT_SETTING: 'x-ms-cosmos-offer-autopilot-setting',
|
||||
X_MS_DOCUMENTDB_IS_UPSERT: 'x-ms-documentdb-is-upsert',
|
||||
X_MS_DOCUMENTDB_PARTITIONKEY: 'x-ms-documentdb-partitionkey',
|
||||
X_MS_MAX_ITEM_COUNT: 'x-ms-max-item-count',
|
||||
X_MS_OFFER_THROUGHPUT: 'x-ms-offer-throughput',
|
||||
};
|
||||
|
||||
export const ERROR_MESSAGES = {
|
||||
ResourceNotFound: {
|
||||
Group: {
|
||||
delete: {
|
||||
message: 'The group you are trying to delete could not be found.',
|
||||
description: 'Adjust the "Group" parameter setting to delete the group correctly.',
|
||||
},
|
||||
get: {
|
||||
message: 'The group you are trying to retrieve could not be found.',
|
||||
description: 'Adjust the "Group" parameter setting to retrieve the group correctly.',
|
||||
},
|
||||
update: {
|
||||
message: 'The group you are trying to update could not be found.',
|
||||
description: 'Adjust the "Group" parameter setting to update the group correctly.',
|
||||
},
|
||||
},
|
||||
User: {
|
||||
delete: {
|
||||
message: 'The user are trying to retrieve could not be found.',
|
||||
description: 'Adjust the "User" parameter setting to delete the user correctly.',
|
||||
},
|
||||
get: {
|
||||
message: 'The user you are trying to retrieve could not be found.',
|
||||
description: 'Adjust the "User" parameter setting to retrieve the user correctly.',
|
||||
},
|
||||
update: {
|
||||
message: 'The user you are trying to update could not be found.',
|
||||
description: 'Adjust the "User" parameter setting to update the user correctly.',
|
||||
},
|
||||
},
|
||||
},
|
||||
EntityAlreadyExists: {
|
||||
Group: {
|
||||
message: 'The group you are trying to create already exists.',
|
||||
description: 'Adjust the "Group Name" parameter setting to create the group correctly.',
|
||||
},
|
||||
User: {
|
||||
message: 'The user you are trying to create already exists.',
|
||||
description: 'Adjust the "User Name" parameter setting to create the user correctly.',
|
||||
},
|
||||
},
|
||||
UserGroup: {
|
||||
add: {
|
||||
message: 'The user/group you are trying to add could not be found.',
|
||||
description:
|
||||
'Adjust the "User" and "Group" parameters to add the user to the group correctly.',
|
||||
},
|
||||
remove: {
|
||||
message: 'The user/group you are trying to remove could not be found.',
|
||||
description:
|
||||
'Adjust the "User" and "Group" parameters to remove the user from the group correctly.',
|
||||
},
|
||||
},
|
||||
};
|
||||
117
packages/nodes-base/nodes/Aws/Cognito/helpers/errorHandler.ts
Normal file
117
packages/nodes-base/nodes/Aws/Cognito/helpers/errorHandler.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type {
|
||||
JsonObject,
|
||||
IExecuteSingleFunctions,
|
||||
IN8nHttpFullResponse,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import { ERROR_MESSAGES } from './constants';
|
||||
import type { AwsError, ErrorMessage } from './interfaces';
|
||||
|
||||
function mapErrorToResponse(
|
||||
errorType: string,
|
||||
resource: string,
|
||||
operation: string,
|
||||
inputValue?: string,
|
||||
): ErrorMessage | undefined {
|
||||
const op = operation as keyof typeof ERROR_MESSAGES.ResourceNotFound.User;
|
||||
const nameLabel = resource.charAt(0).toUpperCase() + resource.slice(1);
|
||||
const valuePart = inputValue ? ` "${inputValue}"` : '';
|
||||
|
||||
const notFoundMessage = (base: ErrorMessage, suffix: string): ErrorMessage => ({
|
||||
...base,
|
||||
message: `${nameLabel}${valuePart} ${suffix}`,
|
||||
});
|
||||
|
||||
const isNotFound = [
|
||||
'UserNotFoundException',
|
||||
'ResourceNotFoundException',
|
||||
'NoSuchEntity',
|
||||
].includes(errorType);
|
||||
|
||||
const isExists = [
|
||||
'UsernameExistsException',
|
||||
'EntityAlreadyExists',
|
||||
'GroupExistsException',
|
||||
].includes(errorType);
|
||||
|
||||
if (isNotFound) {
|
||||
if (resource === 'user') {
|
||||
if (operation === 'addToGroup') {
|
||||
return notFoundMessage(ERROR_MESSAGES.UserGroup.add, 'not found while adding to group.');
|
||||
}
|
||||
if (operation === 'removeFromGroup') {
|
||||
return notFoundMessage(
|
||||
ERROR_MESSAGES.UserGroup.remove,
|
||||
'not found while removing from group.',
|
||||
);
|
||||
}
|
||||
return notFoundMessage(ERROR_MESSAGES.ResourceNotFound.User[op], 'not found.');
|
||||
}
|
||||
|
||||
if (resource === 'group') {
|
||||
return notFoundMessage(ERROR_MESSAGES.ResourceNotFound.Group[op], 'not found.');
|
||||
}
|
||||
}
|
||||
|
||||
if (isExists) {
|
||||
const existsMessage = `${nameLabel}${valuePart} already exists.`;
|
||||
|
||||
if (resource === 'user') {
|
||||
return { ...ERROR_MESSAGES.EntityAlreadyExists.User, message: existsMessage };
|
||||
}
|
||||
if (resource === 'group') {
|
||||
return { ...ERROR_MESSAGES.EntityAlreadyExists.Group, message: existsMessage };
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function handleError(
|
||||
this: IExecuteSingleFunctions,
|
||||
data: INodeExecutionData[],
|
||||
response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const statusCode = String(response.statusCode);
|
||||
|
||||
if (!statusCode.startsWith('4') && !statusCode.startsWith('5')) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const resource = this.getNodeParameter('resource') as string;
|
||||
const operation = this.getNodeParameter('operation') as string;
|
||||
|
||||
let inputValue: string | undefined;
|
||||
|
||||
if (operation === 'create') {
|
||||
if (resource === 'user') {
|
||||
inputValue = this.getNodeParameter('newUserName', '') as string;
|
||||
} else if (resource === 'group') {
|
||||
inputValue = this.getNodeParameter('newGroupName', '') as string;
|
||||
}
|
||||
} else {
|
||||
inputValue = this.getNodeParameter(resource, '', { extractValue: true }) as string;
|
||||
}
|
||||
|
||||
const responseBody = response.body as AwsError;
|
||||
const errorType = (responseBody.__type ?? response.headers?.['x-amzn-errortype']) as string;
|
||||
const errorMessage = (responseBody.message ??
|
||||
response.headers?.['x-amzn-errormessage']) as string;
|
||||
|
||||
if (!errorType) {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject);
|
||||
}
|
||||
|
||||
const specificError = mapErrorToResponse(errorType, resource, operation, inputValue);
|
||||
|
||||
throw new NodeApiError(
|
||||
this.getNode(),
|
||||
response as unknown as JsonObject,
|
||||
specificError ?? {
|
||||
message: errorType,
|
||||
description: errorMessage,
|
||||
},
|
||||
);
|
||||
}
|
||||
73
packages/nodes-base/nodes/Aws/Cognito/helpers/interfaces.ts
Normal file
73
packages/nodes-base/nodes/Aws/Cognito/helpers/interfaces.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
|
||||
export interface IUserAttribute {
|
||||
Name: string;
|
||||
Value: string;
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
Username: string;
|
||||
Enabled: boolean;
|
||||
UserCreateDate: string;
|
||||
UserLastModifiedDate: string;
|
||||
UserStatus: string;
|
||||
Attributes?: IUserAttribute[];
|
||||
}
|
||||
|
||||
export interface IGroup {
|
||||
GroupName: string;
|
||||
}
|
||||
|
||||
export interface IListUsersResponse {
|
||||
Users: IUser[];
|
||||
NextToken?: string;
|
||||
}
|
||||
|
||||
export interface IListGroupsResponse {
|
||||
Groups: IGroup[];
|
||||
NextToken?: string;
|
||||
}
|
||||
|
||||
export interface IGroupWithUserResponse extends IGroup {
|
||||
Users: IUser[];
|
||||
}
|
||||
|
||||
export interface IUserAttributeInput {
|
||||
attributeType: string;
|
||||
standardName: string;
|
||||
customName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface IUserPool {
|
||||
Id: string;
|
||||
Name: string;
|
||||
UsernameAttributes?: string[];
|
||||
AccountRecoverySetting?: IDataObject;
|
||||
AdminCreateUserConfig?: IDataObject;
|
||||
EmailConfiguration?: IDataObject;
|
||||
LambdaConfig?: IDataObject;
|
||||
Policies?: IDataObject;
|
||||
SchemaAttributes?: IDataObject;
|
||||
UserAttributeUpdateSettings?: IDataObject;
|
||||
UserPoolTags?: IDataObject;
|
||||
UserPoolTier?: string;
|
||||
VerificationMessageTemplate?: IDataObject;
|
||||
}
|
||||
|
||||
export interface Filters {
|
||||
filter?: {
|
||||
attribute?: string;
|
||||
value?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AwsError {
|
||||
__type?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ErrorMessage {
|
||||
message: string;
|
||||
description: string;
|
||||
}
|
||||
424
packages/nodes-base/nodes/Aws/Cognito/helpers/utils.ts
Normal file
424
packages/nodes-base/nodes/Aws/Cognito/helpers/utils.ts
Normal file
@@ -0,0 +1,424 @@
|
||||
import type {
|
||||
IHttpRequestOptions,
|
||||
ILoadOptionsFunctions,
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
IN8nHttpFullResponse,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
import { jsonParse, NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import type {
|
||||
IGroup,
|
||||
IGroupWithUserResponse,
|
||||
IListGroupsResponse,
|
||||
IUser,
|
||||
IUserAttribute,
|
||||
IUserAttributeInput,
|
||||
IUserPool,
|
||||
} from './interfaces';
|
||||
import { awsApiRequest, awsApiRequestAllItems } from '../transport';
|
||||
|
||||
const validateEmail = (email: string): boolean => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
const validatePhoneNumber = (phone: string): boolean => /^\+[0-9]\d{1,14}$/.test(phone);
|
||||
|
||||
export async function preSendStringifyBody(
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
if (requestOptions.body) {
|
||||
requestOptions.body = JSON.stringify(requestOptions.body);
|
||||
}
|
||||
return requestOptions;
|
||||
}
|
||||
|
||||
export async function getUserPool(
|
||||
this: IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
userPoolId: string,
|
||||
): Promise<IUserPool> {
|
||||
if (!userPoolId) {
|
||||
throw new NodeOperationError(this.getNode(), 'User Pool ID is required');
|
||||
}
|
||||
|
||||
const response = (await awsApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'DescribeUserPool',
|
||||
JSON.stringify({ UserPoolId: userPoolId }),
|
||||
)) as { UserPool: IUserPool };
|
||||
|
||||
if (!response?.UserPool) {
|
||||
throw new NodeOperationError(this.getNode(), 'User Pool not found in response');
|
||||
}
|
||||
|
||||
return response.UserPool;
|
||||
}
|
||||
|
||||
export async function getUsersInGroup(
|
||||
this: IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
groupName: string,
|
||||
userPoolId: string,
|
||||
): Promise<IUser[]> {
|
||||
if (!userPoolId) {
|
||||
throw new NodeOperationError(this.getNode(), 'User Pool ID is required');
|
||||
}
|
||||
|
||||
const requestBody: IDataObject = {
|
||||
UserPoolId: userPoolId,
|
||||
GroupName: groupName,
|
||||
};
|
||||
|
||||
const allUsers = (await awsApiRequestAllItems.call(
|
||||
this,
|
||||
'POST',
|
||||
'ListUsersInGroup',
|
||||
requestBody,
|
||||
'Users',
|
||||
)) as unknown as IUser[];
|
||||
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
export async function getUserNameFromExistingUsers(
|
||||
this: IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
userName: string,
|
||||
userPoolId: string,
|
||||
isEmailOrPhone: boolean,
|
||||
): Promise<string | undefined> {
|
||||
if (isEmailOrPhone) {
|
||||
return userName;
|
||||
}
|
||||
|
||||
const usersResponse = (await awsApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'ListUsers',
|
||||
JSON.stringify({
|
||||
UserPoolId: userPoolId,
|
||||
Filter: `sub = "${userName}"`,
|
||||
}),
|
||||
)) as { Users: IUser[] };
|
||||
|
||||
const username =
|
||||
usersResponse.Users && usersResponse.Users.length > 0
|
||||
? usersResponse.Users[0].Username
|
||||
: undefined;
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
export async function preSendUserFields(
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const operation = this.getNodeParameter('operation') as string;
|
||||
const userPoolId = this.getNodeParameter('userPool', undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const userPool = await getUserPool.call(this, userPoolId);
|
||||
|
||||
const usernameAttributes = userPool.UsernameAttributes ?? [];
|
||||
const isEmailAuth = usernameAttributes.includes('email');
|
||||
const isPhoneAuth = usernameAttributes.includes('phone_number');
|
||||
const isEmailOrPhone = isEmailAuth || isPhoneAuth;
|
||||
|
||||
const getValidatedNewUserName = (): string => {
|
||||
const newUsername = this.getNodeParameter('newUserName') as string;
|
||||
|
||||
if (isEmailAuth && !validateEmail(newUsername)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'Invalid email format',
|
||||
description: 'Please provide a valid email (e.g., name@gmail.com)',
|
||||
});
|
||||
}
|
||||
if (isPhoneAuth && !validatePhoneNumber(newUsername)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'Invalid phone number format',
|
||||
description: 'Please provide a valid phone number (e.g., +14155552671)',
|
||||
});
|
||||
}
|
||||
|
||||
return newUsername;
|
||||
};
|
||||
|
||||
const finalUserName =
|
||||
operation === 'create'
|
||||
? getValidatedNewUserName()
|
||||
: await getUserNameFromExistingUsers.call(
|
||||
this,
|
||||
this.getNodeParameter('user', undefined, { extractValue: true }) as string,
|
||||
userPoolId,
|
||||
isEmailOrPhone,
|
||||
);
|
||||
|
||||
const body = jsonParse<IDataObject>(String(requestOptions.body), {
|
||||
acceptJSObject: true,
|
||||
errorMessage: 'Invalid request body. Request body must be valid JSON.',
|
||||
});
|
||||
|
||||
return {
|
||||
...requestOptions,
|
||||
body: JSON.stringify({
|
||||
...body,
|
||||
...(finalUserName ? { Username: finalUserName } : {}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function processGroup(
|
||||
this: IExecuteSingleFunctions,
|
||||
items: INodeExecutionData[],
|
||||
response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const userPoolId = this.getNodeParameter('userPool', undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
const includeUsers = this.getNodeParameter('includeUsers') as boolean;
|
||||
const body = response.body as IDataObject;
|
||||
|
||||
if (body.Group) {
|
||||
const group = body.Group as IGroup;
|
||||
|
||||
if (!includeUsers) {
|
||||
return this.helpers.returnJsonArray({ ...group });
|
||||
}
|
||||
|
||||
const users = await getUsersInGroup.call(this, group.GroupName, userPoolId);
|
||||
return this.helpers.returnJsonArray({ ...group, Users: users });
|
||||
}
|
||||
|
||||
const groups = (response.body as IListGroupsResponse).Groups ?? [];
|
||||
|
||||
if (!includeUsers) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const processedGroups: IGroupWithUserResponse[] = [];
|
||||
for (const group of groups) {
|
||||
const users = await getUsersInGroup.call(this, group.GroupName, userPoolId);
|
||||
processedGroups.push({
|
||||
...group,
|
||||
Users: users,
|
||||
});
|
||||
}
|
||||
|
||||
return items.map((item) => ({ json: { ...item.json, Groups: processedGroups } }));
|
||||
}
|
||||
|
||||
export async function validateArn(
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const arn = this.getNodeParameter('additionalFields.arn', '') as string;
|
||||
const arnRegex =
|
||||
/^arn:[-.\w+=/,@]+:[-.\w+=/,@]+:([-.\w+=/,@]*)?:[0-9]+:[-.\w+=/,@]+(:[-.\w+=/,@]+)?(:[-.\w+=/,@]+)?$/;
|
||||
|
||||
if (!arnRegex.test(arn)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'Invalid ARN format',
|
||||
description:
|
||||
'Please provide a valid AWS ARN (e.g., arn:aws:iam::123456789012:role/GroupRole).',
|
||||
});
|
||||
}
|
||||
|
||||
return requestOptions;
|
||||
}
|
||||
|
||||
export async function simplifyUserPool(
|
||||
this: IExecuteSingleFunctions,
|
||||
items: INodeExecutionData[],
|
||||
_response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const simple = this.getNodeParameter('simple') as boolean;
|
||||
|
||||
if (!simple) {
|
||||
return items;
|
||||
}
|
||||
|
||||
return items
|
||||
.map((item) => {
|
||||
const data = item.json?.UserPool as IUserPool;
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
AccountRecoverySetting,
|
||||
AdminCreateUserConfig,
|
||||
EmailConfiguration,
|
||||
LambdaConfig,
|
||||
Policies,
|
||||
SchemaAttributes,
|
||||
UserAttributeUpdateSettings,
|
||||
UserPoolTags,
|
||||
UserPoolTier,
|
||||
VerificationMessageTemplate,
|
||||
...selectedData
|
||||
} = data;
|
||||
|
||||
return { json: { UserPool: { ...selectedData } } };
|
||||
})
|
||||
.filter(Boolean) as INodeExecutionData[];
|
||||
}
|
||||
|
||||
export async function simplifyUser(
|
||||
this: IExecuteSingleFunctions,
|
||||
items: INodeExecutionData[],
|
||||
_response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const simple = this.getNodeParameter('simple') as boolean;
|
||||
|
||||
if (!simple) {
|
||||
return items;
|
||||
}
|
||||
|
||||
return items
|
||||
.map((item) => {
|
||||
const data = item.json;
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(data.Users)) {
|
||||
const users = data.Users as IUser[];
|
||||
|
||||
const simplifiedUsers = users.map((user) => {
|
||||
const attributesArray = user.Attributes ?? [];
|
||||
|
||||
const userAttributes = Object.fromEntries(
|
||||
attributesArray
|
||||
.filter(({ Name }) => Name?.trim())
|
||||
.map(({ Name, Value }) => [Name, Value ?? '']),
|
||||
);
|
||||
|
||||
const { Attributes, ...rest } = user;
|
||||
return { ...rest, ...userAttributes };
|
||||
});
|
||||
|
||||
return { json: { ...data, Users: simplifiedUsers } };
|
||||
}
|
||||
|
||||
if (Array.isArray(data.UserAttributes)) {
|
||||
const attributesArray = data.UserAttributes as IUserAttribute[];
|
||||
|
||||
const userAttributes = Object.fromEntries(
|
||||
attributesArray
|
||||
.filter(({ Name }) => Name?.trim())
|
||||
.map(({ Name, Value }) => [Name, Value ?? '']),
|
||||
);
|
||||
|
||||
const { UserAttributes, ...rest } = data;
|
||||
return { json: { ...rest, ...userAttributes } };
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
.filter(Boolean) as INodeExecutionData[];
|
||||
}
|
||||
|
||||
export async function preSendAttributes(
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
const parameterName =
|
||||
operation === 'create'
|
||||
? 'additionalFields.userAttributes.attributes'
|
||||
: 'userAttributes.attributes';
|
||||
const attributes = this.getNodeParameter(parameterName, []) as IUserAttributeInput[];
|
||||
|
||||
if (operation === 'update' && (!attributes || attributes.length === 0)) {
|
||||
throw new NodeOperationError(this.getNode(), 'No user attributes provided', {
|
||||
description: 'At least one user attribute must be provided for the update operation.',
|
||||
});
|
||||
}
|
||||
|
||||
if (operation === 'create') {
|
||||
const hasEmail = attributes.some((a) => a.standardName === 'email');
|
||||
const hasEmailVerified = attributes.some(
|
||||
(a) => a.standardName === 'email_verified' && a.value === 'true',
|
||||
);
|
||||
|
||||
if (hasEmailVerified && !hasEmail) {
|
||||
throw new NodeOperationError(this.getNode(), 'Missing required "email" attribute', {
|
||||
description:
|
||||
'"email_verified" is set to true, but the corresponding "email" attribute is not provided.',
|
||||
});
|
||||
}
|
||||
|
||||
const hasPhone = attributes.some((a) => a.standardName === 'phone_number');
|
||||
const hasPhoneVerified = attributes.some(
|
||||
(a) => a.standardName === 'phone_number_verified' && a.value === 'true',
|
||||
);
|
||||
|
||||
if (hasPhoneVerified && !hasPhone) {
|
||||
throw new NodeOperationError(this.getNode(), 'Missing required "phone_number" attribute', {
|
||||
description:
|
||||
'"phone_number_verified" is set to true, but the corresponding "phone_number" attribute is not provided.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const body = jsonParse<IDataObject>(String(requestOptions.body), {
|
||||
acceptJSObject: true,
|
||||
errorMessage: 'Invalid request body. Request body must be valid JSON.',
|
||||
});
|
||||
|
||||
body.UserAttributes = attributes.map(({ attributeType, standardName, customName, value }) => {
|
||||
if (!value || !attributeType || !(standardName ?? customName)) {
|
||||
throw new NodeOperationError(this.getNode(), 'Invalid User Attribute', {
|
||||
description: 'Each attribute must have a valid name and value.',
|
||||
});
|
||||
}
|
||||
|
||||
const attributeName =
|
||||
attributeType === 'standard'
|
||||
? standardName
|
||||
: `custom:${customName?.startsWith('custom:') ? customName : customName}`;
|
||||
|
||||
return { Name: attributeName, Value: value };
|
||||
});
|
||||
|
||||
requestOptions.body = JSON.stringify(body);
|
||||
|
||||
return requestOptions;
|
||||
}
|
||||
|
||||
export async function preSendDesiredDeliveryMediums(
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const desiredDeliveryMediums = this.getNodeParameter(
|
||||
'additionalFields.desiredDeliveryMediums',
|
||||
[],
|
||||
) as string[];
|
||||
|
||||
const attributes = this.getNodeParameter(
|
||||
'additionalFields.userAttributes.attributes',
|
||||
[],
|
||||
) as IUserAttributeInput[];
|
||||
|
||||
const hasEmail = attributes.some((attr) => attr.standardName === 'email' && !!attr.value?.trim());
|
||||
const hasPhone = attributes.some(
|
||||
(attr) => attr.standardName === 'phone_number' && !!attr.value?.trim(),
|
||||
);
|
||||
|
||||
if (desiredDeliveryMediums.includes('EMAIL') && !hasEmail) {
|
||||
throw new NodeOperationError(this.getNode(), 'Missing required "email" attribute', {
|
||||
description: 'Email is selected as a delivery medium but no email attribute is provided.',
|
||||
});
|
||||
}
|
||||
|
||||
if (desiredDeliveryMediums.includes('SMS') && !hasPhone) {
|
||||
throw new NodeOperationError(this.getNode(), 'Missing required "phone_number" attribute', {
|
||||
description:
|
||||
'SMS is selected as a delivery medium but no phone_number attribute is provided.',
|
||||
});
|
||||
}
|
||||
|
||||
return requestOptions;
|
||||
}
|
||||
1
packages/nodes-base/nodes/Aws/Cognito/methods/index.ts
Normal file
1
packages/nodes-base/nodes/Aws/Cognito/methods/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * as listSearch from './listSearch';
|
||||
190
packages/nodes-base/nodes/Aws/Cognito/methods/listSearch.ts
Normal file
190
packages/nodes-base/nodes/Aws/Cognito/methods/listSearch.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
INodeListSearchItems,
|
||||
INodeListSearchResult,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import type { IGroup, IUser, IUserAttribute, IUserPool } from '../helpers/interfaces';
|
||||
import { getUserNameFromExistingUsers, getUserPool } from '../helpers/utils';
|
||||
import { awsApiRequest, awsApiRequestAllItems } from '../transport';
|
||||
|
||||
function formatResults(items: IDataObject[], filter?: string): INodeListSearchItems[] {
|
||||
return items
|
||||
.map(({ id, name }) => ({
|
||||
name: String(name).replace(/ /g, ''),
|
||||
value: String(id),
|
||||
}))
|
||||
.filter(({ name }) => !filter || name.toLowerCase().includes(filter.toLowerCase()))
|
||||
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
||||
}
|
||||
|
||||
export async function searchGroups(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const userPoolId = this.getNodeParameter('userPool', undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
if (!userPoolId) {
|
||||
throw new NodeOperationError(this.getNode(), 'User Pool ID is required to search groups');
|
||||
}
|
||||
|
||||
const responseData = (await awsApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'ListGroups',
|
||||
JSON.stringify({ UserPoolId: userPoolId, Limit: 50, NextToken: paginationToken }),
|
||||
)) as IDataObject;
|
||||
|
||||
const groups = responseData.Groups as IDataObject[];
|
||||
|
||||
const groupsMapped = groups.map(({ GroupName }) => ({
|
||||
id: GroupName,
|
||||
name: GroupName,
|
||||
}));
|
||||
|
||||
const formattedResults = formatResults(groupsMapped, filter);
|
||||
|
||||
return { results: formattedResults, paginationToken: responseData.NextToken };
|
||||
}
|
||||
|
||||
export async function searchGroupsForUser(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const userPoolId = this.getNodeParameter('userPool', undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
const inputUser = this.getNodeParameter('user', undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
if (!userPoolId || !inputUser) {
|
||||
return { results: [] };
|
||||
}
|
||||
|
||||
const userPool = await getUserPool.call(this, userPoolId);
|
||||
|
||||
const usernameAttributes = userPool.UsernameAttributes ?? [];
|
||||
const isEmailAuth = usernameAttributes.includes('email');
|
||||
const isPhoneAuth = usernameAttributes.includes('phone_number');
|
||||
const isEmailOrPhone = isEmailAuth || isPhoneAuth;
|
||||
|
||||
const userName = await getUserNameFromExistingUsers.call(
|
||||
this,
|
||||
inputUser,
|
||||
userPoolId,
|
||||
isEmailOrPhone,
|
||||
);
|
||||
|
||||
if (!userName) {
|
||||
return { results: [] };
|
||||
}
|
||||
|
||||
const groups = (await awsApiRequestAllItems.call(
|
||||
this,
|
||||
'POST',
|
||||
'AdminListGroupsForUser',
|
||||
{
|
||||
Username: userName,
|
||||
UserPoolId: userPoolId,
|
||||
},
|
||||
'Groups',
|
||||
)) as unknown as IGroup[];
|
||||
|
||||
const resultGroups = groups
|
||||
.filter((group) => !filter || group.GroupName.toLowerCase().includes(filter.toLowerCase()))
|
||||
.map((group) => ({
|
||||
name: group.GroupName,
|
||||
value: group.GroupName,
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return { results: resultGroups };
|
||||
}
|
||||
|
||||
export async function searchUsers(
|
||||
this: IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const userPoolId = this.getNodeParameter('userPool', undefined, { extractValue: true }) as string;
|
||||
|
||||
if (!userPoolId) {
|
||||
throw new NodeOperationError(this.getNode(), 'User Pool ID is required to search users');
|
||||
}
|
||||
|
||||
const userPoolData = (await awsApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'DescribeUserPool',
|
||||
JSON.stringify({ UserPoolId: userPoolId }),
|
||||
)) as IDataObject;
|
||||
|
||||
const userPool = userPoolData.UserPool as IUserPool;
|
||||
const usernameAttributes = userPool.UsernameAttributes;
|
||||
|
||||
const responseData = (await awsApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'ListUsers',
|
||||
JSON.stringify({
|
||||
UserPoolId: userPoolId,
|
||||
Limit: 50,
|
||||
NextToken: paginationToken,
|
||||
}),
|
||||
)) as IDataObject;
|
||||
|
||||
const users = responseData.Users as IUser[];
|
||||
|
||||
if (!users.length) {
|
||||
return { results: [] };
|
||||
}
|
||||
|
||||
const userResults = users.map((user) => {
|
||||
const attributes: IUserAttribute[] = user.Attributes ?? [];
|
||||
const username = user.Username;
|
||||
|
||||
const email = attributes.find((attr) => attr.Name === 'email')?.Value ?? '';
|
||||
const phoneNumber = attributes.find((attr) => attr.Name === 'phone_number')?.Value ?? '';
|
||||
const sub = attributes.find((attr) => attr.Name === 'sub')?.Value ?? '';
|
||||
|
||||
const name = usernameAttributes?.includes('email')
|
||||
? email
|
||||
: usernameAttributes?.includes('phone_number')
|
||||
? phoneNumber
|
||||
: username;
|
||||
|
||||
return { id: sub, name, value: sub };
|
||||
});
|
||||
|
||||
return { results: formatResults(userResults, filter), paginationToken: responseData.NextToken };
|
||||
}
|
||||
|
||||
export async function searchUserPools(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const responseData = (await awsApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'ListUserPools',
|
||||
JSON.stringify({ Limit: 50, NextToken: paginationToken }),
|
||||
)) as IDataObject;
|
||||
|
||||
const userPools = responseData.UserPools as IUserPool[];
|
||||
|
||||
const userPoolsMapped = userPools.map((userPool) => ({
|
||||
id: userPool.Id,
|
||||
name: userPool.Name,
|
||||
}));
|
||||
|
||||
const formattedResults = formatResults(userPoolsMapped, filter);
|
||||
|
||||
return { results: formattedResults, paginationToken: responseData.NextToken };
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Create Group', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
GroupName: 'MyNewGroup11',
|
||||
})
|
||||
.reply(200, {
|
||||
Group: {
|
||||
GroupName: 'MyNewGroup11',
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
CreationDate: 1743068959.243,
|
||||
LastModifiedDate: 1743068959.243,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['create.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [260, 360],
|
||||
"id": "9ed2b86b-7c24-4ea0-a328-92d9e6dba35a",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "group",
|
||||
"operation": "create",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_qqle3XBUA",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolThree"
|
||||
},
|
||||
"newGroupName": "MyNewGroup11",
|
||||
"additionalFields": {},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"id": "fc6ee80d-3c8f-4ecc-a053-b4a717484c2a",
|
||||
"name": "createGroup",
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [500, 360],
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"createGroup": [
|
||||
{
|
||||
"json": {
|
||||
"CreationDate": 1743068959.243,
|
||||
"GroupName": "MyNewGroup11",
|
||||
"LastModifiedDate": 1743068959.243,
|
||||
"UserPoolId": "eu-central-1_qqle3XBUA"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "createGroup",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Delete Group', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
GroupName: 'MyNewGroup22',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DeleteGroup')
|
||||
.reply(200, {
|
||||
Group: {
|
||||
GroupName: 'MyNewGroup22',
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
CreationDate: 1743068959.243,
|
||||
LastModifiedDate: 1743068959.243,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['delete.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "group",
|
||||
"operation": "delete",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_qqle3XBUA",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolThree"
|
||||
},
|
||||
"group": {
|
||||
"__rl": true,
|
||||
"value": "MyNewGroup22",
|
||||
"mode": "list",
|
||||
"cachedResultName": "MyNewGroup22"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 1300],
|
||||
"id": "ece9e04f-305c-45ed-afc6-34d34303f8b0",
|
||||
"name": "AWS Cognito1",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {},
|
||||
"pinData": {
|
||||
"AWS Cognito1": [
|
||||
{
|
||||
"json": {
|
||||
"deleted": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
86
packages/nodes-base/nodes/Aws/Cognito/test/group/get.test.ts
Normal file
86
packages/nodes-base/nodes/Aws/Cognito/test/group/get.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Get Group', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:123456789012:userpool/eu-central-1_qqle3XBUA',
|
||||
CreationDate: 1739530218.869,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 4,
|
||||
Id: 'eu-central-1_qqle3XBUA',
|
||||
LastModifiedDate: 1739530218.869,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolThree',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
Limit: 50,
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListGroups')
|
||||
.reply(200, {
|
||||
Groups: [
|
||||
{
|
||||
CreationDate: 1741609269.287,
|
||||
GroupName: 'MyNewGroup2',
|
||||
LastModifiedDate: 1741609269.287,
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
},
|
||||
],
|
||||
});
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
GroupName: 'MyNewGroup2',
|
||||
Limit: 50,
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsersInGroup')
|
||||
.reply(200, {
|
||||
Users: [],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
GroupName: 'MyNewGroup2',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.GetGroup')
|
||||
.reply(200, {
|
||||
Group: {
|
||||
CreationDate: 1741609269.287,
|
||||
GroupName: 'MyNewGroup2',
|
||||
LastModifiedDate: 1741609269.287,
|
||||
UserPoolId: 'eu-central-1_qqle3XBUA',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['get.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "group",
|
||||
"operation": "get",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_qqle3XBUA",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolThree"
|
||||
},
|
||||
"group": {
|
||||
"__rl": true,
|
||||
"value": "MyNewGroup2",
|
||||
"mode": "list",
|
||||
"cachedResultName": "MyNewGroup2"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 1300],
|
||||
"id": "ece9e04f-305c-45ed-afc6-34d34303f8b0",
|
||||
"name": "AWS Cognito1",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {},
|
||||
"pinData": {
|
||||
"AWS Cognito1": [
|
||||
{
|
||||
"json": {
|
||||
"CreationDate": 1741609269.287,
|
||||
"GroupName": "MyNewGroup2",
|
||||
"LastModifiedDate": 1741609269.287,
|
||||
"UserPoolId": "eu-central-1_qqle3XBUA"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
144
packages/nodes-base/nodes/Aws/Cognito/test/group/getAll.test.ts
Normal file
144
packages/nodes-base/nodes/Aws/Cognito/test/group/getAll.test.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Get All Groups', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListGroups')
|
||||
.reply(200, {
|
||||
Groups: [
|
||||
{
|
||||
GroupName: 'MyNewGroup',
|
||||
Description: 'Updated',
|
||||
CreationDate: 1732740693.563,
|
||||
LastModifiedDate: 1733422336.443,
|
||||
Precedence: 0,
|
||||
RoleArn: 'arn:aws:iam::123456789012:group/Admins',
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Users: [
|
||||
{
|
||||
Username: 'user1',
|
||||
Attributes: [{ Name: 'email', Value: 'user1@example.com' }],
|
||||
},
|
||||
{
|
||||
Username: 'user2',
|
||||
Attributes: [{ Name: 'email', Value: 'user2@example.com' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
GroupName: 'MyNewTesttttt',
|
||||
Description: 'Updated description',
|
||||
CreationDate: 1733424987.825,
|
||||
LastModifiedDate: 1741609241.742,
|
||||
Precedence: 5,
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Users: [],
|
||||
},
|
||||
{
|
||||
GroupName: 'MyNewTest1',
|
||||
Description: 'test',
|
||||
CreationDate: 1733398042.783,
|
||||
LastModifiedDate: 1733691256.447,
|
||||
Precedence: 5,
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Users: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
GroupName: 'MyNewGroup',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsersInGroup')
|
||||
.reply(200, {
|
||||
Users: [],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
GroupName: 'MyNewTesttttt',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsersInGroup')
|
||||
.reply(200, {
|
||||
Users: [],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
GroupName: 'MyNewTest1',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsersInGroup')
|
||||
.reply(200, {
|
||||
Users: [],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListGroups')
|
||||
.reply(200, {
|
||||
Groups: [
|
||||
{
|
||||
GroupName: 'MyNewGroup',
|
||||
Description: 'Updated',
|
||||
CreationDate: 1732740693.563,
|
||||
LastModifiedDate: 1733422336.443,
|
||||
Precedence: 0,
|
||||
RoleArn: 'arn:aws:iam::123456789012:group/Admins',
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Users: [],
|
||||
},
|
||||
{
|
||||
GroupName: 'MyNewTesttttt',
|
||||
Description: 'Updated description',
|
||||
CreationDate: 1733424987.825,
|
||||
LastModifiedDate: 1741609241.742,
|
||||
Precedence: 5,
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Users: [],
|
||||
},
|
||||
{
|
||||
GroupName: 'MyNewTest1',
|
||||
Description: 'test',
|
||||
CreationDate: 1733398042.783,
|
||||
LastModifiedDate: 1733691256.447,
|
||||
Precedence: 5,
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Users: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['getAll.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [260, 360],
|
||||
"id": "9ed2b86b-7c24-4ea0-a328-92d9e6dba35a",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "group",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_KkXQgdCJv",
|
||||
"mode": "list",
|
||||
"cachedResultName": "AWS test"
|
||||
},
|
||||
"returnAll": true,
|
||||
"includeUsers": true,
|
||||
"requestOptions": {}
|
||||
},
|
||||
"id": "f1d96de9-a43f-4452-8760-94fc13990e0b",
|
||||
"name": "getAllGroups",
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [460, 360],
|
||||
"alwaysOutputData": true,
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"getAllGroups": [
|
||||
{
|
||||
"json": {
|
||||
"CreationDate": 1732740693.563,
|
||||
"Description": "Updated",
|
||||
"GroupName": "MyNewGroup",
|
||||
"LastModifiedDate": 1733422336.443,
|
||||
"Precedence": 0,
|
||||
"RoleArn": "arn:aws:iam::123456789012:group/Admins",
|
||||
"UserPoolId": "eu-central-1_KkXQgdCJv",
|
||||
"Users": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"CreationDate": 1733424987.825,
|
||||
"Description": "Updated description",
|
||||
"GroupName": "MyNewTesttttt",
|
||||
"LastModifiedDate": 1741609241.742,
|
||||
"Precedence": 5,
|
||||
"UserPoolId": "eu-central-1_KkXQgdCJv",
|
||||
"Users": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"CreationDate": 1733398042.783,
|
||||
"Description": "test",
|
||||
"GroupName": "MyNewTest1",
|
||||
"LastModifiedDate": 1733691256.447,
|
||||
"Precedence": 5,
|
||||
"UserPoolId": "eu-central-1_KkXQgdCJv",
|
||||
"Users": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "getAllGroups",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Update Group', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
GroupName: 'MyNewTesttttt',
|
||||
Description: 'Updated description',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.UpdateGroup')
|
||||
.reply(200, {
|
||||
Group: {
|
||||
GroupName: 'MyNewTesttttt',
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
Description: 'Updated description',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['update.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, -100],
|
||||
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "group",
|
||||
"operation": "update",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_KkXQgdCJv",
|
||||
"mode": "list",
|
||||
"cachedResultName": "AWS test"
|
||||
},
|
||||
"group": {
|
||||
"__rl": true,
|
||||
"value": "MyNewTesttttt",
|
||||
"mode": "list",
|
||||
"cachedResultName": "MyNewTesttttt"
|
||||
},
|
||||
"additionalFields": {
|
||||
"description": "Updated description"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"id": "fd0610c8-9af2-4ca5-943f-f0de5d897fc0",
|
||||
"name": "updateGroup",
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [160, -100],
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "updateGroup",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"updateGroup": [
|
||||
{
|
||||
"json": {
|
||||
"updated": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
701
packages/nodes-base/nodes/Aws/Cognito/test/helpers/utils.test.ts
Normal file
701
packages/nodes-base/nodes/Aws/Cognito/test/helpers/utils.test.ts
Normal file
@@ -0,0 +1,701 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { MockProxy } from 'jest-mock-extended';
|
||||
import type { IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
import { NodeOperationError, NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
getUserPool,
|
||||
validateArn,
|
||||
simplifyUserPool,
|
||||
preSendUserFields,
|
||||
preSendAttributes,
|
||||
preSendDesiredDeliveryMediums,
|
||||
getUsersInGroup,
|
||||
simplifyUser,
|
||||
getUserNameFromExistingUsers,
|
||||
} from '../../helpers/utils';
|
||||
import { searchUsers } from '../../methods/listSearch';
|
||||
import { awsApiRequest, awsApiRequestAllItems } from '../../transport/index';
|
||||
|
||||
jest.mock('../../transport/index', () => ({
|
||||
awsApiRequest: jest.fn(),
|
||||
awsApiRequestAllItems: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../methods/listSearch', () => ({
|
||||
searchUsers: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('AWS Cognito - Helpers functions', () => {
|
||||
let loadOptionsFunctions: MockProxy<IExecuteSingleFunctions>;
|
||||
let mockRequestWithAuthentication: jest.Mock;
|
||||
let mockReturnJsonArray: jest.Mock;
|
||||
let requestOptions: IHttpRequestOptions;
|
||||
|
||||
beforeEach(() => {
|
||||
loadOptionsFunctions = mock<IExecuteSingleFunctions>();
|
||||
mockRequestWithAuthentication = jest.fn();
|
||||
mockReturnJsonArray = jest.fn();
|
||||
loadOptionsFunctions.helpers.httpRequestWithAuthentication = mockRequestWithAuthentication;
|
||||
loadOptionsFunctions.helpers.returnJsonArray = mockReturnJsonArray;
|
||||
loadOptionsFunctions.getCredentials.mockResolvedValue({
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('getUserPool', () => {
|
||||
it('should fetch the user pool information', async () => {
|
||||
const userPoolId = 'eu-central-1_W3WwpiBXV';
|
||||
|
||||
const mockResponse = {
|
||||
UserPool: {
|
||||
Id: userPoolId,
|
||||
Name: 'UserPoolSimple',
|
||||
},
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const userPool = await getUserPool.call(loadOptionsFunctions, userPoolId);
|
||||
|
||||
expect(userPool).toEqual({ Id: userPoolId, Name: 'UserPoolSimple' });
|
||||
});
|
||||
|
||||
it('should throw an error if user pool ID is missing', async () => {
|
||||
await expect(getUserPool.call(loadOptionsFunctions, '')).rejects.toThrowError(
|
||||
NodeOperationError,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if user pool is not found', async () => {
|
||||
const userPoolId = 'invalid-user-pool-id';
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({});
|
||||
|
||||
await expect(getUserPool.call(loadOptionsFunctions, userPoolId)).rejects.toThrowError(
|
||||
NodeOperationError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUsersInGroup', () => {
|
||||
it('should throw an error if UserPoolId is missing', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockReturnValue(undefined);
|
||||
|
||||
await expect(
|
||||
getUsersInGroup.call(loadOptionsFunctions, 'groupName', ''),
|
||||
).rejects.toThrowError(NodeOperationError);
|
||||
});
|
||||
|
||||
it('should return an empty list if no users are found', async () => {
|
||||
(loadOptionsFunctions.getNodeParameter as jest.Mock).mockReturnValue('userPoolId');
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValue([]);
|
||||
|
||||
const result = await getUsersInGroup.call(loadOptionsFunctions, 'groupName', 'userPoolId');
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return users correctly', async () => {
|
||||
const mockUsers = [
|
||||
{
|
||||
Username: 'user1',
|
||||
Enabled: true,
|
||||
Attributes: [{ Name: 'email', Value: 'user1@example.com' }],
|
||||
},
|
||||
{
|
||||
Username: 'user2',
|
||||
Enabled: true,
|
||||
Attributes: [{ Name: 'email', Value: 'user2@example.com' }],
|
||||
},
|
||||
];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValue(mockUsers);
|
||||
|
||||
const result = await getUsersInGroup.call(loadOptionsFunctions, 'groupName', 'userPoolId');
|
||||
expect(result).toEqual([
|
||||
{
|
||||
Username: 'user1',
|
||||
Enabled: true,
|
||||
Attributes: [
|
||||
{
|
||||
Name: 'email',
|
||||
Value: 'user1@example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Username: 'user2',
|
||||
Enabled: true,
|
||||
Attributes: [
|
||||
{
|
||||
Name: 'email',
|
||||
Value: 'user2@example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty attributes and missing values', async () => {
|
||||
const mockUsers = [
|
||||
{
|
||||
Username: 'user1',
|
||||
Enabled: true,
|
||||
Attributes: [{ Name: 'email', Value: '' }],
|
||||
},
|
||||
];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValue(mockUsers);
|
||||
|
||||
const result = await getUsersInGroup.call(loadOptionsFunctions, 'groupName', 'userPoolId');
|
||||
expect(result).toEqual([
|
||||
{
|
||||
Username: 'user1',
|
||||
Enabled: true,
|
||||
Attributes: [
|
||||
{
|
||||
Name: 'email',
|
||||
Value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateArn', () => {
|
||||
it('should validate the ARN format', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.arn') {
|
||||
return 'arn:aws:iam::123456789012:role/GroupRole';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
requestOptions = {
|
||||
body: { additionalFields: { arn: 'arn:aws:iam::123456789012:role/GroupRole' } },
|
||||
headers: {},
|
||||
url: 'example.com',
|
||||
};
|
||||
|
||||
const result = await validateArn.call(loadOptionsFunctions, requestOptions);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should throw an error if the ARN format is invalid', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.arn') {
|
||||
return 'invalid-arn';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
requestOptions = {
|
||||
body: { additionalFields: { arn: 'invalid-arn' } },
|
||||
headers: {},
|
||||
url: 'example.com',
|
||||
};
|
||||
|
||||
await expect(validateArn.call(loadOptionsFunctions, requestOptions)).rejects.toThrowError(
|
||||
NodeApiError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('simplifyUserPool', () => {
|
||||
it('should simplify the user pool data when simple is true', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation(() => true);
|
||||
const items = [{ json: { UserPool: { Id: 'userPoolId', Name: 'UserPoolName' } } }];
|
||||
const result = await simplifyUserPool.call(loadOptionsFunctions, items, {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
});
|
||||
expect(result).toEqual([{ json: { UserPool: { Id: 'userPoolId', Name: 'UserPoolName' } } }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('simplifyUserData', () => {
|
||||
it('should simplify a single user with UserAttributes when simple is true', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockReturnValue(true);
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: {
|
||||
UserAttributes: [{ Name: 'email', Value: 'user@example.com' }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const result = await simplifyUser.call(loadOptionsFunctions, items, {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
});
|
||||
|
||||
expect(result).toEqual([{ json: { email: 'user@example.com' } }]);
|
||||
});
|
||||
|
||||
it('should simplify multiple users in Users array when simple is true', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockReturnValue(true);
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: {
|
||||
Users: [
|
||||
{
|
||||
Attributes: [{ Name: 'email', Value: 'user1@example.com' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const result = await simplifyUser.call(loadOptionsFunctions, items, {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
json: {
|
||||
Users: [{ email: 'user1@example.com' }],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return original items when simple is false', async () => {
|
||||
const items = [
|
||||
{
|
||||
json: {
|
||||
UserAttributes: [{ Name: 'email', Value: 'user@example.com' }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const result = await simplifyUser.call(loadOptionsFunctions, items, {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
});
|
||||
|
||||
expect(result).toEqual(items);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUserNameFromExistingUsers', () => {
|
||||
const userPoolId = 'eu-central-1_KkXQgdCJv';
|
||||
const userName = '03a438f2-10d1-70f1-f45a-09753ab5c4c3';
|
||||
|
||||
it('should return the userName if email or phone is used for authentication', async () => {
|
||||
const isEmailOrPhone = true;
|
||||
|
||||
const result = await getUserNameFromExistingUsers.call(
|
||||
loadOptionsFunctions,
|
||||
userName,
|
||||
userPoolId,
|
||||
isEmailOrPhone,
|
||||
);
|
||||
|
||||
expect(result).toEqual(userName);
|
||||
});
|
||||
|
||||
it('should return the username from ListUsers API when it is not email or phone authentication', async () => {
|
||||
const isEmailOrPhone = false;
|
||||
const mockApiResponse = {
|
||||
Users: [{ Username: 'existing-user' }],
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockApiResponse);
|
||||
|
||||
const result = await getUserNameFromExistingUsers.call(
|
||||
loadOptionsFunctions,
|
||||
userName,
|
||||
userPoolId,
|
||||
isEmailOrPhone,
|
||||
);
|
||||
|
||||
expect(result).toEqual('existing-user');
|
||||
});
|
||||
|
||||
it('should return undefined if no user is found in ListUsers API', async () => {
|
||||
const isEmailOrPhone = false;
|
||||
const mockApiResponse = {
|
||||
Users: [],
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockApiResponse);
|
||||
|
||||
const result = await getUserNameFromExistingUsers.call(
|
||||
loadOptionsFunctions,
|
||||
userName,
|
||||
userPoolId,
|
||||
isEmailOrPhone,
|
||||
);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('preSendUserFields', () => {
|
||||
beforeEach(() => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'userPool') return 'test-pool-id';
|
||||
if (name === 'newUserName') return 'test@example.com';
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the request body with the correct username when operation is "create" and valid email is provided', async () => {
|
||||
requestOptions = {
|
||||
body: JSON.stringify({ someField: 'value' }),
|
||||
method: 'POST',
|
||||
url: '',
|
||||
headers: {},
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({
|
||||
UserPool: { UsernameAttributes: ['email'] },
|
||||
});
|
||||
|
||||
const result = await preSendUserFields.call(loadOptionsFunctions, requestOptions);
|
||||
|
||||
expect(result.body).toEqual(
|
||||
JSON.stringify({
|
||||
someField: 'value',
|
||||
Username: 'test@example.com',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the request body with the correct username when operation is "update" and user exists', async () => {
|
||||
requestOptions = {
|
||||
body: JSON.stringify({ someField: 'value' }),
|
||||
method: 'POST',
|
||||
url: '',
|
||||
headers: {},
|
||||
};
|
||||
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'update';
|
||||
if (name === 'user') return 'existing-user';
|
||||
if (name === 'userPool') return 'eu-central-1_KkXQgdCJv';
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({
|
||||
UserPool: { UsernameAttributes: ['email'] },
|
||||
});
|
||||
|
||||
(searchUsers as jest.Mock).mockResolvedValue({
|
||||
results: [{ name: 'existing-user', value: 'existing-user' }],
|
||||
});
|
||||
|
||||
const result = await preSendUserFields.call(loadOptionsFunctions, requestOptions);
|
||||
|
||||
expect(result.body).toEqual(
|
||||
JSON.stringify({
|
||||
someField: 'value',
|
||||
Username: 'existing-user',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the request body without a username when operation is "update" and no user is found', async () => {
|
||||
requestOptions = {
|
||||
body: JSON.stringify({ someField: 'value' }),
|
||||
method: 'POST',
|
||||
url: '',
|
||||
headers: {},
|
||||
};
|
||||
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementationOnce((name: string) => {
|
||||
if (name === 'operation') return 'update';
|
||||
if (name === 'user') return 'non-existing-user';
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({
|
||||
UserPool: { UsernameAttributes: ['email'] },
|
||||
});
|
||||
|
||||
(searchUsers as jest.Mock).mockResolvedValue({
|
||||
results: [],
|
||||
});
|
||||
|
||||
const result = await preSendUserFields.call(loadOptionsFunctions, requestOptions);
|
||||
|
||||
expect(result.body).toEqual(
|
||||
JSON.stringify({
|
||||
someField: 'value',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('preSendAttributes', () => {
|
||||
beforeEach(() => {
|
||||
requestOptions = {
|
||||
body: JSON.stringify({ someField: 'value' }),
|
||||
method: 'POST',
|
||||
url: '',
|
||||
headers: {},
|
||||
};
|
||||
});
|
||||
|
||||
it('should throw an error if no user attributes are provided (update operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'update';
|
||||
if (name === 'userAttributes.attributes') return [];
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendAttributes.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError(
|
||||
new NodeOperationError(loadOptionsFunctions.getNode(), 'No user attributes provided', {
|
||||
description: 'At least one user attribute must be provided for the update operation.',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if a user attribute is invalid (empty value) (create operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [{ attributeType: 'standard', standardName: 'email', value: '' }];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendAttributes.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError(
|
||||
new NodeOperationError(loadOptionsFunctions.getNode(), 'Invalid User Attribute', {
|
||||
description: 'Each attribute must have a valid name and value.',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if email_verified is true but email is missing (create operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [{ attributeType: 'standard', standardName: 'email_verified', value: 'true' }];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendAttributes.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError(
|
||||
new NodeOperationError(
|
||||
loadOptionsFunctions.getNode(),
|
||||
'Missing required "email" attribute',
|
||||
{
|
||||
description:
|
||||
'"email_verified" is set to true, but the corresponding "email" attribute is not provided.',
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if phone_number_verified is true but phone_number is missing (create operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [
|
||||
{ attributeType: 'standard', standardName: 'phone_number_verified', value: 'true' },
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendAttributes.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError(
|
||||
new NodeOperationError(
|
||||
loadOptionsFunctions.getNode(),
|
||||
'Missing required "phone_number" attribute',
|
||||
{
|
||||
description:
|
||||
'"phone_number_verified" is set to true, but the corresponding "phone_number" attribute is not provided.',
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('should add the user attribute to the body when valid (create operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [
|
||||
{ attributeType: 'standard', standardName: 'email', value: 'test@example.com' },
|
||||
{ attributeType: 'standard', standardName: 'phone_number', value: '1234567890' },
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await preSendAttributes.call(loadOptionsFunctions, requestOptions);
|
||||
|
||||
expect(result.body).toEqual(
|
||||
JSON.stringify({
|
||||
someField: 'value',
|
||||
UserAttributes: [
|
||||
{ Name: 'email', Value: 'test@example.com' },
|
||||
{ Name: 'phone_number', Value: '1234567890' },
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly process custom attributes (create operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [
|
||||
{
|
||||
attributeType: 'custom',
|
||||
customName: 'custom_attr',
|
||||
value: 'custom_value',
|
||||
},
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await preSendAttributes.call(loadOptionsFunctions, requestOptions);
|
||||
|
||||
expect(result.body).toEqual(
|
||||
JSON.stringify({
|
||||
someField: 'value',
|
||||
UserAttributes: [{ Name: 'custom:custom_attr', Value: 'custom_value' }],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if a custom attribute has no name or value (create operation)', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'operation') return 'create';
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [{ attributeType: 'custom', customName: '', value: '' }];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendAttributes.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError(
|
||||
new NodeOperationError(loadOptionsFunctions.getNode(), 'Invalid User Attribute', {
|
||||
description: 'Each attribute must have a valid name and value.',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('preSendDesiredDeliveryMediums', () => {
|
||||
beforeEach(() => {
|
||||
requestOptions = { body: {}, url: '' };
|
||||
});
|
||||
|
||||
it('should not throw an error if EMAIL is selected and email is provided', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.desiredDeliveryMediums') {
|
||||
return ['EMAIL'];
|
||||
}
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [
|
||||
{ standardName: 'email', value: 'test@example.com' },
|
||||
{ standardName: 'phone_number', value: '1234567890' },
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendDesiredDeliveryMediums.call(loadOptionsFunctions, requestOptions),
|
||||
).resolves.toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should throw an error if EMAIL is selected but email is missing', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.desiredDeliveryMediums') {
|
||||
return ['EMAIL'];
|
||||
}
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [{ standardName: 'phone_number', value: '1234567890' }];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendDesiredDeliveryMediums.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError('Missing required "email" attribute');
|
||||
});
|
||||
|
||||
it('should not throw an error if SMS is selected and phone number is provided', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.desiredDeliveryMediums') {
|
||||
return ['SMS'];
|
||||
}
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [
|
||||
{ standardName: 'email', value: 'test@example.com' },
|
||||
{ standardName: 'phone_number', value: '1234567890' },
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendDesiredDeliveryMediums.call(loadOptionsFunctions, requestOptions),
|
||||
).resolves.toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should throw an error if SMS is selected but phone number is missing', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.desiredDeliveryMediums') {
|
||||
return ['SMS'];
|
||||
}
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [{ standardName: 'email', value: 'test@example.com' }];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendDesiredDeliveryMediums.call(loadOptionsFunctions, requestOptions),
|
||||
).rejects.toThrowError('Missing required "phone_number" attribute');
|
||||
});
|
||||
|
||||
it('should not throw an error if both EMAIL and SMS are selected and both attributes are provided', async () => {
|
||||
loadOptionsFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
if (name === 'additionalFields.desiredDeliveryMediums') {
|
||||
return ['EMAIL', 'SMS'];
|
||||
}
|
||||
if (name === 'additionalFields.userAttributes.attributes') {
|
||||
return [
|
||||
{ standardName: 'email', value: 'test@example.com' },
|
||||
{ standardName: 'phone_number', value: '1234567890' },
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
preSendDesiredDeliveryMediums.call(loadOptionsFunctions, requestOptions),
|
||||
).resolves.toEqual(requestOptions);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,252 @@
|
||||
import {
|
||||
ApplicationError,
|
||||
type ILoadOptionsFunctions,
|
||||
type INodeListSearchResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import type { IUserPool } from '../../helpers/interfaces';
|
||||
import {
|
||||
searchUsers,
|
||||
searchGroups,
|
||||
searchUserPools,
|
||||
searchGroupsForUser,
|
||||
} from '../../methods/listSearch';
|
||||
import { awsApiRequest, awsApiRequestAllItems } from '../../transport/index';
|
||||
|
||||
jest.mock('../../transport/index', () => ({
|
||||
awsApiRequest: jest.fn(),
|
||||
awsApiRequestAllItems: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('AWS Cognito Functions', () => {
|
||||
describe('searchUsers', () => {
|
||||
it('should return user results when users are found', async () => {
|
||||
const mockDescribeUserPoolResponse = {
|
||||
UserPool: {
|
||||
UsernameAttributes: ['email'],
|
||||
},
|
||||
};
|
||||
|
||||
const mockResponse = {
|
||||
Users: [
|
||||
{
|
||||
Username: 'User1',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'user1@example.com' },
|
||||
{ Name: 'phone_number', Value: '1234567890' },
|
||||
{ Name: 'sub', Value: 'sub1' },
|
||||
],
|
||||
},
|
||||
{
|
||||
Username: 'User2',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'user2@example.com' },
|
||||
{ Name: 'phone_number', Value: '9876543210' },
|
||||
{ Name: 'sub', Value: 'sub2' },
|
||||
],
|
||||
},
|
||||
],
|
||||
NextToken: 'next-token',
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock)
|
||||
.mockResolvedValueOnce(mockDescribeUserPoolResponse)
|
||||
.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
if (param === 'userPool') {
|
||||
return 'user-pool-id';
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
const result = await searchUsers.call(mockContext, '', '');
|
||||
|
||||
const expectedResult: INodeListSearchResult = {
|
||||
results: [
|
||||
{ name: 'user1@example.com', value: 'sub1' },
|
||||
{ name: 'user2@example.com', value: 'sub2' },
|
||||
],
|
||||
paginationToken: 'next-token',
|
||||
};
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
expect(awsApiRequest).toHaveBeenCalledWith(
|
||||
'POST',
|
||||
'ListUsers',
|
||||
expect.stringContaining('UserPoolId'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if UserPoolId is missing', async () => {
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn().mockReturnValue(''),
|
||||
getNode: jest.fn(),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
await expect(searchUsers.call(mockContext)).rejects.toThrow(
|
||||
'User Pool ID is required to search users',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchGroups', () => {
|
||||
it('should return group results when groups are found', async () => {
|
||||
const mockResponse = {
|
||||
Groups: [
|
||||
{ GroupName: 'Group1', UserPoolId: 'user-pool-id' },
|
||||
{ GroupName: 'Group2', UserPoolId: 'user-pool-id' },
|
||||
],
|
||||
NextToken: 'next-token',
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
if (param === 'userPool') {
|
||||
return { value: 'user-pool-id' };
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
const result = await searchGroups.call(mockContext, '', '');
|
||||
|
||||
const expectedResult: INodeListSearchResult = {
|
||||
results: [
|
||||
{ name: 'Group1', value: 'Group1' },
|
||||
{ name: 'Group2', value: 'Group2' },
|
||||
],
|
||||
paginationToken: 'next-token',
|
||||
};
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
expect(awsApiRequest).toHaveBeenCalledWith(
|
||||
'POST',
|
||||
'ListGroups',
|
||||
expect.stringContaining('UserPoolId'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if UserPoolId is missing', async () => {
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn().mockReturnValue(null),
|
||||
getNode: jest.fn(),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
await expect(searchGroups.call(mockContext)).rejects.toThrow(ApplicationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchUserPools', () => {
|
||||
it('should return user pool results when user pools are found', async () => {
|
||||
const mockResponse = {
|
||||
UserPools: [
|
||||
{ Id: 'pool1', Name: 'User Pool 1' },
|
||||
{ Id: 'pool2', Name: 'User Pool 2' },
|
||||
],
|
||||
NextToken: 'next-token',
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
if (param === 'userPool') {
|
||||
return { value: 'user-pool-id' };
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
const result = await searchUserPools.call(mockContext, '', '');
|
||||
|
||||
const expectedResult: INodeListSearchResult = {
|
||||
results: [
|
||||
{ name: 'UserPool1', value: 'pool1' },
|
||||
{ name: 'UserPool2', value: 'pool2' },
|
||||
],
|
||||
paginationToken: 'next-token',
|
||||
};
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
expect(awsApiRequest).toHaveBeenCalledWith(
|
||||
'POST',
|
||||
'ListUserPools',
|
||||
expect.stringContaining('Limit'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchGroupsForUser', () => {
|
||||
const userName = '03a438f2-10d1-70f1-f45a-09753ab5c4c3';
|
||||
const userPoolId = 'eu-central-1_KkXQgdCJv';
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
if (param === 'user') return userName;
|
||||
if (param === 'userPool') return userPoolId;
|
||||
return null;
|
||||
}),
|
||||
getNode: jest.fn(() => ({
|
||||
name: 'mockNode',
|
||||
})),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
(awsApiRequest as jest.Mock).mockResolvedValueOnce({
|
||||
UserPool: {
|
||||
Id: userPoolId,
|
||||
UsernameAttributes: ['email', 'phone_number'],
|
||||
},
|
||||
} as { UserPool: IUserPool });
|
||||
});
|
||||
|
||||
it('should handle empty groups response', async () => {
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValueOnce([]);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext, '');
|
||||
|
||||
const expectedResult: INodeListSearchResult = {
|
||||
results: [],
|
||||
};
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should return filtered and sorted group list', async () => {
|
||||
const mockGroups = [
|
||||
{ GroupName: 'Developers' },
|
||||
{ GroupName: 'Admins' },
|
||||
{ GroupName: 'Guests' },
|
||||
];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValueOnce(mockGroups);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext, 'dev');
|
||||
|
||||
expect(result).toEqual({
|
||||
results: [{ name: 'Developers', value: 'Developers' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all groups when no filter is passed', async () => {
|
||||
const mockGroups = [{ GroupName: 'Zeta' }, { GroupName: 'Alpha' }, { GroupName: 'Beta' }];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValueOnce(mockGroups);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: [
|
||||
{ name: 'Alpha', value: 'Alpha' },
|
||||
{ name: 'Beta', value: 'Beta' },
|
||||
{ name: 'Zeta', value: 'Zeta' },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Add User to Group', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_W3WwpiBXV',
|
||||
CreationDate: 1739530218.869,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 4,
|
||||
Id: 'eu-central-1_W3WwpiBXV',
|
||||
LastModifiedDate: 1739530218.869,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolSimple',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
Filter: 'sub = "0394e8e2-5081-7020-06bd-44bdfc84dd10"',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: '0394e8e2-5081-7020-06bd-44bdfc84dd10',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'UserSimple' },
|
||||
{ Name: 'Sub', Value: '0394e8e2-5081-7020-06bd-44bdfc84dd10' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
Username: '0394e8e2-5081-7020-06bd-44bdfc84dd10',
|
||||
GroupName: 'MyNewGroupSimple',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminAddUserToGroup')
|
||||
.reply(200, {});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['addToGroup.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, -100],
|
||||
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "addToGroup",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_W3WwpiBXV",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolSimple"
|
||||
},
|
||||
"user": {
|
||||
"__rl": true,
|
||||
"value": "0394e8e2-5081-7020-06bd-44bdfc84dd10",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserSimple"
|
||||
},
|
||||
"group": {
|
||||
"__rl": true,
|
||||
"value": "MyNewGroupSimple",
|
||||
"mode": "list",
|
||||
"cachedResultName": "MyNewGroupSimple"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [100, -100],
|
||||
"id": "0accb395-3cdb-4c3f-8adc-1a47567c37b5",
|
||||
"name": "AWS Cognito",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AWS Cognito",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"AWS Cognito": [
|
||||
{
|
||||
"json": {
|
||||
"addedToGroup": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Create User', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Id: 'eu-central-1_W3WwpiBXV',
|
||||
Name: 'MyUserPool',
|
||||
CreationDate: 1627891230,
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
Username: 'Johnn12',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminCreateUser')
|
||||
.reply(200, {
|
||||
User: {
|
||||
Username: 'Johnn12',
|
||||
UserStatus: 'FORCE_CHANGE_PASSWORD',
|
||||
Attributes: [
|
||||
{
|
||||
Name: 'sub',
|
||||
Value: '03d43812-00c1-7098-075e-8a535fdefc1b',
|
||||
},
|
||||
],
|
||||
UserCreateDate: 1743068750.761,
|
||||
UserLastModifiedDate: 1743068750.761,
|
||||
Enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['create.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [260, 360],
|
||||
"id": "9ed2b86b-7c24-4ea0-a328-92d9e6dba35a",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "create",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_W3WwpiBXV",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolSimple"
|
||||
},
|
||||
"newUserName": "Johnn12",
|
||||
"additionalFields": {},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"id": "ea53747b-2354-486c-9a9d-bac3dd88bacb",
|
||||
"name": "createUser",
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [460, 360],
|
||||
"alwaysOutputData": true,
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"createUser": [
|
||||
{
|
||||
"json": {
|
||||
"Attributes": [
|
||||
{
|
||||
"Name": "sub",
|
||||
"Value": "03d43812-00c1-7098-075e-8a535fdefc1b"
|
||||
}
|
||||
],
|
||||
"Enabled": true,
|
||||
"UserCreateDate": 1743068750.761,
|
||||
"UserLastModifiedDate": 1743068750.761,
|
||||
"UserStatus": "FORCE_CHANGE_PASSWORD",
|
||||
"Username": "Johnn12"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "createUser",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Delete User', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_EUZ4iEF1T',
|
||||
CreationDate: 1739530218.869,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 4,
|
||||
Id: 'eu-central-1_EUZ4iEF1T',
|
||||
LastModifiedDate: 1739530218.869,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolTwo',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Filter: 'sub = "53c4f8c2-c071-707b-debd-d45585618da0"',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: '53c4f8c2-c071-707b-debd-d45585618da0',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'UserSimple' },
|
||||
{ Name: 'Sub', Value: '53c4f8c2-c071-707b-debd-d45585618da0' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Username: '53c4f8c2-c071-707b-debd-d45585618da0',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminDeleteUser')
|
||||
.reply(200, {
|
||||
Message: 'User successfully deleted',
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['delete.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, -100],
|
||||
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "delete",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_EUZ4iEF1T",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolTwo"
|
||||
},
|
||||
"user": {
|
||||
"__rl": true,
|
||||
"value": "53c4f8c2-c071-707b-debd-d45585618da0",
|
||||
"mode": "list",
|
||||
"cachedResultName": "userName12"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 40],
|
||||
"id": "bc3483ef-4922-4759-a51d-12e5bd996628",
|
||||
"name": "AWS Cognito",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AWS Cognito",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"AWS Cognito": [
|
||||
{
|
||||
"json": {
|
||||
"deleted": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
83
packages/nodes-base/nodes/Aws/Cognito/test/user/get.test.ts
Normal file
83
packages/nodes-base/nodes/Aws/Cognito/test/user/get.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Get User', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_EUZ4iEF1T',
|
||||
CreationDate: 1739530218.869,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 4,
|
||||
Id: 'eu-central-1_EUZ4iEF1T',
|
||||
LastModifiedDate: 1739530218.869,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolSimple',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Filter: 'sub = "b30498c2-d0f1-70a8-4b0c-3da25a3b998f"',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: 'userName10',
|
||||
UserAttributes: [
|
||||
{ Name: 'sub', Value: 'b30498c2-d0f1-70a8-4b0c-3da25a3b998f' },
|
||||
{ Name: 'family_name', Value: 'New FamilyName 2' },
|
||||
],
|
||||
UserCreateDate: 1744206331.569,
|
||||
UserLastModifiedDate: 1744206366.034,
|
||||
Enabled: true,
|
||||
UserStatus: 'FORCE_CHANGE_PASSWORD',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Username: 'userName10',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminGetUser')
|
||||
.reply(200, {
|
||||
Username: 'userName10',
|
||||
UserAttributes: [
|
||||
{ Name: 'sub', Value: 'b30498c2-d0f1-70a8-4b0c-3da25a3b998f' },
|
||||
{ Name: 'family_name', Value: 'New FamilyName 2' },
|
||||
],
|
||||
UserCreateDate: 1744206331.569,
|
||||
UserLastModifiedDate: 1744206366.034,
|
||||
Enabled: true,
|
||||
UserStatus: 'FORCE_CHANGE_PASSWORD',
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['get.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4570d7a2-f10a-495d-8a0e-8520b638649e",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-780, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "get",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_EUZ4iEF1T",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolTwo"
|
||||
},
|
||||
"user": {
|
||||
"__rl": true,
|
||||
"value": "b30498c2-d0f1-70a8-4b0c-3da25a3b998f",
|
||||
"mode": "list",
|
||||
"cachedResultName": "userName10"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"id": "2105b4ec-0db4-40f5-9d38-78c0fe20708f",
|
||||
"name": "getUser",
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [-620, 500],
|
||||
"alwaysOutputData": true,
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "exampleId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "getUser",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"getUser": [
|
||||
{
|
||||
"json": {
|
||||
"Enabled": true,
|
||||
"UserCreateDate": 1744206331.569,
|
||||
"UserLastModifiedDate": 1744206366.034,
|
||||
"UserStatus": "FORCE_CHANGE_PASSWORD",
|
||||
"Username": "userName10",
|
||||
"family_name": "New FamilyName 2",
|
||||
"sub": "b30498c2-d0f1-70a8-4b0c-3da25a3b998f"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Get All Users', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_KkXQgdCJv',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: '034448d2-4011-7079-9474-9a4fccd4247a',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'FinalUser@gmail.com' },
|
||||
{ Name: 'Sub', Value: '034448d2-4011-7079-9474-9a4fccd4247a' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
{
|
||||
Username: '03a438f2-10d1-70f1-f45a-09753ab5c4c3',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'mail.this1@gmail.com' },
|
||||
{ Name: 'Sub', Value: '03a438f2-10d1-70f1-f45a-09753ab5c4c3' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1733746687.223 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1733746687.223 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
{
|
||||
Username: '03f438d2-b0f1-70bc-04d9-f6dd31f2d878',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'test3@gmail.com' },
|
||||
{ Name: 'Sub', Value: '03f438d2-b0f1-70bc-04d9-f6dd31f2d878' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1742928785.796 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1742928785.796 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['getAll.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [260, 360],
|
||||
"id": "9ed2b86b-7c24-4ea0-a328-92d9e6dba35a",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_KkXQgdCJv",
|
||||
"mode": "list",
|
||||
"cachedResultName": "AWS test"
|
||||
},
|
||||
"returnAll": true,
|
||||
"requestOptions": {}
|
||||
},
|
||||
"id": "e788623f-5d4a-4e76-b7f6-3e9389e2fe29",
|
||||
"name": "getAllUsers",
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [460, 360],
|
||||
"alwaysOutputData": true,
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"getAllUsers": [
|
||||
{
|
||||
"json": {
|
||||
"Enabled": true,
|
||||
"UserCreateDate": 1736343033.226,
|
||||
"UserLastModifiedDate": 1736343033.226,
|
||||
"UserStatus": "FORCE_CHANGE_PASSWORD",
|
||||
"Username": "034448d2-4011-7079-9474-9a4fccd4247a",
|
||||
"email": "FinalUser@gmail.com",
|
||||
"Sub": "034448d2-4011-7079-9474-9a4fccd4247a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"Enabled": true,
|
||||
"UserCreateDate": 1733746687.223,
|
||||
"UserLastModifiedDate": 1733746687.223,
|
||||
"UserStatus": "FORCE_CHANGE_PASSWORD",
|
||||
"Username": "03a438f2-10d1-70f1-f45a-09753ab5c4c3",
|
||||
"email": "mail.this1@gmail.com",
|
||||
"Sub": "03a438f2-10d1-70f1-f45a-09753ab5c4c3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"Enabled": true,
|
||||
"UserCreateDate": 1742928785.796,
|
||||
"UserLastModifiedDate": 1742928785.796,
|
||||
"UserStatus": "FORCE_CHANGE_PASSWORD",
|
||||
"Username": "03f438d2-b0f1-70bc-04d9-f6dd31f2d878",
|
||||
"email": "test3@gmail.com",
|
||||
"Sub": "03f438d2-b0f1-70bc-04d9-f6dd31f2d878"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "getAllUsers",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Remove User From Group', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_W3WwpiBXV',
|
||||
CreationDate: 1739530218.869,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 4,
|
||||
Id: 'eu-central-1_W3WwpiBXV',
|
||||
LastModifiedDate: 1739530218.869,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolSimple',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
Filter: 'sub = "0394e8e2-5081-7020-06bd-44bdfc84dd10"',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: '0394e8e2-5081-7020-06bd-44bdfc84dd10',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'UserSimple' },
|
||||
{ Name: 'Sub', Value: '0394e8e2-5081-7020-06bd-44bdfc84dd10' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
Username: '0394e8e2-5081-7020-06bd-44bdfc84dd10',
|
||||
GroupName: 'MyNewGroupSimple',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminRemoveUserFromGroup')
|
||||
.reply(200, {});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['removeFromGroup.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, -100],
|
||||
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeFromGroup",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_W3WwpiBXV",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolSimple"
|
||||
},
|
||||
"user": {
|
||||
"__rl": true,
|
||||
"value": "0394e8e2-5081-7020-06bd-44bdfc84dd10",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserSimple"
|
||||
},
|
||||
"group": {
|
||||
"__rl": true,
|
||||
"value": "MyNewGroupSimple",
|
||||
"mode": "list",
|
||||
"cachedResultName": "MyNewGroupSimple"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [100, -100],
|
||||
"id": "0accb395-3cdb-4c3f-8adc-1a47567c37b5",
|
||||
"name": "AWS Cognito",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AWS Cognito",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"AWS Cognito": [
|
||||
{
|
||||
"json": {
|
||||
"removedFromGroup": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Update User', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_EUZ4iEF1T',
|
||||
CreationDate: 1739530218.869,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 4,
|
||||
Id: 'eu-central-1_EUZ4iEF1T',
|
||||
LastModifiedDate: 1739530218.869,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolTwo',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Filter: 'sub = "43045822-80e1-70f6-582d-78ae7992e9d9"',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: '43045822-80e1-70f6-582d-78ae7992e9d9',
|
||||
Attributes: [
|
||||
{ Name: 'email', Value: 'UserSimple' },
|
||||
{ Name: 'Sub', Value: '43045822-80e1-70f6-582d-78ae7992e9d9' },
|
||||
{ Name: 'Enabled', Value: true },
|
||||
{ Name: 'UserCreateDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserLastModifiedDate', Value: 1736343033.226 },
|
||||
{ Name: 'UserStatus', Value: 'FORCE_CHANGE_PASSWORD' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Username: '43045822-80e1-70f6-582d-78ae7992e9d9',
|
||||
UserAttributes: [
|
||||
{
|
||||
Name: 'address',
|
||||
Value: 'New',
|
||||
},
|
||||
],
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminUpdateUserAttributes')
|
||||
.reply(200, {});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['update.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, -100],
|
||||
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
|
||||
"name": "When clicking ‘Test workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "update",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_EUZ4iEF1T",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolTwo"
|
||||
},
|
||||
"user": {
|
||||
"__rl": true,
|
||||
"value": "43045822-80e1-70f6-582d-78ae7992e9d9",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Johnn"
|
||||
},
|
||||
"userAttributes": {
|
||||
"attributes": [
|
||||
{
|
||||
"standardName": "address",
|
||||
"value": "New"
|
||||
}
|
||||
]
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [120, -100],
|
||||
"id": "4c6bdeea-f863-4974-b3d6-7dfc04f78df3",
|
||||
"name": "AWS Cognito",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "exampleId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AWS Cognito",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"AWS Cognito": [
|
||||
{
|
||||
"json": {
|
||||
"updated": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('AWS Cognito - Get User Pool', () => {
|
||||
beforeEach(() => {
|
||||
const baseUrl = 'https://cognito-idp.eu-central-1.amazonaws.com/';
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_W3WwpiBXV',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.DescribeUserPool')
|
||||
.reply(200, {
|
||||
UserPool: {
|
||||
Arn: 'arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_W3WwpiBXV',
|
||||
CreationDate: 1739527771.267,
|
||||
DeletionProtection: 'INACTIVE',
|
||||
EstimatedNumberOfUsers: 8,
|
||||
Id: 'eu-central-1_W3WwpiBXV',
|
||||
LastModifiedDate: 1739527771.267,
|
||||
MfaConfiguration: 'OFF',
|
||||
Name: 'UserPoolSimple',
|
||||
},
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Limit: 50,
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.ListUsers')
|
||||
.reply(200, {
|
||||
Users: [
|
||||
{
|
||||
Username: 'userName10',
|
||||
UserAttributes: [
|
||||
{ Name: 'sub', Value: 'b30498c2-d0f1-70a8-4b0c-3da25a3b998f' },
|
||||
{ Name: 'family_name', Value: 'New FamilyName 2' },
|
||||
],
|
||||
UserCreateDate: 1744206331.569,
|
||||
UserLastModifiedDate: 1744206366.034,
|
||||
Enabled: true,
|
||||
UserStatus: 'FORCE_CHANGE_PASSWORD',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
|
||||
.post('/', {
|
||||
UserPoolId: 'eu-central-1_EUZ4iEF1T',
|
||||
Username: 'b30498c2-d0f1-70a8-4b0c-3da25a3b998f',
|
||||
})
|
||||
.matchHeader('x-amz-target', 'AWSCognitoIdentityProviderService.AdminGetUser')
|
||||
.reply(200, {
|
||||
Username: 'userName10',
|
||||
UserAttributes: [
|
||||
{ Name: 'sub', Value: 'b30498c2-d0f1-70a8-4b0c-3da25a3b998f' },
|
||||
{ Name: 'family_name', Value: 'New FamilyName 2' },
|
||||
],
|
||||
UserCreateDate: 1744206331.569,
|
||||
UserLastModifiedDate: 1744206366.034,
|
||||
Enabled: true,
|
||||
UserStatus: 'FORCE_CHANGE_PASSWORD',
|
||||
});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['get.workflow.json'],
|
||||
credentials: {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'test',
|
||||
secretAccessKey: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4570d7a2-f10a-495d-8a0e-8520b638649e",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-700, 320]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "userPool",
|
||||
"userPool": {
|
||||
"__rl": true,
|
||||
"value": "eu-central-1_W3WwpiBXV",
|
||||
"mode": "list",
|
||||
"cachedResultName": "UserPoolSimple"
|
||||
},
|
||||
"requestOptions": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.awsCognito",
|
||||
"typeVersion": 1,
|
||||
"position": [-700, 500],
|
||||
"id": "fc734bb9-acd3-4b7d-a2a3-0a7b664e3c5a",
|
||||
"name": "AWS Cognito2",
|
||||
"credentials": {
|
||||
"aws": {
|
||||
"id": "testId",
|
||||
"name": "AWS account Central Europe"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AWS Cognito2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"AWS Cognito2": [
|
||||
{
|
||||
"json": {
|
||||
"Arn": "arn:aws:cognito-idp:eu-central-1:130450532146:userpool/eu-central-1_W3WwpiBXV",
|
||||
"CreationDate": 1739527771.267,
|
||||
"DeletionProtection": "INACTIVE",
|
||||
"EstimatedNumberOfUsers": 8,
|
||||
"Id": "eu-central-1_W3WwpiBXV",
|
||||
"LastModifiedDate": 1739527771.267,
|
||||
"MfaConfiguration": "OFF",
|
||||
"Name": "UserPoolSimple"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
72
packages/nodes-base/nodes/Aws/Cognito/transport/index.ts
Normal file
72
packages/nodes-base/nodes/Aws/Cognito/transport/index.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type {
|
||||
ILoadOptionsFunctions,
|
||||
IPollFunctions,
|
||||
IHttpRequestOptions,
|
||||
IExecuteSingleFunctions,
|
||||
IDataObject,
|
||||
IHttpRequestMethods,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import type { AwsCredentialsType } from '../../../../credentials/Aws.credentials';
|
||||
|
||||
export async function awsApiRequest(
|
||||
this: ILoadOptionsFunctions | IPollFunctions | IExecuteSingleFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
action: string,
|
||||
body: string,
|
||||
): Promise<any> {
|
||||
const credentialsType = 'aws';
|
||||
const credentials = await this.getCredentials<AwsCredentialsType>(credentialsType);
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: '',
|
||||
method,
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-amz-json-1.1',
|
||||
'X-Amz-Target': `AWSCognitoIdentityProviderService.${action}`,
|
||||
},
|
||||
qs: {
|
||||
service: 'cognito-idp',
|
||||
_region: credentials.region,
|
||||
},
|
||||
};
|
||||
|
||||
return await this.helpers.httpRequestWithAuthentication.call(
|
||||
this,
|
||||
credentialsType,
|
||||
requestOptions,
|
||||
);
|
||||
}
|
||||
|
||||
export async function awsApiRequestAllItems(
|
||||
this: ILoadOptionsFunctions | IPollFunctions | IExecuteSingleFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
action: string,
|
||||
body: IDataObject,
|
||||
propertyName: string,
|
||||
): Promise<IDataObject[]> {
|
||||
const returnData: IDataObject[] = [];
|
||||
let nextToken: string | undefined;
|
||||
|
||||
do {
|
||||
const requestBody: IDataObject = {
|
||||
...body,
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
};
|
||||
|
||||
const response = (await awsApiRequest.call(
|
||||
this,
|
||||
method,
|
||||
action,
|
||||
JSON.stringify(requestBody),
|
||||
)) as IDataObject;
|
||||
|
||||
const items = (response[propertyName] ?? []) as IDataObject[];
|
||||
returnData.push(...items);
|
||||
|
||||
nextToken = response.NextToken as string | undefined;
|
||||
} while (nextToken);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
@@ -430,6 +430,7 @@
|
||||
"dist/nodes/Aws/AwsSns.node.js",
|
||||
"dist/nodes/Aws/AwsSnsTrigger.node.js",
|
||||
"dist/nodes/Aws/CertificateManager/AwsCertificateManager.node.js",
|
||||
"dist/nodes/Aws/Cognito/AwsCognito.node.js",
|
||||
"dist/nodes/Aws/Comprehend/AwsComprehend.node.js",
|
||||
"dist/nodes/Aws/DynamoDB/AwsDynamoDB.node.js",
|
||||
"dist/nodes/Aws/ELB/AwsElb.node.js",
|
||||
|
||||
Reference in New Issue
Block a user