Fixed some issues after feedback

This commit is contained in:
Adina Totorean 2025-01-06 15:38:03 +02:00
parent f3bcf18ce5
commit abdd1521c0
4 changed files with 456 additions and 210 deletions

View file

@ -14,6 +14,14 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { ApplicationError, jsonParse, NodeApiError, NodeOperationError } from 'n8n-workflow'; import { ApplicationError, jsonParse, NodeApiError, NodeOperationError } from 'n8n-workflow';
export async function presendTest(
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
console.log('requestOptions', requestOptions);
return requestOptions;
}
/* /*
* Helper function which stringifies the body before sending the request. * Helper function which stringifies the body before sending the request.
* It is added to the routing property in the "resource" parameter thus for all requests. * It is added to the routing property in the "resource" parameter thus for all requests.
@ -33,14 +41,20 @@ export async function presendFilter(
requestOptions: IHttpRequestOptions, requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> { ): Promise<IHttpRequestOptions> {
const additionalFields = this.getNodeParameter('additionalFields', {}) as IDataObject; const additionalFields = this.getNodeParameter('additionalFields', {}) as IDataObject;
const filterAttribute = additionalFields.filterAttribute as string; const filterAttribute = additionalFields.filters as string;
let filterType = additionalFields.filterType as string; let filterType = additionalFields.filterType as string;
const filterValue = additionalFields.filterValue as string; const filterValue = additionalFields.filterValue as string;
if (!filterAttribute || !filterType || !filterValue) { const hasAnyFilter = filterAttribute || filterType || filterValue;
if (!hasAnyFilter) {
return requestOptions;
}
if (hasAnyFilter && (!filterAttribute || !filterType || !filterValue)) {
throw new NodeOperationError( throw new NodeOperationError(
this.getNode(), this.getNode(),
'Please provide Filter Attribute, Filter Type, and Filter Value to use filtering.', 'If filtering is used, please provide Filter Attribute, Filter Type, and Filter Value.',
); );
} }
@ -69,25 +83,25 @@ export async function presendFilter(
return requestOptions; return requestOptions;
} }
export async function presendOptions( export async function presendAdditionalFields(
this: IExecuteSingleFunctions, this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions, requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> { ): Promise<IHttpRequestOptions> {
const options = this.getNodeParameter('options', {}) as IDataObject; const additionalFields = this.getNodeParameter('additionalFields', {}) as IDataObject;
const hasOptions = options.Description || options.Precedence || options.Path || options.RoleArn; const hasOptions = Object.keys(additionalFields).length > 0;
if (!hasOptions) { if (!hasOptions) {
throw new NodeOperationError( throw new NodeOperationError(
this.getNode(), this.getNode(),
'At least one of the options (Description, Precedence, Path, or RoleArn) must be provided to update the group.', 'At least one of the additional fields must be provided to update the group.',
); );
} }
return requestOptions; return requestOptions;
} }
export async function presendPath( export async function presendVerifyPath(
this: IExecuteSingleFunctions, this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions, requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> { ): Promise<IHttpRequestOptions> {
@ -325,7 +339,7 @@ export async function handleErrorPostReceive(
/* Helper function used in listSearch methods */ /* Helper function used in listSearch methods */
export async function awsRequest( export async function awsRequest(
this: ILoadOptionsFunctions | IPollFunctions, this: ILoadOptionsFunctions | IPollFunctions | IExecuteSingleFunctions,
opts: IHttpRequestOptions, opts: IHttpRequestOptions,
): Promise<IDataObject> { ): Promise<IDataObject> {
const region = (await this.getCredentials('aws')).region as string; const region = (await this.getCredentials('aws')).region as string;
@ -376,7 +390,6 @@ export async function awsRequest(
} }
} }
/* listSearch methods */
export async function searchUserPools( export async function searchUserPools(
this: ILoadOptionsFunctions, this: ILoadOptionsFunctions,
filter?: string, filter?: string,
@ -448,7 +461,6 @@ export async function searchUsers(
const users = responseData.Users as IDataObject[] | undefined; const users = responseData.Users as IDataObject[] | undefined;
if (!users) { if (!users) {
console.warn('No users found in the response');
return { results: [] }; return { results: [] };
} }
@ -529,3 +541,283 @@ export async function searchGroups(
return { results, paginationToken: responseData.NextToken }; return { results, paginationToken: responseData.NextToken };
} }
export async function simplifyData(
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
const simple = this.getNodeParameter('simple') as boolean;
type UserPool = {
Arn: string;
CreationDate: number;
DeletionProtection: string;
Domain: string;
EstimatedNumberOfUsers: number;
Id: string;
LastModifiedDate: number;
MfaConfiguration: string;
Name: string;
};
type User = {
Enabled: boolean;
UserAttributes?: Array<{ Name: string; Value: string }>;
Attributes?: Array<{ Name: string; Value: string }>;
UserCreateDate: number;
UserLastModifiedDate: number;
UserStatus: string;
Username: string;
};
function mapUserAttributes(userAttributes: Array<{ Name: string; Value: string }>): {
[key: string]: string;
} {
return userAttributes?.reduce(
(acc, { Name, Value }) => {
if (Name !== 'sub') {
acc[Name] = Value;
}
return acc;
},
{} as { [key: string]: string },
);
}
if (!simple) {
return items;
}
const resource = this.getNodeParameter('resource');
const operation = this.getNodeParameter('operation');
const simplifiedItems = items
.map((item) => {
const data = item.json?.UserPool as UserPool | undefined;
const userData = item.json as User | undefined;
const users = item.json?.Users as User[] | undefined;
switch (resource) {
case 'userPool':
if (data) {
return {
json: {
UserPool: {
Arn: data.Arn,
CreationDate: data.CreationDate,
DeletionProtection: data.DeletionProtection,
Domain: data.Domain,
EstimatedNumberOfUsers: data.EstimatedNumberOfUsers,
Id: data.Id,
LastModifiedDate: data.LastModifiedDate,
MfaConfiguration: data.MfaConfiguration,
Name: data.Name,
},
},
};
}
break;
case 'user':
if (userData) {
if (operation === 'get') {
const userAttributes = userData.UserAttributes
? mapUserAttributes(userData.UserAttributes)
: {};
return {
json: {
User: {
Enabled: userData.Enabled,
...Object.fromEntries(Object.entries(userAttributes).slice(0, 6)),
UserCreateDate: userData.UserCreateDate,
UserLastModifiedDate: userData.UserLastModifiedDate,
UserStatus: userData.UserStatus,
Username: userData.Username,
},
},
};
} else if (operation === 'getAll') {
if (users && Array.isArray(users)) {
const processedUsers: User[] = [];
users.forEach((user) => {
const userAttributes = user.Attributes ? mapUserAttributes(user.Attributes) : {};
processedUsers.push({
Enabled: userData.Enabled,
...Object.fromEntries(Object.entries(userAttributes).slice(0, 6)),
UserCreateDate: userData.UserCreateDate,
UserLastModifiedDate: userData.UserLastModifiedDate,
UserStatus: userData.UserStatus,
Username: userData.Username,
});
});
return {
json: {
Users: processedUsers,
},
};
}
}
}
break;
}
return undefined;
})
.filter((item) => item !== undefined)
.flat();
return simplifiedItems;
}
export async function listUsersInGroup(
this: IExecuteSingleFunctions,
groupName: string,
userPoolId: string,
): Promise<IDataObject> {
if (!userPoolId) {
throw new ApplicationError('User Pool ID is required');
}
const opts: IHttpRequestOptions = {
url: '',
method: 'POST',
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.ListUsersInGroup',
},
body: JSON.stringify({
UserPoolId: userPoolId,
GroupName: groupName,
MaxResults: 60,
}),
};
const responseData: IDataObject = await awsRequest.call(this, opts);
const users = responseData.Users as IDataObject[] | undefined;
if (!users) {
return { results: [] };
}
const results: INodeListSearchItems[] = users
.map((user) => {
const attributes = user.Attributes as Array<{ Name: string; Value: string }> | undefined;
const email = attributes?.find((attr) => attr.Name === 'email')?.Value;
const sub = attributes?.find((attr) => attr.Name === 'sub')?.Value;
const username = user.Username as string;
const name = email || sub || username;
const value = username;
return { name, value };
})
.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
return { results, paginationToken: responseData.NextToken as string | undefined };
}
export async function getUsersForGroup(
this: IExecuteSingleFunctions,
groupName: string,
userPoolId: string,
): Promise<IDataObject[]> {
const users = await listUsersInGroup.call(this, groupName, userPoolId);
if (users && users.results && Array.isArray(users.results) && users.results.length > 0) {
return users.results as IDataObject[];
}
return [] as IDataObject[];
}
export async function processUsersForGroups(
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
const userPoolIdRaw = this.getNodeParameter('userPoolId') as IDataObject;
const userPoolId = userPoolIdRaw.value as string;
const include = this.getNodeParameter('includeUsers', 0) as boolean;
if (!include) {
return items;
}
const processedGroups: IDataObject[] = [];
if (response.body && typeof response.body === 'object') {
if ('Group' in response.body) {
const group = (response.body as { Group: IDataObject }).Group;
const usersResponse = await getUsersForGroup.call(
this,
group.GroupName as string,
userPoolId,
);
if (usersResponse.length > 0) {
return items.map((item) => ({
json: { ...item.json, Users: usersResponse },
}));
} else {
return items.map((item) => ({
json: { ...item.json },
}));
}
} else {
const groups = (response.body as { Groups: IDataObject[] }).Groups;
for (const group of groups) {
const usersResponse = await getUsersForGroup.call(
this,
group.GroupName as string,
userPoolId,
);
if (usersResponse.length > 0) {
processedGroups.push({
...group,
Users: usersResponse,
});
} else {
processedGroups.push(group);
}
}
}
}
return items.map((item) => ({
json: { ...item.json, Groups: processedGroups },
}));
}
//Check if needed
export async function fetchUserPoolConfig(
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const userPoolIdRaw = this.getNodeParameter('userPoolId') as IDataObject;
const userPoolId = userPoolIdRaw.value as string;
if (!userPoolId) {
throw new ApplicationError('User Pool ID is required');
}
const opts: IHttpRequestOptions = {
url: '',
method: 'POST',
body: JSON.stringify({
UserPoolId: userPoolId,
}),
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.DescribeUserPool',
},
};
const responseData: IDataObject = await awsRequest.call(this, opts);
return requestOptions;
}

View file

@ -3,8 +3,9 @@ import type { INodeProperties } from 'n8n-workflow';
import { import {
handleErrorPostReceive, handleErrorPostReceive,
handlePagination, handlePagination,
presendOptions, presendAdditionalFields,
presendPath, presendVerifyPath,
processUsersForGroups,
} from '../GenericFunctions'; } from '../GenericFunctions';
export const groupOperations: INodeProperties[] = [ export const groupOperations: INodeProperties[] = [
@ -77,7 +78,7 @@ export const groupOperations: INodeProperties[] = [
ignoreHttpStatusErrors: true, ignoreHttpStatusErrors: true,
}, },
output: { output: {
postReceive: [handleErrorPostReceive], postReceive: [processUsersForGroups, handleErrorPostReceive],
}, },
}, },
action: 'Get group', action: 'Get group',
@ -101,7 +102,7 @@ export const groupOperations: INodeProperties[] = [
ignoreHttpStatusErrors: true, ignoreHttpStatusErrors: true,
}, },
output: { output: {
postReceive: [handleErrorPostReceive], postReceive: [processUsersForGroups, handleErrorPostReceive],
}, },
}, },
action: 'Get many groups', action: 'Get many groups',
@ -112,7 +113,7 @@ export const groupOperations: INodeProperties[] = [
description: 'Update an existing group', description: 'Update an existing group',
routing: { routing: {
send: { send: {
preSend: [presendOptions], preSend: [presendAdditionalFields],
}, },
request: { request: {
method: 'POST', method: 'POST',
@ -264,7 +265,7 @@ const createFields: INodeProperties[] = [
send: { send: {
property: 'Path', property: 'Path',
type: 'body', type: 'body',
preSend: [presendPath], preSend: [presendVerifyPath],
}, },
}, },
}, },
@ -492,6 +493,19 @@ const getFields: INodeProperties[] = [
}, },
], ],
}, },
{
displayName: 'Include User List',
name: 'includeUsers',
type: 'boolean',
displayOptions: {
show: {
resource: ['group'],
operation: ['get'],
},
},
default: true,
description: 'Whether to include a list of users in the group',
},
]; ];
const getAllFields: INodeProperties[] = [ const getAllFields: INodeProperties[] = [
@ -553,6 +567,19 @@ const getAllFields: INodeProperties[] = [
displayOptions: { show: { resource: ['group'], operation: ['getAll'], returnAll: [false] } }, displayOptions: { show: { resource: ['group'], operation: ['getAll'], returnAll: [false] } },
routing: { send: { type: 'body', property: 'Limit' } }, routing: { send: { type: 'body', property: 'Limit' } },
}, },
{
displayName: 'Include User List',
name: 'includeUsers',
type: 'boolean',
displayOptions: {
show: {
resource: ['group'],
operation: ['getAll'],
},
},
default: true,
description: 'Whether to include a list of users in the group',
},
]; ];
const updateFields: INodeProperties[] = [ const updateFields: INodeProperties[] = [
@ -657,8 +684,8 @@ const updateFields: INodeProperties[] = [
type: 'resourceLocator', type: 'resourceLocator',
}, },
{ {
displayName: 'Options', displayName: 'Additional Fields',
name: 'options', name: 'additionalFields',
default: {}, default: {},
displayOptions: { displayOptions: {
show: { show: {
@ -708,7 +735,7 @@ const updateFields: INodeProperties[] = [
send: { send: {
property: 'Path', property: 'Path',
type: 'body', type: 'body',
preSend: [presendPath], preSend: [presendVerifyPath],
}, },
}, },
}, },

View file

@ -1,10 +1,13 @@
import type { INodeProperties } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow';
import { import {
fetchUserPoolConfig,
handleErrorPostReceive, handleErrorPostReceive,
handlePagination, handlePagination,
presendFilter, presendFilter,
presendTest,
processAttributes, processAttributes,
simplifyData,
} from '../GenericFunctions'; } from '../GenericFunctions';
export const userOperations: INodeProperties[] = [ export const userOperations: INodeProperties[] = [
@ -52,6 +55,9 @@ export const userOperations: INodeProperties[] = [
description: 'Create a new user', description: 'Create a new user',
action: 'Create user', action: 'Create user',
routing: { routing: {
send: {
preSend: [presendTest, fetchUserPoolConfig],
},
request: { request: {
method: 'POST', method: 'POST',
headers: { headers: {
@ -104,7 +110,7 @@ export const userOperations: INodeProperties[] = [
ignoreHttpStatusErrors: true, ignoreHttpStatusErrors: true,
}, },
output: { output: {
postReceive: [handleErrorPostReceive], postReceive: [simplifyData, handleErrorPostReceive],
}, },
}, },
}, },
@ -130,7 +136,7 @@ export const userOperations: INodeProperties[] = [
ignoreHttpStatusErrors: true, ignoreHttpStatusErrors: true,
}, },
output: { output: {
postReceive: [handleErrorPostReceive], postReceive: [simplifyData, handleErrorPostReceive],
}, },
}, },
action: 'Get many users', action: 'Get many users',
@ -167,6 +173,7 @@ export const userOperations: INodeProperties[] = [
description: 'Update a user', description: 'Update a user',
action: 'Update user', action: 'Update user',
routing: { routing: {
send: { preSend: [presendTest] },
request: { request: {
method: 'POST', method: 'POST',
headers: { headers: {
@ -246,7 +253,7 @@ const createFields: INodeProperties[] = [
displayName: 'User Name', displayName: 'User Name',
name: 'Username', name: 'Username',
default: '', default: '',
description: 'The username of the new user to create', description: 'The username of the new user to create. No whitespace is allowed.',
placeholder: 'e.g. JohnSmith', placeholder: 'e.g. JohnSmith',
displayOptions: { displayOptions: {
show: { show: {
@ -277,61 +284,51 @@ const createFields: INodeProperties[] = [
}, },
}, },
options: [ options: [
//doesn't work
{ {
displayName: 'Client Metadata', displayName: 'User Attributes',
name: 'clientMetadata', name: 'UserAttributes',
type: 'fixedCollection', type: 'fixedCollection',
placeholder: 'Add Metadata', placeholder: 'Add Attribute',
default: { metadata: [] }, default: {
description: 'A map of custom key-value pairs for workflows triggered by this action', attributes: [],
},
description: 'Attributes to add for the user',
typeOptions: { typeOptions: {
multipleValues: true, multipleValues: true,
}, },
options: [ options: [
{ {
displayName: 'Metadata', displayName: 'Attributes',
name: 'metadata', name: 'attributes',
values: [ values: [
{ {
displayName: 'Key', displayName: 'Name',
name: 'key', name: 'Name',
type: 'string', type: 'string',
default: '', default: '',
description: 'The key of the metadata attribute', description: 'The name of the attribute (e.g., custom:deliverables)',
}, },
{ {
displayName: 'Value', displayName: 'Value',
name: 'value', name: 'Value',
type: 'string', type: 'string',
default: '', default: '',
description: 'The value of the metadata attribute', description: 'The value of the attribute',
}, },
], ],
}, },
], ],
routing: { routing: {
send: { send: {
preSend: [processAttributes],
type: 'body', type: 'body',
property: 'ClientMetadata', property: 'UserAttributes',
value: value:
'={{ $value.metadata && $value.metadata.length > 0 ? Object.fromEntries($value.metadata.map(attribute => [attribute.Name, attribute.Value])) : {} }}', '={{ $value.attributes?.map(attribute => ({ Name: attribute.Name, Value: attribute.Value })) || [] }}',
}, },
}, },
}, },
{
displayName: 'Temporary Password',
name: 'TemporaryPassword',
default: '',
description: "The user's temporary password",
routing: {
send: {
property: 'TemporaryPassword',
type: 'body',
},
},
type: 'string',
typeOptions: { password: true },
},
{ {
displayName: 'Message Action', displayName: 'Message Action',
name: 'MessageAction', name: 'MessageAction',
@ -396,88 +393,45 @@ const createFields: INodeProperties[] = [
}, },
}, },
{ {
displayName: 'User Attributes', displayName: 'Temporary Password',
name: 'UserAttributes', name: 'temporaryPasswordOptions',
type: 'fixedCollection', type: 'options',
placeholder: 'Add Attribute', default: 'generatePassword',
default: { description: 'Choose to set a password manually or one will be automatically generated',
attributes: [],
},
description: 'Attributes to add for the user',
typeOptions: {
multipleValues: true,
},
options: [ options: [
{ {
displayName: 'Attributes', name: 'Set a Password',
name: 'attributes', value: 'setPassword',
values: [ },
{ {
displayName: 'Name', name: 'Generate a Password',
name: 'Name', value: 'generatePassword',
type: 'string',
default: '',
description: 'The name of the attribute (e.g., custom:deliverables)',
},
{
displayName: 'Value',
name: 'Value',
type: 'string',
default: '',
description: 'The value of the attribute',
},
],
}, },
], ],
routing: { routing: {
send: { send: {
preSend: [processAttributes], property: 'TemporaryPassword',
type: 'body', type: 'body',
property: 'UserAttributes',
value:
'={{ $value.attributes?.map(attribute => ({ Name: attribute.Name, Value: attribute.Value })) || [] }}',
}, },
}, },
}, },
{ {
displayName: 'Validation Data', displayName: 'Password',
name: 'ValidationData', name: 'password',
type: 'fixedCollection', type: 'string',
placeholder: 'Add Attribute', typeOptions: { password: true },
default: { default: '',
attributes: [], placeholder: 'Enter a temporary password',
}, description: "The user's temporary password",
description: 'Validation data to add for the user', displayOptions: {
typeOptions: { show: {
multipleValues: true, temporaryPasswordOptions: ['setPassword'],
},
options: [
{
displayName: 'Data',
name: 'data',
values: [
{
displayName: 'Key',
name: 'Key',
type: 'string',
default: '',
description: 'The name of the data (e.g., custom:deliverables)',
},
{
displayName: 'Value',
name: 'Value',
type: 'string',
default: '',
description: 'The value of the data',
},
],
}, },
], },
routing: { routing: {
send: { send: {
property: 'TemporaryPassword',
type: 'body', type: 'body',
property: 'ValidationData',
value: '={{ $value.data?.map(data => ({ Name: data.Key, Value: data.Value })) || [] }}',
}, },
}, },
}, },
@ -586,6 +540,19 @@ const getFields: INodeProperties[] = [
required: true, required: true,
type: 'resourceLocator', type: 'resourceLocator',
}, },
{
displayName: 'Simplify',
name: 'simple',
type: 'boolean',
displayOptions: {
show: {
resource: ['user'],
operation: ['get'],
},
},
default: true,
description: 'Whether to return a simplified version of the response instead of the raw data',
},
]; ];
const getAllFields: INodeProperties[] = [ const getAllFields: INodeProperties[] = [
@ -660,48 +627,35 @@ const getAllFields: INodeProperties[] = [
displayOptions: { show: { resource: ['user'], operation: ['getAll'], returnAll: [false] } }, displayOptions: { show: { resource: ['user'], operation: ['getAll'], returnAll: [false] } },
routing: { send: { type: 'body', property: 'Limit' } }, routing: { send: { type: 'body', property: 'Limit' } },
}, },
{
displayName: 'Simplify',
name: 'simple',
type: 'boolean',
displayOptions: {
show: {
resource: ['user'],
operation: ['getAll'],
},
},
default: true,
description: 'Whether to return a simplified version of the response instead of the raw data',
},
{ {
displayName: 'Additional Fields', displayName: 'Additional Fields',
name: 'additionalFields', name: 'additionalFields',
type: 'collection', type: 'collection',
placeholder: 'Add Field', placeholder: 'Add Field',
default: {}, default: {},
displayOptions: { show: { resource: ['user'], operation: ['getAll'] } }, displayOptions: {
show: {
resource: ['user'],
operation: ['getAll'],
},
},
options: [ options: [
{ {
displayName: 'Attributes To Get', displayName: 'Filters',
name: 'attributesToGet', name: 'filters',
type: 'fixedCollection',
typeOptions: { multipleValues: true },
default: {},
placeholder: 'Add Attribute',
description: 'The attributes to return in the response',
options: [
{
name: 'metadataValues',
displayName: 'Metadata',
values: [
{
displayName: 'Attribute',
name: 'attribute',
type: 'string',
default: '',
description: 'The attribute name to return',
},
],
},
],
routing: {
send: {
type: 'body',
property: 'AttributesToGet',
value: '={{ $value.metadataValues.map(attribute => attribute.attribute) }}',
},
},
},
{
displayName: 'Filter Attribute',
name: 'filterAttribute',
type: 'options', type: 'options',
default: 'username', default: 'username',
hint: 'Make sure to select an attribute, type, and provide a value before submitting.', hint: 'Make sure to select an attribute, type, and provide a value before submitting.',
@ -896,7 +850,7 @@ const updateFields: INodeProperties[] = [
], ],
}, },
{ {
displayName: 'User', displayName: 'User Name',
name: 'Username', name: 'Username',
default: { default: {
mode: 'list', mode: 'list',
@ -988,6 +942,7 @@ const updateFields: INodeProperties[] = [
], ],
routing: { routing: {
send: { send: {
preSend: [processAttributes],
type: 'body', type: 'body',
property: 'UserAttributes', property: 'UserAttributes',
value: value:
@ -995,62 +950,6 @@ const updateFields: INodeProperties[] = [
}, },
}, },
}, },
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['user'],
operation: ['update'],
},
},
options: [
{
displayName: 'Client Metadata',
name: 'clientMetadata',
type: 'fixedCollection',
placeholder: 'Add Metadata Pair',
default: { metadata: [] },
description: 'A map of custom key-value pairs for workflows triggered by this action',
typeOptions: {
multipleValues: true,
},
options: [
{
displayName: 'Metadata',
name: 'metadata',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'The key of the metadata attribute',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value of the metadata attribute',
},
],
},
],
routing: {
send: {
type: 'body',
property: 'ClientMetadata',
value:
'={{ $value.metadata && $value.metadata.length > 0 ? Object.fromEntries($value.metadata.map(attribute => [attribute.Name, attribute.Value])) : {} }}',
},
},
},
],
},
]; ];
const addToGroupFields: INodeProperties[] = [ const addToGroupFields: INodeProperties[] = [

View file

@ -1,4 +1,5 @@
import type { INodeProperties } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow';
import { presendTest, simplifyData } from '../GenericFunctions';
export const userPoolOperations: INodeProperties[] = [ export const userPoolOperations: INodeProperties[] = [
{ {
@ -13,12 +14,26 @@ export const userPoolOperations: INodeProperties[] = [
value: 'get', value: 'get',
action: 'Describe the configuration of a user pool', action: 'Describe the configuration of a user pool',
routing: { routing: {
send: {
preSend: [presendTest],
},
request: { request: {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.DescribeUserPool', 'X-Amz-Target': 'AWSCognitoIdentityProviderService.DescribeUserPool',
}, },
}, },
output: {
postReceive: [
simplifyData,
{
type: 'rootProperty',
properties: {
property: 'UserPool',
},
},
],
},
}, },
}, },
], ],
@ -28,7 +43,7 @@ export const userPoolOperations: INodeProperties[] = [
export const userPoolFields: INodeProperties[] = [ export const userPoolFields: INodeProperties[] = [
{ {
displayName: 'User Pool ID', displayName: 'User Pool',
name: 'userPoolId', name: 'userPoolId',
required: true, required: true,
type: 'resourceLocator', type: 'resourceLocator',
@ -69,4 +84,17 @@ export const userPoolFields: INodeProperties[] = [
}, },
], ],
}, },
{
displayName: 'Simplify',
name: 'simple',
type: 'boolean',
displayOptions: {
show: {
resource: ['userPool'],
operation: ['get'],
},
},
default: true,
description: 'Whether to return a simplified version of the response instead of the raw data',
},
]; ];