Modified temporaryPassword and filters for User, and 'options' to additionalFields' for Group

This commit is contained in:
Adina Totorean 2025-01-08 16:09:20 +02:00
parent abdd1521c0
commit 2d5eb7ce24
4 changed files with 104 additions and 189 deletions

View file

@ -14,13 +14,27 @@ 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( type UserPool = {
this: IExecuteSingleFunctions, Arn: string;
requestOptions: IHttpRequestOptions, CreationDate: number;
): Promise<IHttpRequestOptions> { DeletionProtection: string;
console.log('requestOptions', requestOptions); Domain: string;
return requestOptions; 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;
};
/* /*
* Helper function which stringifies the body before sending the request. * Helper function which stringifies the body before sending the request.
@ -36,33 +50,17 @@ export async function presendStringifyBody(
return requestOptions; return requestOptions;
} }
export async function presendFilter( export async function presendFilters(
this: IExecuteSingleFunctions, this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions, requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> { ): Promise<IHttpRequestOptions> {
const additionalFields = this.getNodeParameter('additionalFields', {}) as IDataObject; const filters = this.getNodeParameter('filters') as IDataObject;
const filterAttribute = additionalFields.filters as string;
let filterType = additionalFields.filterType as string;
const filterValue = additionalFields.filterValue as string;
const hasAnyFilter = filterAttribute || filterType || filterValue; if (filters.length === 0) return requestOptions;
if (!hasAnyFilter) { const filterToSend = filters.filter as IDataObject;
return requestOptions; const filterAttribute = filterToSend?.attribute as string;
} const filterValue = filterToSend?.value as string;
if (hasAnyFilter && (!filterAttribute || !filterType || !filterValue)) {
throw new NodeOperationError(
this.getNode(),
'If filtering is used, please provide Filter Attribute, Filter Type, and Filter Value.',
);
}
const filterTypeMapping: { [key: string]: string } = {
exactMatch: '=',
startsWith: '^=',
};
filterType = filterTypeMapping[filterType] || filterType;
let body: IDataObject; let body: IDataObject;
if (typeof requestOptions.body === 'string') { if (typeof requestOptions.body === 'string') {
@ -72,12 +70,17 @@ export async function presendFilter(
throw new NodeOperationError(this.getNode(), 'Failed to parse requestOptions body'); throw new NodeOperationError(this.getNode(), 'Failed to parse requestOptions body');
} }
} else { } else {
body = requestOptions.body as IDataObject; body = (requestOptions.body as IDataObject) || {};
}
let filter = '';
if (filterAttribute && filterValue) {
filter = `"${filterAttribute}"^="${filterValue}"`;
} }
requestOptions.body = JSON.stringify({ requestOptions.body = JSON.stringify({
...body, ...body,
Filter: `${filterAttribute} ${filterType} "${filterValue}"`, Filter: filter,
}); });
return requestOptions; return requestOptions;
@ -121,7 +124,6 @@ export async function presendVerifyPath(
return requestOptions; return requestOptions;
} }
/* Helper function to process attributes in UserAttributes */
export async function processAttributes( export async function processAttributes(
this: IExecuteSingleFunctions, this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions, requestOptions: IHttpRequestOptions,
@ -156,7 +158,6 @@ export async function processAttributes(
return requestOptions; return requestOptions;
} }
/* Helper function to handle pagination */
const possibleRootProperties = ['Users', 'Groups']; const possibleRootProperties = ['Users', 'Groups'];
export async function handlePagination( export async function handlePagination(
this: IExecutePaginationFunctions, this: IExecutePaginationFunctions,
@ -207,7 +208,6 @@ export async function handlePagination(
return aggregatedResult.map((item) => ({ json: item })); return aggregatedResult.map((item) => ({ json: item }));
} }
/* Helper functions to handle errors */
export async function handleErrorPostReceive( export async function handleErrorPostReceive(
this: IExecuteSingleFunctions, this: IExecuteSingleFunctions,
data: INodeExecutionData[], data: INodeExecutionData[],
@ -337,7 +337,6 @@ export async function handleErrorPostReceive(
return data; return data;
} }
/* Helper function used in listSearch methods */
export async function awsRequest( export async function awsRequest(
this: ILoadOptionsFunctions | IPollFunctions | IExecuteSingleFunctions, this: ILoadOptionsFunctions | IPollFunctions | IExecuteSingleFunctions,
opts: IHttpRequestOptions, opts: IHttpRequestOptions,
@ -542,36 +541,7 @@ export async function searchGroups(
return { results, paginationToken: responseData.NextToken }; return { results, paginationToken: responseData.NextToken };
} }
export async function simplifyData( export function mapUserAttributes(userAttributes: Array<{ Name: string; Value: string }>): {
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; [key: string]: string;
} { } {
return userAttributes?.reduce( return userAttributes?.reduce(
@ -585,6 +555,13 @@ export async function simplifyData(
); );
} }
export async function simplifyData(
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
const simple = this.getNodeParameter('simple') as boolean;
if (!simple) { if (!simple) {
return items; return items;
} }
@ -643,12 +620,12 @@ export async function simplifyData(
users.forEach((user) => { users.forEach((user) => {
const userAttributes = user.Attributes ? mapUserAttributes(user.Attributes) : {}; const userAttributes = user.Attributes ? mapUserAttributes(user.Attributes) : {};
processedUsers.push({ processedUsers.push({
Enabled: userData.Enabled, Enabled: user.Enabled,
...Object.fromEntries(Object.entries(userAttributes).slice(0, 6)), ...Object.fromEntries(Object.entries(userAttributes).slice(0, 6)),
UserCreateDate: userData.UserCreateDate, UserCreateDate: user.UserCreateDate,
UserLastModifiedDate: userData.UserLastModifiedDate, UserLastModifiedDate: user.UserLastModifiedDate,
UserStatus: userData.UserStatus, UserStatus: user.UserStatus,
Username: userData.Username, Username: user.Username,
}); });
}); });
return { return {
@ -793,31 +770,3 @@ export async function processUsersForGroups(
json: { ...item.json, Groups: processedGroups }, 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

@ -34,7 +34,15 @@ export const groupOperations: INodeProperties[] = [
ignoreHttpStatusErrors: true, ignoreHttpStatusErrors: true,
}, },
output: { output: {
postReceive: [handleErrorPostReceive], postReceive: [
handleErrorPostReceive,
{
type: 'rootProperty',
properties: {
property: 'Group',
},
},
],
}, },
}, },
action: 'Create group', action: 'Create group',
@ -215,8 +223,8 @@ const createFields: INodeProperties[] = [
validateType: 'string', validateType: 'string',
}, },
{ {
displayName: 'Options', displayName: 'Additional Fields',
name: 'options', name: 'additionalFields',
default: {}, default: {},
displayOptions: { displayOptions: {
show: { show: {

View file

@ -1,11 +1,9 @@
import type { INodeProperties } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow';
import { import {
fetchUserPoolConfig,
handleErrorPostReceive, handleErrorPostReceive,
handlePagination, handlePagination,
presendFilter, presendFilters,
presendTest,
processAttributes, processAttributes,
simplifyData, simplifyData,
} from '../GenericFunctions'; } from '../GenericFunctions';
@ -55,9 +53,6 @@ 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: {
@ -121,7 +116,7 @@ export const userOperations: INodeProperties[] = [
routing: { routing: {
send: { send: {
paginate: true, paginate: true,
preSend: [presendFilter], preSend: [presendFilters],
}, },
operations: { pagination: handlePagination }, operations: { pagination: handlePagination },
request: { request: {
@ -173,7 +168,6 @@ 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: {
@ -395,39 +389,11 @@ const createFields: INodeProperties[] = [
{ {
displayName: 'Temporary Password', displayName: 'Temporary Password',
name: 'temporaryPasswordOptions', name: 'temporaryPasswordOptions',
type: 'options',
default: 'generatePassword',
description: 'Choose to set a password manually or one will be automatically generated',
options: [
{
name: 'Set a Password',
value: 'setPassword',
},
{
name: 'Generate a Password',
value: 'generatePassword',
},
],
routing: {
send: {
property: 'TemporaryPassword',
type: 'body',
},
},
},
{
displayName: 'Password',
name: 'password',
type: 'string', type: 'string',
typeOptions: { password: true }, typeOptions: { password: true },
default: '', default: '',
placeholder: 'Enter a temporary password', description:
description: "The user's temporary password", "The user's temporary password that will be valid only once. If not set, Amazon Cognito will automatically generate one for you.",
displayOptions: {
show: {
temporaryPasswordOptions: ['setPassword'],
},
},
routing: { routing: {
send: { send: {
property: 'TemporaryPassword', property: 'TemporaryPassword',
@ -641,11 +607,11 @@ const getAllFields: INodeProperties[] = [
description: 'Whether to return a simplified version of the response instead of the raw data', description: 'Whether to return a simplified version of the response instead of the raw data',
}, },
{ {
displayName: 'Additional Fields', displayName: 'Filters',
name: 'additionalFields', name: 'filters',
type: 'collection', type: 'fixedCollection',
placeholder: 'Add Field', placeholder: 'Add Filter',
default: {}, default: [],
displayOptions: { displayOptions: {
show: { show: {
resource: ['user'], resource: ['user'],
@ -654,11 +620,14 @@ const getAllFields: INodeProperties[] = [
}, },
options: [ options: [
{ {
displayName: 'Filters', displayName: 'Filter',
name: 'filters', name: 'filter',
values: [
{
displayName: 'Attribute',
name: 'attribute',
type: 'options', type: 'options',
default: 'username', default: 'email',
hint: 'Make sure to select an attribute, type, and provide a value before submitting.',
description: 'The attribute to search for', description: 'The attribute to search for',
options: [ options: [
{ name: 'Cognito User Status', value: 'cognito:user_status' }, { name: 'Cognito User Status', value: 'cognito:user_status' },
@ -674,25 +643,16 @@ const getAllFields: INodeProperties[] = [
], ],
}, },
{ {
displayName: 'Filter Type', displayName: 'Value',
name: 'filterType', name: 'value',
type: 'options',
default: 'exactMatch',
description: 'The matching strategy of the filter',
options: [
{ name: 'Exact Match', value: 'exactMatch' },
{ name: 'Starts With', value: 'startsWith' },
],
},
{
displayName: 'Filter Value',
name: 'filterValue',
type: 'string', type: 'string',
default: '', default: '',
description: 'The value of the attribute to search for', description: 'The value of the attribute to search for',
}, },
], ],
}, },
],
},
]; ];
const deleteFields: INodeProperties[] = [ const deleteFields: INodeProperties[] = [

View file

@ -1,5 +1,6 @@
import type { INodeProperties } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow';
import { presendTest, simplifyData } from '../GenericFunctions';
import { simplifyData } from '../GenericFunctions';
export const userPoolOperations: INodeProperties[] = [ export const userPoolOperations: INodeProperties[] = [
{ {
@ -14,9 +15,6 @@ 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: {