Worked on error handling and pagination

This commit is contained in:
Adina Totorean 2024-11-28 10:03:01 +02:00
parent c57f838d51
commit f09091a0cb
3 changed files with 270 additions and 173 deletions

View file

@ -116,7 +116,7 @@ export async function processAttributes(
return requestOptions;
}
/* Helper function to process Group Response */
/* Helper function to process Group/User Response */
export async function processGroupsResponse(
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
@ -135,8 +135,26 @@ export async function processGroupsResponse(
return executionData;
}
export async function processUsersResponse(
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
const responseBody = response.body as { Users: IDataObject[] };
if (!responseBody || !Array.isArray(responseBody.Users)) {
throw new ApplicationError('Unexpected response format: No users found.');
}
const executionData: INodeExecutionData[] = responseBody.Users.map((user) => ({
json: user,
}));
return executionData;
}
/* Helper function to handle pagination */
const possibleRootProperties = ['Users']; // Root properties that can be returned by the list operations of the API
const possibleRootProperties = ['Attributes'];
// ToDo: Test if pagination works
export async function handlePagination(
this: IExecutePaginationFunctions,
@ -146,6 +164,8 @@ export async function handlePagination(
let nextPageToken: string | undefined;
const returnAll = this.getNodeParameter('returnAll') as boolean;
let limit = 60;
// Update limit if 'returnAll' is not selected
if (!returnAll) {
limit = this.getNodeParameter('limit') as number;
resultOptions.maxResults = limit;
@ -154,147 +174,229 @@ export async function handlePagination(
do {
if (nextPageToken) {
// For different responses the pagination token might differ. ToDo: Ensure this code works for all endpoints.
// Append PaginationToken to the request body
const body =
typeof resultOptions.options.body === 'object' && resultOptions.options.body !== null
? resultOptions.options.body
: {};
resultOptions.options.body = {
...(resultOptions.options.body as IDataObject),
...body,
PaginationToken: nextPageToken,
} as IDataObject;
console.log('Updated request body with PaginationToken:', resultOptions.options.body);
}
// Make the request
console.log('Sending request with options:', resultOptions);
const responseData = await this.makeRoutingRequest(resultOptions);
// Process response data
for (const page of responseData) {
console.log('Processing page:', page.json);
// Iterate over possible root properties (e.g., "Users")
for (const prop of possibleRootProperties) {
if (page.json[prop]) {
const currentData = page.json[prop] as IDataObject[];
console.log(`Extracted data from property "${prop}":`, currentData);
aggregatedResult.push(...currentData);
}
}
// Check if the limit has been reached
if (!returnAll && aggregatedResult.length >= limit) {
console.log('Limit reached. Returning results.');
return aggregatedResult.slice(0, limit).map((item) => ({ json: item }));
}
// For different responses the pagination token might differ. ToDo: Ensure this code works for all endpoints.
// Update the nextPageToken for the next request
nextPageToken = page.json.PaginationToken as string | undefined;
console.log('Next Page Token:', nextPageToken);
}
} while (nextPageToken);
console.log('Final Aggregated Results:', aggregatedResult);
return aggregatedResult.map((item) => ({ json: item }));
}
/* Helper functions to handle errors */
// ToDo: Handle errors when something is not found. Answer the questions "what happened?" and "how to fix it?".
export async function handleErrorPostReceive(
this: IExecuteSingleFunctions,
data: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
if (String(response.statusCode).startsWith('4') || String(response.statusCode).startsWith('5')) {
const { resource, operation } = this.getNode().parameters as {
resource: string;
operation: string;
};
const {
code: errorCode,
message: errorMessage,
details: errorDetails,
} = (response.body as IDataObject)?.error as {
code: string;
message: string;
innerError?: {
code: string;
'request-id'?: string;
date?: string;
};
details?: Array<{
code: string;
message: string;
}>;
};
const resource = this.getNodeParameter('resource') as string;
const operation = this.getNodeParameter('operation') as string;
const responseBody = response.body as IDataObject;
const errorType = responseBody.__type ?? response.headers?.['x-amzn-errortype'];
const errorMessage = responseBody.message ?? response.headers?.['x-amzn-errormessage'];
if (errorCode === 'Request_BadRequest' && errorMessage === 'Invalid object identifier') {
// Resource/Operation specific errors
if (resource === 'group') {
if (operation === 'delete') {
if (errorType === 'ResourceNotFoundException' || errorType === 'NoSuchEntity') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required group doesn't match any existing one",
description: "Double-check the value in the parameter 'Group' and try again",
});
}
} else if (operation === 'get') {
if (errorType === 'ResourceNotFoundException' || errorType === 'NoSuchEntity') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required group doesn't match any existing one",
description: "Double-check the value in the parameter 'Group' and try again",
});
}
} else if (operation === 'update') {
if (errorType === 'ResourceNotFoundException' || errorType === 'NoSuchEntity') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required group doesn't match any existing one",
description: "Double-check the value in the parameter 'Group' and try again",
});
}
} else if (operation === 'create') {
if (errorType === 'EntityAlreadyExists' || errorType === 'GroupExistsException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: 'The group is already created',
description: "Double-check the value in the parameter 'Group Name' and try again",
});
}
}
} else if (resource === 'user') {
if (operation === 'create') {
if (
errorType === 'UsernameExistsException' &&
errorMessage === 'User account already exists'
) {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: 'The user is already created',
description: "Double-check the value in the parameter 'User Name' and try again",
});
}
} else if (operation === 'addToGroup') {
// Group or user doesn't exist
if (errorType === 'UserNotFoundException') {
const user = this.getNodeParameter('user.value', '') as string;
if (typeof errorMessage === 'string' && errorMessage.includes(user)) {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required user doesn't match any existing one",
description: "Double-check the value in the parameter 'User' and try again.",
});
}
} else if (errorType === 'ResourceNotFoundException') {
const group = this.getNodeParameter('group.value', '') as string;
if (typeof errorMessage === 'string' && errorMessage.includes(group)) {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required group doesn't match any existing one",
description: "Double-check the value in the parameter 'Group' and try again.",
});
}
}
} else if (operation === 'delete') {
if (errorType === 'UserNotFoundException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required user doesn't match any existing one",
description: "Double-check the value in the parameter 'User' and try again",
});
}
} else if (operation === 'get') {
if (errorType === 'UserNotFoundException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required user doesn't match any existing one",
description: "Double-check the value in the parameter 'User' and try again",
});
}
} else if (operation === 'removeFromGroup') {
// Group or user doesn't exist
if (errorType === 'UserNotFoundException') {
const user = this.getNodeParameter('user.value', '') as string;
if (typeof errorMessage === 'string' && errorMessage.includes(user)) {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required user doesn't match any existing one",
description: "Double-check the value in the parameter 'User' and try again.",
});
}
} else if (errorType === 'ResourceNotFoundException') {
const group = this.getNodeParameter('group.value', '') as string;
if (typeof errorMessage === 'string' && errorMessage.includes(group)) {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required group doesn't match any existing one",
description: "Double-check the value in the parameter 'Group' and try again.",
});
}
}
} else if (operation === 'update') {
if (errorType === 'UserNotFoundException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: "The required user doesn't match any existing one",
description: "Double-check the value in the parameter 'User' and try again",
});
}
}
}
// Generic Error Handling
if (errorType === 'InvalidParameterException') {
const group = this.getNodeParameter('group.value', '') as string;
const parameterResource =
resource === 'group' || errorMessage.includes(group) ? 'group' : 'user';
resource === 'group' || (typeof errorMessage === 'string' && errorMessage.includes(group))
? 'group'
: 'user';
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: `The ${parameterResource} ID is invalid`,
description: 'The ID should be in the format e.g. 02bd9fd6-8f93-4758-87c3-1fb73740a315',
});
}
if (errorCode === 'Request_ResourceNotFound') {
const group = this.getNodeParameter('group.value', '') as string;
const parameterResource =
resource === 'group' || errorMessage.includes(group) ? 'group' : 'user';
let parameterDisplayName = undefined;
if (parameterResource === 'group' && operation === 'delete') {
parameterDisplayName = 'Group to Delete';
} else if (parameterResource === 'group' && operation === 'get') {
parameterDisplayName = 'Group to Get';
} else if (parameterResource === 'group' && operation === 'update') {
parameterDisplayName = 'Group to Update';
} else if (parameterResource === 'user' && operation === 'delete') {
parameterDisplayName = 'User to Delete';
} else if (parameterResource === 'user' && operation === 'get') {
parameterDisplayName = 'User to Get';
} else if (parameterResource === 'user' && operation === 'update') {
parameterDisplayName = 'User to Update';
} else if (parameterResource === 'group' && operation === 'addGroup') {
parameterDisplayName = 'Group';
} else if (parameterResource === 'user' && operation === 'addGroup') {
parameterDisplayName = 'User to Add';
} else if (parameterResource === 'group' && operation === 'removeGroup') {
parameterDisplayName = 'Group';
} else if (parameterResource === 'user' && operation === 'removeGroup') {
parameterDisplayName = 'User to Remove';
}
if (parameterDisplayName) {
if (errorType === 'InternalErrorException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: `The required ${parameterResource} doesn't match any existing one`,
description: `Double-check the value in the parameter '${parameterDisplayName}' and try again`,
});
}
}
if (errorDetails?.some((x) => x.code === 'ObjectConflict' || x.code === 'ConflictingObjects')) {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: `The ${resource} already exists`,
description: errorMessage,
message: 'Internal Server Error',
description: 'Amazon Cognito encountered an internal error. Try again later.',
});
}
if (errorType === 'TooManyRequestsException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: 'Too Many Requests',
description: 'You have exceeded the allowed number of requests. Try again later.',
});
}
if (errorType === 'NotAuthorizedException') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: 'Unauthorized Access',
description:
'You are not authorized to perform this operation. Check your permissions and try again.',
});
}
if (errorType === 'ServiceFailure') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: 'Service Failure',
description:
'The request processing has failed because of an unknown error, exception, or failure. Try again later.',
});
}
if (errorType === 'LimitExceeded') {
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
message: 'Limit Exceeded',
description:
'The request was rejected because it attempted to create resources beyond the current AWS account limits. Check your AWS limits and try again.',
});
}
throw new NodeApiError(this.getNode(), response as unknown as JsonObject);
}
return data;
}
export async function handleErrorsDeleteUser(
this: IExecuteSingleFunctions,
data: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
if (response.statusCode < 200 || response.statusCode >= 300) {
const post = this.getNodeParameter('user', undefined) as IDataObject;
// Provide a user-friendly error message
if (post && response.statusCode === 404) {
throw new NodeOperationError(
this.getNode(),
'The user you are deleting could not be found. Adjust the "user" parameter setting to delete the post correctly.',
);
}
throw new NodeApiError(this.getNode(), response.body as JsonObject, {
message: response.statusMessage,
httpCode: response.statusCode.toString(),
});
}
return data;
}
/* Helper function used in listSearch methods */
export async function awsRequest(
this: ILoadOptionsFunctions | IPollFunctions,

View file

@ -26,12 +26,7 @@ export const groupOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.CreateGroup',
},
// preSend: [
// async function (this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions) {
// // Call the function you created in GenericFunctions
// return await getUserIdForGroup.call(this, requestOptions);
// },
// ],
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
@ -49,6 +44,7 @@ export const groupOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.DeleteGroup',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [
@ -74,6 +70,7 @@ export const groupOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.GetGroup',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
@ -98,6 +95,7 @@ export const groupOperations: INodeProperties[] = [
pageSize:
'={{ $parameter["limit"] ? ($parameter["limit"] < 60 ? $parameter["limit"] : 60) : 60 }}', // The API allows maximum 60 results per page
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive, processGroupsResponse],
@ -115,6 +113,7 @@ export const groupOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.UpdateGroup',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [
@ -186,7 +185,7 @@ const createFields: INodeProperties[] = [
],
},
{
displayName: 'Name',
displayName: 'Group Name',
name: 'GroupName',
default: '',
placeholder: 'e.g. My New Group',

View file

@ -2,9 +2,10 @@ import type { INodeProperties } from 'n8n-workflow';
import {
handleErrorPostReceive,
handlePagination,
presendFilter,
presendTest,
processAttributes,
processUsersResponse,
} from '../GenericFunctions';
export const userOperations: INodeProperties[] = [
@ -31,6 +32,7 @@ export const userOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminAddUserToGroup',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
@ -48,6 +50,7 @@ export const userOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminCreateUser',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
@ -65,9 +68,18 @@ export const userOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminDeleteUser',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
postReceive: [
handleErrorPostReceive,
{
type: 'set',
properties: {
value: '={{ { "deleted": true } }}',
},
},
],
},
},
},
@ -82,6 +94,7 @@ export const userOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminGetUser',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
@ -91,13 +104,11 @@ export const userOperations: INodeProperties[] = [
{
name: 'Get Many',
value: 'getAll',
action: 'Get many users',
description: 'Retrieve a list of users',
routing: {
send: {
paginate: true,
},
// ToDo: Test with pagination (ideally we need 4+ users in the user pool)
// operations: { pagination: handlePagination }, // Responsible for pagination and number of results returned
request: {
method: 'POST',
headers: {
@ -107,11 +118,13 @@ export const userOperations: INodeProperties[] = [
pageSize:
'={{ $parameter["limit"] ? ($parameter["limit"] < 60 ? $parameter["limit"] : 60) : 60 }}', // The API allows maximum 60 results per page
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
postReceive: [handleErrorPostReceive, processUsersResponse],
},
},
action: 'Get many users',
},
{
name: 'Remove From Group',
@ -124,6 +137,7 @@ export const userOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminRemoveUserFromGroup',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
@ -141,9 +155,18 @@ export const userOperations: INodeProperties[] = [
headers: {
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminUpdateUserAttributes',
},
ignoreHttpStatusErrors: true,
},
output: {
postReceive: [handleErrorPostReceive],
postReceive: [
handleErrorPostReceive,
{
type: 'set',
properties: {
value: '={{ { "updated": true } }}',
},
},
],
},
},
},
@ -176,7 +199,7 @@ const createFields: INodeProperties[] = [
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -194,8 +217,8 @@ const createFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -219,7 +242,6 @@ const createFields: INodeProperties[] = [
send: {
property: 'Username',
type: 'body',
preSend: [presendTest],
},
},
type: 'string',
@ -288,7 +310,6 @@ const createFields: INodeProperties[] = [
send: {
property: 'TemporaryPassword',
type: 'body',
preSend: [presendTest],
},
},
type: 'string',
@ -315,7 +336,6 @@ const createFields: INodeProperties[] = [
send: {
property: 'MessageAction',
type: 'body',
preSend: [presendTest],
},
},
},
@ -355,7 +375,6 @@ const createFields: INodeProperties[] = [
send: {
property: 'DesiredDeliveryMediums',
type: 'body',
preSend: [presendTest],
},
},
},
@ -470,12 +489,11 @@ const getFields: INodeProperties[] = [
send: {
type: 'body',
property: 'UserPoolId',
preSend: [presendTest],
},
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -492,8 +510,8 @@ const getFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -502,7 +520,7 @@ const getFields: INodeProperties[] = [
],
},
{
displayName: 'User Name',
displayName: 'User',
name: 'Username',
default: {
mode: 'list',
@ -535,8 +553,8 @@ const getFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[\\w-]+-[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx-xxxxxxxxxxx"',
},
},
],
@ -546,7 +564,6 @@ const getFields: INodeProperties[] = [
send: {
type: 'body',
property: 'Username',
preSend: [presendTest],
},
},
required: true,
@ -564,7 +581,7 @@ const getAllFields: INodeProperties[] = [
mode: 'list',
value: '',
},
description: 'The user pool ID that the users are in', // ToDo: Improve description
description: 'The user pool ID where the users are managed',
displayOptions: {
show: {
resource: ['user'],
@ -579,7 +596,7 @@ const getAllFields: INodeProperties[] = [
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -597,8 +614,8 @@ const getAllFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -621,21 +638,7 @@ const getAllFields: INodeProperties[] = [
paginate: '={{ $value }}',
},
operations: {
pagination: {
type: 'generic',
properties: {
continue: '={{ !!$response.body?.["@odata.nextLink"] }}',
request: {
url: '={{ $response.body?.["@odata.nextLink"] ?? $request.url }}',
qs: {
$filter:
'={{ !!$response.body?.["@odata.nextLink"] ? undefined : $request.qs?.$filter }}',
$select:
'={{ !!$response.body?.["@odata.nextLink"] ? undefined : $request.qs?.$select }}',
},
},
},
},
pagination: handlePagination,
},
},
type: 'boolean',
@ -654,8 +657,8 @@ const getAllFields: INodeProperties[] = [
},
routing: {
send: {
property: '$top',
type: 'query',
property: 'limit',
type: 'body',
value: '={{ $value }}',
},
},
@ -775,12 +778,11 @@ const deleteFields: INodeProperties[] = [
send: {
type: 'body',
property: 'UserPoolId',
preSend: [presendTest],
},
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -797,8 +799,8 @@ const deleteFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -807,7 +809,7 @@ const deleteFields: INodeProperties[] = [
],
},
{
displayName: 'User Name',
displayName: 'User',
name: 'Username',
default: {
mode: 'list',
@ -840,8 +842,8 @@ const deleteFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[\\w-]+-[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx-xxxxxxxxxxx"',
},
},
],
@ -851,7 +853,6 @@ const deleteFields: INodeProperties[] = [
send: {
type: 'body',
property: 'Username',
preSend: [presendTest],
},
},
required: true,
@ -880,12 +881,11 @@ const updateFields: INodeProperties[] = [
send: {
type: 'body',
property: 'UserPoolId',
preSend: [presendTest],
},
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -902,8 +902,8 @@ const updateFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -912,7 +912,7 @@ const updateFields: INodeProperties[] = [
],
},
{
displayName: 'User Name',
displayName: 'User',
name: 'Username',
default: {
mode: 'list',
@ -945,8 +945,8 @@ const updateFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[\\w-]+-[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx-xxxxxxxxxxx"',
},
},
],
@ -956,7 +956,6 @@ const updateFields: INodeProperties[] = [
send: {
type: 'body',
property: 'Username',
preSend: [presendTest],
},
},
required: true,
@ -1095,7 +1094,7 @@ const addToGroupFields: INodeProperties[] = [
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -1112,8 +1111,8 @@ const addToGroupFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -1122,7 +1121,7 @@ const addToGroupFields: INodeProperties[] = [
],
},
{
displayName: 'User Name',
displayName: 'User',
name: 'Username',
default: {
mode: 'list',
@ -1155,8 +1154,8 @@ const addToGroupFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[\\w-]+-[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx-xxxxxxxxxxx"',
},
},
],
@ -1172,7 +1171,7 @@ const addToGroupFields: INodeProperties[] = [
type: 'resourceLocator',
},
{
displayName: 'Group Name',
displayName: 'Group',
name: 'GroupName',
default: {
mode: 'list',
@ -1215,7 +1214,6 @@ const addToGroupFields: INodeProperties[] = [
required: true,
routing: {
send: {
preSend: [presendTest], // ToDo: Remove this line before completing the pull request
type: 'body',
property: 'GroupName',
},
@ -1249,7 +1247,7 @@ const removeFromGroupFields: INodeProperties[] = [
},
modes: [
{
displayName: 'From list', // ToDo: Fix error when selecting this option
displayName: 'From list',
name: 'list',
type: 'list',
typeOptions: {
@ -1266,8 +1264,8 @@ const removeFromGroupFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[a-zA-Z0-9-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xx-xx-xx_xxxxxx"',
},
},
],
@ -1276,7 +1274,7 @@ const removeFromGroupFields: INodeProperties[] = [
],
},
{
displayName: 'User Name',
displayName: 'User',
name: 'Username',
default: {
mode: 'list',
@ -1309,8 +1307,8 @@ const removeFromGroupFields: INodeProperties[] = [
{
type: 'regex',
properties: {
regex: '^[\\w-]+_[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx_xxxxxxxxxxx"',
regex: '^[\\w-]+-[0-9a-zA-Z]+$',
errorMessage: 'The ID must follow the pattern "xxxxxx-xxxxxxxxxxx"',
},
},
],
@ -1318,7 +1316,6 @@ const removeFromGroupFields: INodeProperties[] = [
],
routing: {
send: {
preSend: [presendTest], // ToDo: Remove this line before completing the pull request
type: 'body',
property: 'Username',
},
@ -1327,7 +1324,7 @@ const removeFromGroupFields: INodeProperties[] = [
type: 'resourceLocator',
},
{
displayName: 'Group Name',
displayName: 'Group',
name: 'GroupName',
default: {
mode: 'list',
@ -1370,7 +1367,6 @@ const removeFromGroupFields: INodeProperties[] = [
required: true,
routing: {
send: {
preSend: [presendTest], // ToDo: Remove this line before completing the pull request
type: 'body',
property: 'GroupName',
},