Refactor files for channel property management + validation - #3
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe Buffer node was refactored into modular descriptions, GraphQL transport, load-option providers, resource actions, scheduling logic, network-specific validators, metadata builders, and shared date/URL helpers. The node now delegates idea and post execution to these modules while preserving per-item error handling. Jest configuration, development dependencies, execution helpers, and broad tests were added for credentials, transport, loading options, batching, errors, scheduling, asset validation, idea creation, and multiple social network post formats. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (7)
nodes/Buffer/Buffer.node.ts (1)
52-62: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueConsider reading
resource/operationper item.They're resolved once at index 0, so expression-driven values (
={{ $json.resource }}) would be applied uniformly across all items. Moving the reads inside the loop costs nothing and keeps per-item semantics consistent with the rest of the action code, which already resolves every parameter atitemIndex.♻️ Proposed refactor
- const resource = this.getNodeParameter('resource', 0) as string; - const operation = this.getNodeParameter('operation', 0) as string; - const apiUrl = await getApiUrl(this); for (let i = 0; i < items.length; i++) { try { + const resource = this.getNodeParameter('resource', i) as string; + const operation = this.getNodeParameter('operation', i) as string; if (resource === 'idea') {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/Buffer.node.ts` around lines 52 - 62, Move the `resource` and `operation` parameter reads into the per-item loop in the node’s execution method, resolving both with the current loop index `i` before the dispatch branches. Keep the existing createIdea/createPost handling, but ensure expression-driven values are evaluated independently for each item instead of being reused from index 0.nodes/Buffer/helpers/urlValidation.ts (1)
4-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winEnforce
requiredLabelat the type level whenrequired: true.The docstring says
requiredLabelis required whenrequiredis true, but the type still allows it to be omitted, so a misconfigured caller gets"undefined is required and cannot be empty"instead of a compile error. This helper will back every network's asset validation in the next layer, so getting the contract right now avoids copy-pasted mistakes later.♻️ Proposed fix
-interface ValidateUrlOptions { - /** Whether an empty value should throw. When false, an empty value is silently accepted (skips validation). */ - required: boolean; - /** Label used in the "is required and cannot be empty" message, e.g. "Image URL". Required when `required` is true. */ - requiredLabel?: string; - /** Label used in the "Invalid <label> URL format/protocol" messages, e.g. "image", "video", "thumbnail". */ - formatLabel: string; -} +type ValidateUrlOptions = + | { required: true; requiredLabel: string; formatLabel: string } + | { required: false; requiredLabel?: string; formatLabel: string };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/helpers/urlValidation.ts` around lines 4 - 11, Make ValidateUrlOptions a discriminated union so required: true requires a non-optional requiredLabel, while required: false permits requiredLabel to remain optional. Preserve formatLabel as mandatory and ensure callers receive a compile-time error when required is true without its label.nodes/Buffer/descriptions/post.description.ts (1)
88-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftCasing-variant channelService arrays duplicated across six description files.
Root cause:
channelService's default expression takes the raw, un-normalized value from the channel API response, so every file that gates a field on it must hand-enumerate every casing/underscore variant (e.g.google_business/...). Normalizing once at the source (e.g. append.toLowerCase()to the default expression) and extracting one shared constants module (e.g.GOOGLE_BUSINESS_SERVICE,FACEBOOK_SERVICE) would collapse dozens of repeated literals and remove the risk of a missed/typo'd variant silently breaking a field's visibility for a whole network.
nodes/Buffer/descriptions/post.description.ts#L88-L99: normalize thechannelServicedefault expression's casing (root cause); also update its own repeated arrays at L202-213, L226-236, L269-284, L296-305, L322-331, L343-352 to use the shared constant.nodes/Buffer/descriptions/google-business.description.ts#L25-L31: replace all 13 repeated 9-variant arrays with one sharedGOOGLE_BUSINESS_SERVICEconstant.nodes/Buffer/descriptions/facebook.description.ts#L25-L34: replace the 3 repeated channelService/channelType arrays with shared constants.nodes/Buffer/descriptions/instagram.description.ts#L25-L37: replace the 4 repeated channelService/channelType arrays with shared constants.nodes/Buffer/descriptions/pinterest.description.ts#L12-L21: replace the 3 repeated channelService arrays with a shared constant.nodes/Buffer/descriptions/youtube.description.ts#L8-L20: replace the 7 repeated channelService arrays with a shared constant.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/descriptions/post.description.ts` around lines 88 - 99, Normalize the channelService default expression in post.description.ts and extract shared service constants, including GOOGLE_BUSINESS_SERVICE and FACEBOOK_SERVICE, for reuse. Replace the repeated channelService/channelType variant arrays in nodes/Buffer/descriptions/post.description.ts (L202-213, L226-236, L269-284, L296-305, L322-331, L343-352), nodes/Buffer/descriptions/google-business.description.ts (L25-31), nodes/Buffer/descriptions/facebook.description.ts (L25-34), nodes/Buffer/descriptions/instagram.description.ts (L25-37), nodes/Buffer/descriptions/pinterest.description.ts (L12-21), and nodes/Buffer/descriptions/youtube.description.ts (L8-20) with the appropriate shared constants; ensure all existing visibility behavior is preserved without casing or underscore variants duplicated locally.nodes/Buffer/actions/post/create.ts (2)
59-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated Google Business alias list.
['google', 'googlebusiness', 'google_business']is repeated at both call sites. Consider exporting a helper (e.g.isGoogleBusinessChannel(channelService)) fromnetworks/google-business.ts, which already owns Google Business logic, to keep the alias list in one place.Also applies to: 95-96
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/actions/post/create.ts` around lines 59 - 61, Centralize the Google Business channel alias list by adding an exported helper such as isGoogleBusinessChannel in networks/google-business.ts. Update both call sites in the create flow, including the secondary location around the alternate referenced lines, to use this helper instead of maintaining inline alias arrays.
205-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated GraphQL response error-handling logic. Both actions repeat the same
response.errors/result?.messagechecks; extracting a shared helper (e.g. intransport/graphql.ts) would centralize this for future network actions.
nodes/Buffer/actions/post/create.ts#L205-L215: replace with a call to a sharedcheckGraphQLResult(ctx, response, 'createPost', itemIndex)style helper.nodes/Buffer/actions/idea/create.ts#L79-L89: replace with the same shared helper, passing'createIdea'.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/actions/post/create.ts` around lines 205 - 215, Extract the duplicated GraphQL error checks into a shared helper in transport/graphql.ts, such as checkGraphQLResult, preserving both response.errors and mutation-level message handling while accepting the node context, response, mutation key, and item index. Replace the checks in nodes/Buffer/actions/post/create.ts lines 205-215 with the helper using createPost, and in nodes/Buffer/actions/idea/create.ts lines 79-89 with the helper using createIdea.nodes/Buffer/actions/post/networks/google-business.ts (2)
20-89: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winOptional link/URL metadata fields bypass the
validateUrlhelper used for asset URLs.create.tsvalidates image/video asset URLs viavalidateUrl, but the analogous "link" fields in network metadata builders skip this check entirely, letting malformed URLs reach the API unvalidated.
nodes/Buffer/actions/post/networks/google-business.ts#L20-L89: validategoogleWhatsNewLink,googleOfferLink, andgoogleEventLinkwithvalidateUrl(non-required) before assigning them.nodes/Buffer/actions/post/networks/facebook.ts#L13-L20: validatefacebookLinkAttachmentwithvalidateUrlbefore assigning tometa.linkAttachment.url.nodes/Buffer/actions/post/networks/instagram.ts#L27-L39: validateinstagramLinkwithvalidateUrlbefore assigning tometa.link.nodes/Buffer/actions/post/networks/pinterest.ts#L36-L53: validatepinterestUrlwithvalidateUrlbefore assigning tometa.url.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/actions/post/networks/google-business.ts` around lines 20 - 89, Validate each optional network link before assignment using the existing non-required validateUrl helper. In nodes/Buffer/actions/post/networks/google-business.ts lines 20-89, validate googleWhatsNewLink, googleOfferLink, and googleEventLink; apply the same change to facebookLinkAttachment in nodes/Buffer/actions/post/networks/facebook.ts lines 13-20, instagramLink in nodes/Buffer/actions/post/networks/instagram.ts lines 27-39, and pinterestUrl in nodes/Buffer/actions/post/networks/pinterest.ts lines 36-53, preserving omission of unset optional values.
20-26: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winGoogle Business link fields (
googleWhatsNewLink,googleOfferLink,googleEventLink) bypass URL validation.Same gap as other network builders — these links skip the
validateUrlhelper used for asset URLs.Also applies to: 27-44, 45-89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@nodes/Buffer/actions/post/networks/google-business.ts` around lines 20 - 26, Update the Google Business post-building branches for googleWhatsNewLink, googleOfferLink, and googleEventLink to pass supplied links through the existing validateUrl helper before assigning them to the corresponding metadata details. Preserve the current optional-field behavior and ensure invalid URLs are handled consistently with other asset URL fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@nodes/Buffer/__tests__/google-business.test.ts`:
- Around line 190-202: Strengthen the test “should combine the date and time
when a specific event time is provided” by overriding googleEventStartDate and
googleEventEndDate with dates whose embedded times differ from 10:00 and 18:00,
then update the expected ISO timestamps to reflect the supplied
googleEventStartTime and googleEventEndTime. Keep the isFullDayEvent assertion
unchanged.
In `@nodes/Buffer/actions/post/create.ts`:
- Around line 146-150: Validate dueAt in the customScheduled branch before
calling toISOString, and convert invalid date values into a NodeOperationError
that includes the current item index. Keep assigning the normalized ISO string
to input.dueAt for valid dates, matching the validation behavior used elsewhere
in the create flow.
In `@nodes/Buffer/actions/post/networks/google-business.ts`:
- Around line 27-44: Add required-value validation in the Google Business offer
and event branches before processing title, startDate, or endDate. Reuse the
existing validation pattern used for event times, and ensure missing or blank
fields are rejected before calling toDateOnlyIso or combineDateAndTime; preserve
the existing metadata construction for valid values.
In `@nodes/Buffer/helpers/dateTime.ts`:
- Around line 5-14: Update toDateOnlyIso and combineDateAndTime to validate
parsed dates before calling toISOString, and throw a descriptive catchable error
for invalid dateStr values. In combineDateAndTime, also validate timeStr with
the existing TIME_FORMAT_REGEX before constructing the timestamp, preserving
normal ISO output for valid inputs.
In `@nodes/Buffer/transport/graphql.ts`:
- Around line 12-27: Update executeGraphQL and the load-options consumers
getOrganizations, getChannels, and getPinterestBoards so GraphQL responses
containing errors are surfaced as failures before response.data is read, rather
than being converted to empty arrays. Preserve normal data handling for
successful responses and use the existing request error-handling conventions.
---
Nitpick comments:
In `@nodes/Buffer/actions/post/create.ts`:
- Around line 59-61: Centralize the Google Business channel alias list by adding
an exported helper such as isGoogleBusinessChannel in
networks/google-business.ts. Update both call sites in the create flow,
including the secondary location around the alternate referenced lines, to use
this helper instead of maintaining inline alias arrays.
- Around line 205-215: Extract the duplicated GraphQL error checks into a shared
helper in transport/graphql.ts, such as checkGraphQLResult, preserving both
response.errors and mutation-level message handling while accepting the node
context, response, mutation key, and item index. Replace the checks in
nodes/Buffer/actions/post/create.ts lines 205-215 with the helper using
createPost, and in nodes/Buffer/actions/idea/create.ts lines 79-89 with the
helper using createIdea.
In `@nodes/Buffer/actions/post/networks/google-business.ts`:
- Around line 20-89: Validate each optional network link before assignment using
the existing non-required validateUrl helper. In
nodes/Buffer/actions/post/networks/google-business.ts lines 20-89, validate
googleWhatsNewLink, googleOfferLink, and googleEventLink; apply the same change
to facebookLinkAttachment in nodes/Buffer/actions/post/networks/facebook.ts
lines 13-20, instagramLink in nodes/Buffer/actions/post/networks/instagram.ts
lines 27-39, and pinterestUrl in nodes/Buffer/actions/post/networks/pinterest.ts
lines 36-53, preserving omission of unset optional values.
- Around line 20-26: Update the Google Business post-building branches for
googleWhatsNewLink, googleOfferLink, and googleEventLink to pass supplied links
through the existing validateUrl helper before assigning them to the
corresponding metadata details. Preserve the current optional-field behavior and
ensure invalid URLs are handled consistently with other asset URL fields.
In `@nodes/Buffer/Buffer.node.ts`:
- Around line 52-62: Move the `resource` and `operation` parameter reads into
the per-item loop in the node’s execution method, resolving both with the
current loop index `i` before the dispatch branches. Keep the existing
createIdea/createPost handling, but ensure expression-driven values are
evaluated independently for each item instead of being reused from index 0.
In `@nodes/Buffer/descriptions/post.description.ts`:
- Around line 88-99: Normalize the channelService default expression in
post.description.ts and extract shared service constants, including
GOOGLE_BUSINESS_SERVICE and FACEBOOK_SERVICE, for reuse. Replace the repeated
channelService/channelType variant arrays in
nodes/Buffer/descriptions/post.description.ts (L202-213, L226-236, L269-284,
L296-305, L322-331, L343-352),
nodes/Buffer/descriptions/google-business.description.ts (L25-31),
nodes/Buffer/descriptions/facebook.description.ts (L25-34),
nodes/Buffer/descriptions/instagram.description.ts (L25-37),
nodes/Buffer/descriptions/pinterest.description.ts (L12-21), and
nodes/Buffer/descriptions/youtube.description.ts (L8-20) with the appropriate
shared constants; ensure all existing visibility behavior is preserved without
casing or underscore variants duplicated locally.
In `@nodes/Buffer/helpers/urlValidation.ts`:
- Around line 4-11: Make ValidateUrlOptions a discriminated union so required:
true requires a non-optional requiredLabel, while required: false permits
requiredLabel to remain optional. Preserve formatLabel as mandatory and ensure
callers receive a compile-time error when required is true without its label.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6b23d38e-3886-46c2-8dc7-35e7acc9ad4d
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (47)
jest.config.jsnodes/Buffer/Buffer.node.tsnodes/Buffer/__tests__/batch.test.tsnodes/Buffer/__tests__/bluesky.test.tsnodes/Buffer/__tests__/continueOnFail.test.tsnodes/Buffer/__tests__/core.test.tsnodes/Buffer/__tests__/credentials.test.tsnodes/Buffer/__tests__/errors.test.tsnodes/Buffer/__tests__/facebook.test.tsnodes/Buffer/__tests__/google-business.test.tsnodes/Buffer/__tests__/graphql.test.tsnodes/Buffer/__tests__/idea.test.tsnodes/Buffer/__tests__/instagram.test.tsnodes/Buffer/__tests__/linkedin.test.tsnodes/Buffer/__tests__/loadOptions.test.tsnodes/Buffer/__tests__/mastodon.test.tsnodes/Buffer/__tests__/pinterest.test.tsnodes/Buffer/__tests__/startpage.test.tsnodes/Buffer/__tests__/test-helpers.tsnodes/Buffer/__tests__/threads.test.tsnodes/Buffer/__tests__/tiktok.test.tsnodes/Buffer/__tests__/twitter.test.tsnodes/Buffer/__tests__/youtube.test.tsnodes/Buffer/actions/idea/create.tsnodes/Buffer/actions/post/create.tsnodes/Buffer/actions/post/networks/facebook.tsnodes/Buffer/actions/post/networks/google-business.tsnodes/Buffer/actions/post/networks/instagram.tsnodes/Buffer/actions/post/networks/mastodon.tsnodes/Buffer/actions/post/networks/pinterest.tsnodes/Buffer/actions/post/networks/startpage.tsnodes/Buffer/actions/post/networks/tiktok.tsnodes/Buffer/actions/post/networks/youtube.tsnodes/Buffer/actions/post/scheduling.tsnodes/Buffer/descriptions/facebook.description.tsnodes/Buffer/descriptions/google-business.description.tsnodes/Buffer/descriptions/idea.description.tsnodes/Buffer/descriptions/index.tsnodes/Buffer/descriptions/instagram.description.tsnodes/Buffer/descriptions/pinterest.description.tsnodes/Buffer/descriptions/post.description.tsnodes/Buffer/descriptions/youtube.description.tsnodes/Buffer/helpers/dateTime.tsnodes/Buffer/helpers/urlValidation.tsnodes/Buffer/methods/loadOptions.tsnodes/Buffer/transport/graphql.tspackage.json
| it('should combine the date and time when a specific event time is provided', async () => { | ||
| const input = await executePostCreate({ | ||
| ...googleEventDefaults, | ||
| googleEventHasTime: true, | ||
| googleEventStartTime: '10:00', | ||
| googleEventEndTime: '18:00', | ||
| }); | ||
| const meta = (input.metadata as IDataObject).google as IDataObject; | ||
| const details = meta.detailsEvent as IDataObject; | ||
| expect(details.startDate).toBe('2026-07-01T10:00:00.000Z'); | ||
| expect(details.endDate).toBe('2026-07-01T18:00:00.000Z'); | ||
| expect(details.isFullDayEvent).toBe(false); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Test can't detect a broken time-combination.
googleEventStartTime/googleEventEndTime ('10:00'/'18:00') coincidentally match the time already embedded in the default googleEventStartDate/googleEventEndDate. A bug that ignores the time overrides entirely (just passing the raw date through) would still satisfy the startDate/endDate assertions here.
Suggested fix: use dates whose default time differs from the override
it('should combine the date and time when a specific event time is provided', async () => {
const input = await executePostCreate({
...googleEventDefaults,
+ googleEventStartDate: '2026-07-01T00:00:00Z',
+ googleEventEndDate: '2026-07-01T00:00:00Z',
googleEventHasTime: true,
googleEventStartTime: '10:00',
googleEventEndTime: '18:00',
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('should combine the date and time when a specific event time is provided', async () => { | |
| const input = await executePostCreate({ | |
| ...googleEventDefaults, | |
| googleEventHasTime: true, | |
| googleEventStartTime: '10:00', | |
| googleEventEndTime: '18:00', | |
| }); | |
| const meta = (input.metadata as IDataObject).google as IDataObject; | |
| const details = meta.detailsEvent as IDataObject; | |
| expect(details.startDate).toBe('2026-07-01T10:00:00.000Z'); | |
| expect(details.endDate).toBe('2026-07-01T18:00:00.000Z'); | |
| expect(details.isFullDayEvent).toBe(false); | |
| }); | |
| it('should combine the date and time when a specific event time is provided', async () => { | |
| const input = await executePostCreate({ | |
| ...googleEventDefaults, | |
| googleEventStartDate: '2026-07-01T00:00:00Z', | |
| googleEventEndDate: '2026-07-01T00:00:00Z', | |
| googleEventHasTime: true, | |
| googleEventStartTime: '10:00', | |
| googleEventEndTime: '18:00', | |
| }); | |
| const meta = (input.metadata as IDataObject).google as IDataObject; | |
| const details = meta.detailsEvent as IDataObject; | |
| expect(details.startDate).toBe('2026-07-01T10:00:00.000Z'); | |
| expect(details.endDate).toBe('2026-07-01T18:00:00.000Z'); | |
| expect(details.isFullDayEvent).toBe(false); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@nodes/Buffer/__tests__/google-business.test.ts` around lines 190 - 202,
Strengthen the test “should combine the date and time when a specific event time
is provided” by overriding googleEventStartDate and googleEventEndDate with
dates whose embedded times differ from 10:00 and 18:00, then update the expected
ISO timestamps to reflect the supplied googleEventStartTime and
googleEventEndTime. Keep the isFullDayEvent assertion unchanged.
| if (shareMode === 'customScheduled') { | ||
| const dueAt = ctx.getNodeParameter('dueAt', i) as string; | ||
| // Ensure ISO 8601 format with timezone | ||
| input.dueAt = new Date(dueAt).toISOString(); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Unvalidated dueAt can throw an unlabeled RangeError.
If dueAt isn't a parseable date string, new Date(dueAt).toISOString() throws RangeError: Invalid time value instead of a NodeOperationError with itemIndex context, unlike every other validation failure in this file.
🐛 Proposed fix
if (shareMode === 'customScheduled') {
const dueAt = ctx.getNodeParameter('dueAt', i) as string;
- // Ensure ISO 8601 format with timezone
- input.dueAt = new Date(dueAt).toISOString();
+ const parsedDueAt = new Date(dueAt);
+ if (isNaN(parsedDueAt.getTime())) {
+ throw new NodeOperationError(
+ ctx.getNode(),
+ `Invalid "Due At" date: "${dueAt}" could not be parsed.`,
+ { itemIndex: i },
+ );
+ }
+ // Ensure ISO 8601 format with timezone
+ input.dueAt = parsedDueAt.toISOString();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (shareMode === 'customScheduled') { | |
| const dueAt = ctx.getNodeParameter('dueAt', i) as string; | |
| // Ensure ISO 8601 format with timezone | |
| input.dueAt = new Date(dueAt).toISOString(); | |
| } | |
| if (shareMode === 'customScheduled') { | |
| const dueAt = ctx.getNodeParameter('dueAt', i) as string; | |
| const parsedDueAt = new Date(dueAt); | |
| if (isNaN(parsedDueAt.getTime())) { | |
| throw new NodeOperationError( | |
| ctx.getNode(), | |
| `Invalid "Due At" date: "${dueAt}" could not be parsed.`, | |
| { itemIndex: i }, | |
| ); | |
| } | |
| // Ensure ISO 8601 format with timezone | |
| input.dueAt = parsedDueAt.toISOString(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@nodes/Buffer/actions/post/create.ts` around lines 146 - 150, Validate dueAt
in the customScheduled branch before calling toISOString, and convert invalid
date values into a NodeOperationError that includes the current item index. Keep
assigning the normalized ISO string to input.dueAt for valid dates, matching the
validation behavior used elsewhere in the create flow.
| } else if (googlePostType === 'offer') { | ||
| const title = ctx.getNodeParameter('googleOfferTitle', itemIndex) as string; | ||
| const startDate = ctx.getNodeParameter('googleOfferStartDate', itemIndex) as string; | ||
| const endDate = ctx.getNodeParameter('googleOfferEndDate', itemIndex) as string; | ||
| const code = ctx.getNodeParameter('googleOfferCode', itemIndex) as string; | ||
| const link = ctx.getNodeParameter('googleOfferLink', itemIndex) as string; | ||
| const terms = ctx.getNodeParameter('googleOfferTerms', itemIndex) as string; | ||
| if (title) googleMeta.title = title; | ||
| const details: IDataObject = { | ||
| title, | ||
| // Google Business offers only support a calendar date, not a time of day | ||
| startDate: toDateOnlyIso(startDate), | ||
| endDate: toDateOnlyIso(endDate), | ||
| }; | ||
| if (code) details.code = code; | ||
| if (link) details.link = link; | ||
| if (terms) details.terms = terms; | ||
| googleMeta.detailsOffer = details; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -B2 -A6 "googleOfferTitle|googleOfferStartDate|googleOfferEndDate|googleEventTitle|googleEventStartDate|googleEventEndDate" nodes/Buffer/descriptions/google-business.description.tsRepository: bufferapp/buffer-n8n
Length of output: 2355
Add presence checks for the Google Business date/title fields. Offer and event title, startDate, and endDate are read without any required-field validation, so blank values can still flow into toDateOnlyIso(...) / combineDateAndTime(...). That should be guarded the same way event times already are.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@nodes/Buffer/actions/post/networks/google-business.ts` around lines 27 - 44,
Add required-value validation in the Google Business offer and event branches
before processing title, startDate, or endDate. Reuse the existing validation
pattern used for event times, and ensure missing or blank fields are rejected
before calling toDateOnlyIso or combineDateAndTime; preserve the existing
metadata construction for valid values.
| export function toDateOnlyIso(dateStr: string): string { | ||
| return `${new Date(dateStr).toISOString().slice(0, 10)}T00:00:00.000Z`; | ||
| } | ||
|
|
||
| // Combines a calendar date with a separately-provided "HH:mm" time into a single ISO timestamp, | ||
| // used for Google Business Events when the user opts in to specifying a start/end time. | ||
| export function combineDateAndTime(dateStr: string, timeStr: string): string { | ||
| const datePart = new Date(dateStr).toISOString().slice(0, 10); | ||
| return new Date(`${datePart}T${timeStr}:00.000Z`).toISOString(); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Unguarded date/time parsing can throw an uncaught RangeError.
Neither helper validates its input before calling .toISOString(). Since these values can originate from expressions (not just the date-picker UI), a malformed dateStr/timeStr produces an Invalid Date and an uncaught RangeError rather than a descriptive, catchable error — TIME_FORMAT_REGEX is defined right here but isn't used to guard combineDateAndTime.
🛡️ Proposed fix
export function combineDateAndTime(dateStr: string, timeStr: string): string {
+ if (!TIME_FORMAT_REGEX.test(timeStr)) {
+ throw new Error(`Invalid time "${timeStr}": expected HH:mm format`);
+ }
const datePart = new Date(dateStr).toISOString().slice(0, 10);
return new Date(`${datePart}T${timeStr}:00.000Z`).toISOString();
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@nodes/Buffer/helpers/dateTime.ts` around lines 5 - 14, Update toDateOnlyIso
and combineDateAndTime to validate parsed dates before calling toISOString, and
throw a descriptive catchable error for invalid dateStr values. In
combineDateAndTime, also validate timeStr with the existing TIME_FORMAT_REGEX
before constructing the timestamp, preserving normal ISO output for valid
inputs.
| export async function executeGraphQL( | ||
| ctx: IExecuteFunctions | ILoadOptionsFunctions, | ||
| apiUrl: string, | ||
| query: string, | ||
| variables?: Record<string, unknown>, | ||
| ) { | ||
| return ctx.helpers.httpRequestWithAuthentication.call(ctx, 'bufferApi', { | ||
| method: 'POST' as IHttpRequestMethods, | ||
| url: apiUrl, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: variables !== undefined ? { query, variables } : { query }, | ||
| json: true, | ||
| }); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the shared GraphQL transport
ast-grep outline nodes/Buffer/transport/graphql.ts --view expanded
# Inspect its callers
rg -n "executeGraphQL\\(" nodes/Buffer -S
rg -n "httpRequestWithAuthentication\\.call\\(ctx, 'bufferApi'" nodes/Buffer -S
# Read the transport and the relevant callers with line numbers
cat -n nodes/Buffer/transport/graphql.ts
printf '\n---\n'
sed -n '1,220p' nodes/Buffer/loadOptions.ts | cat -n
printf '\n---\n'
sed -n '1,260p' nodes/Buffer/Buffer.node.ts | cat -nRepository: bufferapp/buffer-n8n
Length of output: 2699
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the actual Buffer node files
git ls-files nodes/Buffer | sort
printf '\n--- loadOptions ---\n'
cat -n nodes/Buffer/methods/loadOptions.ts
printf '\n--- idea create ---\n'
sed -n '1,180p' nodes/Buffer/actions/idea/create.ts | cat -n
printf '\n--- post create ---\n'
sed -n '160,260p' nodes/Buffer/actions/post/create.ts | cat -n
printf '\n--- graphql tests ---\n'
cat -n nodes/Buffer/__tests__/graphql.test.ts
printf '\n--- search for timeout usage in Buffer node ---\n'
rg -n "timeout\s*:" nodes/Buffer -S
printf '\n--- search for GraphQL errors handling ---\n'
rg -n "errors\\b|response\\.errors|response\\.data\\?" nodes/Buffer -SRepository: bufferapp/buffer-n8n
Length of output: 12776
🌐 Web query:
n8n httpRequestWithAuthentication timeout request options IHttpRequestOptions documentation
💡 Result:
In n8n, when using httpRequestWithAuthentication in custom node development, the timeout property is a member of the IHttpRequestOptions interface [1][2][3]. The timeout property accepts a value in milliseconds [1][2]. It defines how long the n8n execution engine should wait for the server to send response headers and start sending the response body before aborting the request [4][5][6]. According to the IHttpRequestOptions interface definition, the timeout property is typed as a number [3]: { //... other properties timeout?: number; //... } When calling httpRequestWithAuthentication, you pass an object conforming to IHttpRequestOptions as the third argument [1][2][7]: const response = await this.helpers.httpRequestWithAuthentication.call( this, 'credentialTypeName', { url: 'https://api.example.com', method: 'GET', timeout: 10000, // Timeout set to 10 seconds (10,000 milliseconds) },); If the timeout is not explicitly provided in the request options, the system may apply a default timeout (which, in some internal n8n implementations, defaults to 300,000 milliseconds or 5 minutes) [8]. The httpRequestWithAuthentication helper passes these options to the underlying request execution logic, which ensures the timeout is applied to the request [9].
Citations:
- 1: https://docs.n8n.io/integrations/creating-nodes/build/reference/http-helpers/
- 2: https://github.com/n8n-io/n8n-docs/blob/main/docs/connect/create-nodes/build-your-node/reference/http-request-helpers.md
- 3: https://github.com/n8n-io/n8n/blob/master/packages/workflow/src/interfaces.ts
- 4: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest#predefined-credentials
- 5: https://github.com/n8n-io/n8n-docs/blob/main/docs/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/README.md
- 6: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest.md
- 7: https://github.com/n8n-io/n8n/blob/d2dd1796a871ee41681acc44ad01dfb0bbd5eee1/packages/workflow/src/Interfaces.ts
- 8: https://github.com/n8n-io/n8n/blob/3f7258b1/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts
- 9: https://github.com/n8n-io/n8n/blob/6d73d8d9/packages/core/src/execution-engine/node-execution-context/utils/request-helper-functions.ts
Surface GraphQL errors in load-options queries. getOrganizations, getChannels, and getPinterestBoards fall back to [] when the response has GraphQL errors, so API/auth/query failures show up as empty dropdowns instead of an error. Handle response.errors before reading response.data.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@nodes/Buffer/transport/graphql.ts` around lines 12 - 27, Update
executeGraphQL and the load-options consumers getOrganizations, getChannels, and
getPinterestBoards so GraphQL responses containing errors are surfaced as
failures before response.data is read, rather than being converted to empty
arrays. Preserve normal data handling for successful responses and use the
existing request error-handling conventions.
Refactors the files used for handling network validation, splitting things out so that they are more maintainable. We've also fixed support for various properties for several networks.
Fixes issues #1 and #2