fix(Brevo Node): Accept both spellings of the recipients parameter (#40015)

This commit is contained in:
Toni Devine 2026-10-01 15:54:04 +00:00 committed by GitHub
parent 33505d7faa
commit e144b337de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 218 additions and 8 deletions

View File

@ -14,7 +14,7 @@ export class Brevo implements INodeType {
name: 'sendInBlue',
icon: 'file:brevo.svg',
group: ['transform'],
version: 1,
version: [1, 1.1],
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Brevo API',
defaults: {

View File

@ -133,6 +133,26 @@ const sendHtmlEmailFields: INodeProperties[] = [
show: {
resource: ['email'],
operation: ['send'],
'@version': [1],
},
},
default: '',
required: true,
routing: {
send: {
preSend: [BrevoNode.Validators.validateAndCompileRecipientEmails],
},
},
},
{
displayName: 'Recipients',
name: 'recipients',
type: 'string',
displayOptions: {
show: {
resource: ['email'],
operation: ['send'],
'@version': [{ _cnd: { gte: 1.1 } }],
},
},
default: '',
@ -191,6 +211,11 @@ const sendHtmlEmailFields: INodeProperties[] = [
placeholder: 'Add BCC',
type: 'fixedCollection',
default: {},
displayOptions: {
show: {
'@version': [1],
},
},
options: [
{
name: 'receipientBcc',
@ -211,12 +236,48 @@ const sendHtmlEmailFields: INodeProperties[] = [
},
},
},
{
displayName: 'Recipients BCC',
name: 'recipientsBCC',
placeholder: 'Add BCC',
type: 'fixedCollection',
default: {},
displayOptions: {
show: {
'@version': [{ _cnd: { gte: 1.1 } }],
},
},
options: [
{
name: 'recipientBcc',
displayName: 'Recipient',
values: [
{
displayName: 'Recipient',
name: 'bcc',
type: 'string',
default: '',
},
],
},
],
routing: {
send: {
preSend: [BrevoNode.Validators.validateAndCompileBCCEmails],
},
},
},
{
displayName: 'Recipients CC',
name: 'receipientsCC',
placeholder: 'Add CC',
type: 'fixedCollection',
default: {},
displayOptions: {
show: {
'@version': [1],
},
},
options: [
{
name: 'receipientCc',
@ -237,6 +298,37 @@ const sendHtmlEmailFields: INodeProperties[] = [
},
},
},
{
displayName: 'Recipients CC',
name: 'recipientsCC',
placeholder: 'Add CC',
type: 'fixedCollection',
default: {},
displayOptions: {
show: {
'@version': [{ _cnd: { gte: 1.1 } }],
},
},
options: [
{
name: 'recipientCc',
displayName: 'Recipient',
values: [
{
displayName: 'Recipient',
name: 'cc',
type: 'string',
default: '',
},
],
},
],
routing: {
send: {
preSend: [BrevoNode.Validators.validateAndCompileCCEmails],
},
},
},
{
displayName: 'Email Tags',
name: 'emailTags',
@ -334,6 +426,26 @@ const sendHtmlTemplateEmailFields: INodeProperties[] = [
show: {
resource: ['email'],
operation: ['sendTemplate'],
'@version': [1],
},
},
default: '',
required: true,
routing: {
send: {
preSend: [BrevoNode.Validators.validateAndCompileRecipientEmails],
},
},
},
{
displayName: 'Recipients',
name: 'recipients',
type: 'string',
displayOptions: {
show: {
resource: ['email'],
operation: ['sendTemplate'],
'@version': [{ _cnd: { gte: 1.1 } }],
},
},
default: '',

View File

@ -188,13 +188,20 @@ export namespace BrevoNode {
return result as ValidatedEmail;
}
// v1 stored recipients under a misspelled key. v1.1 (NODE-5367) corrects
// the spelling; this picks which key each version actually reads.
function isLegacyVersion(this: IExecuteSingleFunctions): boolean {
return this.getNode().typeVersion < 1.1;
}
export async function validateAndCompileCCEmails(
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const ccData = this.getNodeParameter(
'additionalFields.receipientsCC.receipientCc',
) as JsonObject;
const path = isLegacyVersion.call(this)
? 'additionalFields.receipientsCC.receipientCc'
: 'additionalFields.recipientsCC.recipientCc';
const ccData = this.getNodeParameter(path) as JsonObject;
const { cc } = ccData;
const { body } = requestOptions;
const data = validateEmailStrings({ cc: cc as string });
@ -207,9 +214,10 @@ export namespace BrevoNode {
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const bccData = this.getNodeParameter(
'additionalFields.receipientsBCC.receipientBcc',
) as JsonObject;
const path = isLegacyVersion.call(this)
? 'additionalFields.receipientsBCC.receipientBcc'
: 'additionalFields.recipientsBCC.recipientBcc';
const bccData = this.getNodeParameter(path) as JsonObject;
const { bcc } = bccData;
const { body } = requestOptions;
const data = validateEmailStrings({ bcc: bcc as string });
@ -222,7 +230,9 @@ export namespace BrevoNode {
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const to = this.getNodeParameter('receipients') as string;
const to = this.getNodeParameter(
isLegacyVersion.call(this) ? 'receipients' : 'recipients',
) as string;
const { body } = requestOptions;
const data = validateEmailStrings({ to });
Object.assign(body!, data);

View File

@ -65,6 +65,7 @@ describe('Brevo - email validation', () => {
it('applies content access restrictions when validating addresses', async () => {
const context = {
getNodeParameter: vi.fn().mockReturnValue('recipient@example.com'),
getNode: vi.fn().mockReturnValue({ typeVersion: 1 }),
} as unknown as IExecuteSingleFunctions;
await BrevoNode.Validators.validateAndCompileRecipientEmails.call(context, {
@ -80,6 +81,93 @@ describe('Brevo - email validation', () => {
});
});
function makeVersionedContext(typeVersion: number, params: Record<string, unknown>) {
return {
getNode: vi.fn().mockReturnValue({ typeVersion }),
getNodeParameter: vi.fn((name: string) => params[name]),
} as unknown as IExecuteSingleFunctions;
}
describe('Brevo - recipients/CC/BCC spelling by node version (NODE-5367)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('validateAndCompileRecipientEmails reads the legacy "receipients" key on v1', async () => {
const context = makeVersionedContext(1, { receipients: 'legacy@example.com' });
await BrevoNode.Validators.validateAndCompileRecipientEmails.call(context, {
url: '',
body: {},
});
expect(mailComposerOptions).toHaveBeenCalledWith(
expect.objectContaining({ to: 'legacy@example.com' }),
);
});
it('validateAndCompileRecipientEmails reads the corrected "recipients" key on v1.1', async () => {
const context = makeVersionedContext(1.1, { recipients: 'correct@example.com' });
await BrevoNode.Validators.validateAndCompileRecipientEmails.call(context, {
url: '',
body: {},
});
expect(mailComposerOptions).toHaveBeenCalledWith(
expect.objectContaining({ to: 'correct@example.com' }),
);
});
it('validateAndCompileCCEmails reads the legacy nested CC path on v1', async () => {
const context = makeVersionedContext(1, {
'additionalFields.receipientsCC.receipientCc': { cc: 'legacy-cc@example.com' },
});
await BrevoNode.Validators.validateAndCompileCCEmails.call(context, { url: '', body: {} });
expect(mailComposerOptions).toHaveBeenCalledWith(
expect.objectContaining({ cc: 'legacy-cc@example.com' }),
);
});
it('validateAndCompileCCEmails reads the corrected nested CC path on v1.1', async () => {
const context = makeVersionedContext(1.1, {
'additionalFields.recipientsCC.recipientCc': { cc: 'correct-cc@example.com' },
});
await BrevoNode.Validators.validateAndCompileCCEmails.call(context, { url: '', body: {} });
expect(mailComposerOptions).toHaveBeenCalledWith(
expect.objectContaining({ cc: 'correct-cc@example.com' }),
);
});
it('validateAndCompileBCCEmails reads the legacy nested BCC path on v1', async () => {
const context = makeVersionedContext(1, {
'additionalFields.receipientsBCC.receipientBcc': { bcc: 'legacy-bcc@example.com' },
});
await BrevoNode.Validators.validateAndCompileBCCEmails.call(context, { url: '', body: {} });
expect(mailComposerOptions).toHaveBeenCalledWith(
expect.objectContaining({ bcc: 'legacy-bcc@example.com' }),
);
});
it('validateAndCompileBCCEmails reads the corrected nested BCC path on v1.1', async () => {
const context = makeVersionedContext(1.1, {
'additionalFields.recipientsBCC.recipientBcc': { bcc: 'correct-bcc@example.com' },
});
await BrevoNode.Validators.validateAndCompileBCCEmails.call(context, { url: '', body: {} });
expect(mailComposerOptions).toHaveBeenCalledWith(
expect.objectContaining({ bcc: 'correct-bcc@example.com' }),
);
});
});
describe('Brevo - validateAndCompileAttachmentsData', () => {
const validate = BrevoNode.Validators.validateAndCompileAttachmentsData;