-
Notifications
You must be signed in to change notification settings - Fork 53.4k
feat(Lemlist Node): Add new resources and operations for full API coverage #24066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…erage Add 8 new resources: Company, Contact, Database, Inbox, Schedule, Sequence, Task, Webhook Update existing resources with new operations: - Campaign: create, get, update, pause, getReports, exportLeads - Lead: getAll, update, pause, resume, markInterested, markNotInterested - Team: getSenders - Unsubscribe: get, export - Enrichment: bulkEnrich Add workflow tests with mock API responses
…ter structure - Update searchPeople and searchCompanies to use proper filter array format - Each filter now has filterId, in (include values), and out (exclude values) - Add page, size, and excludes options as per API documentation - Use fixedCollection for better UX when adding multiple filters
…red fields
- Add seed parameter for consistent pagination results
- Move page, size, excludes to top-level fields (not in options)
- Always include in/out arrays in filters (empty array if not set)
- Use consistent filterValues name for fixedCollection
- Body now matches expected format:
{filters: [{filterId, in: [], out: []}], seed, page, size, excludes: []}
- Add GET /database/filters endpoint to retrieve available filters - Each filter returns: filterId, description, mode, type, helper, values - Update filter descriptions to reference the new getFilters operation - Clarify that filters with 'leads' mode work for people queries - Clarify that filters with 'companies' mode work for company queries
|
Hey @nicolasmondain, Thank you for your contribution. We appreciate the time and effort you’ve taken to submit this pull request. Before we can proceed, please ensure the following: Regarding new nodes: If your node integrates with an AI service that you own or represent, please email [email protected] and we will be happy to discuss the best approach. About review timelines: Thank you again for contributing to n8n. |
- Sort step types alphabetically in SequenceDescription - Sort webhook events alphabetically in WebhookDescription
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
7 issues found across 18 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/nodes-base/nodes/Lemlist/v2/descriptions/LeadDescription.ts">
<violation number="1" location="packages/nodes-base/nodes/Lemlist/v2/descriptions/LeadDescription.ts:334">
P2: Missing `required: true` for campaignId when scope is 'campaign'. Other campaignId fields in this file (create, delete, update, unsubscribe) are marked as required. Without this, users could select 'Campaign' scope but fail to provide a campaign ID, leading to API errors.</violation>
<violation number="2" location="packages/nodes-base/nodes/Lemlist/v2/descriptions/LeadDescription.ts:598">
P2: Missing `required: true` for campaignId when scope is 'campaign'. Consistent with other campaignId fields in this file, this should be required to prevent API errors when users select 'Campaign' scope.</violation>
</file>
<file name="packages/nodes-base/nodes/Lemlist/test/Lemlist.node.test.ts">
<violation number="1" location="packages/nodes-base/nodes/Lemlist/test/Lemlist.node.test.ts:93">
P2: Empty test body - this test will always pass without testing anything. The nock mock set up in `beforeEach` will never be called. Either implement the test using `testHarness.setupTests()` pattern (like in the Slack node tests) or remove these placeholder tests until they're implemented.</violation>
</file>
<file name="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts">
<violation number="1" location="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts:238">
P2: Rule violated: **Prefer Typeguards over Type casting**
Avoid inline type casting before method calls. Use a type guard or validate the type before calling `.split()`. If `additionalFields.labels` is not a string, this will cause a runtime error.</violation>
<violation number="2" location="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts:324">
P2: Rule violated: **Prefer Typeguards over Type casting**
Avoid inline type casting before method calls. Use a type guard or validate the type before calling `.split()` to prevent potential runtime errors.</violation>
<violation number="3" location="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts:1218">
P2: Rule violated: **Prefer Typeguards over Type casting**
Avoid inline type casting before method calls. Use `typeof filter.in === 'string'` as the guard instead of just truthy check to safely narrow the type.</violation>
<violation number="4" location="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts:1219">
P2: Rule violated: **Prefer Typeguards over Type casting**
Avoid inline type casting before method calls. Use `typeof filter.out === 'string'` as the guard instead of just truthy check to safely narrow the type.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| { | ||
| name: 'Campaign', | ||
| value: 'campaign', | ||
| description: 'Mark as not interested for a specific campaign', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Missing required: true for campaignId when scope is 'campaign'. Consistent with other campaignId fields in this file, this should be required to prevent API errors when users select 'Campaign' scope.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/descriptions/LeadDescription.ts, line 598:
<comment>Missing `required: true` for campaignId when scope is 'campaign'. Consistent with other campaignId fields in this file, this should be required to prevent API errors when users select 'Campaign' scope.</comment>
<file context>
@@ -243,6 +283,347 @@ export const leadFields: INodeProperties[] = [
+ {
+ name: 'Campaign',
+ value: 'campaign',
+ description: 'Mark as not interested for a specific campaign',
+ },
+ ],
</file context>
✅ Addressed in 7d60da5
| displayName: 'Campaign Name or ID', | ||
| name: 'campaignId', | ||
| type: 'options', | ||
| default: '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Missing required: true for campaignId when scope is 'campaign'. Other campaignId fields in this file (create, delete, update, unsubscribe) are marked as required. Without this, users could select 'Campaign' scope but fail to provide a campaign ID, leading to API errors.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/descriptions/LeadDescription.ts, line 334:
<comment>Missing `required: true` for campaignId when scope is 'campaign'. Other campaignId fields in this file (create, delete, update, unsubscribe) are marked as required. Without this, users could select 'Campaign' scope but fail to provide a campaign ID, leading to API errors.</comment>
<file context>
@@ -243,6 +283,347 @@ export const leadFields: INodeProperties[] = [
+ },
+ options: [
+ {
+ displayName: 'Campaign Name or ID',
+ name: 'campaignId',
+ type: 'options',
</file context>
| displayName: 'Campaign Name or ID', | |
| name: 'campaignId', | |
| type: 'options', | |
| default: '', | |
| displayName: 'Campaign Name or ID', | |
| name: 'campaignId', | |
| type: 'options', | |
| required: true, | |
| default: '', |
✅ Addressed in 7d60da5
| @@ -0,0 +1,522 @@ | |||
| import { NodeTestHarness } from '@nodes-testing/node-test-harness'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Empty test body - this test will always pass without testing anything. The nock mock set up in beforeEach will never be called. Either implement the test using testHarness.setupTests() pattern (like in the Slack node tests) or remove these placeholder tests until they're implemented.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/test/Lemlist.node.test.ts, line 93:
<comment>Empty test body - this test will always pass without testing anything. The nock mock set up in `beforeEach` will never be called. Either implement the test using `testHarness.setupTests()` pattern (like in the Slack node tests) or remove these placeholder tests until they're implemented.</comment>
<file context>
@@ -0,0 +1,522 @@
+ nock.cleanAll();
+ });
+
+ it('should get a specific campaign', async () => {
+ // Test would be similar pattern
+ });
</file context>
✅ Addressed in 7d60da5
| const filterObj: IDataObject = { | ||
| filterId: filter.filterId, | ||
| in: filter.in ? (filter.in as string).split(',').map((v) => v.trim()) : [], | ||
| out: filter.out ? (filter.out as string).split(',').map((v) => v.trim()) : [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Rule violated: Prefer Typeguards over Type casting
Avoid inline type casting before method calls. Use typeof filter.out === 'string' as the guard instead of just truthy check to safely narrow the type.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts, line 1219:
<comment>Avoid inline type casting before method calls. Use `typeof filter.out === 'string'` as the guard instead of just truthy check to safely narrow the type.</comment>
<file context>
@@ -389,6 +661,609 @@ export class LemlistV2 implements INodeType {
+ const filterObj: IDataObject = {
+ filterId: filter.filterId,
+ in: filter.in ? (filter.in as string).split(',').map((v) => v.trim()) : [],
+ out: filter.out ? (filter.out as string).split(',').map((v) => v.trim()) : [],
+ };
+ filtersArray.push(filterObj);
</file context>
| out: filter.out ? (filter.out as string).split(',').map((v) => v.trim()) : [], | |
| out: typeof filter.out === 'string' ? filter.out.split(',').map((v) => v.trim()) : [], |
✅ Addressed in 7d60da5
| for (const filter of filtersInput.filterValues as IDataObject[]) { | ||
| const filterObj: IDataObject = { | ||
| filterId: filter.filterId, | ||
| in: filter.in ? (filter.in as string).split(',').map((v) => v.trim()) : [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Rule violated: Prefer Typeguards over Type casting
Avoid inline type casting before method calls. Use typeof filter.in === 'string' as the guard instead of just truthy check to safely narrow the type.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts, line 1218:
<comment>Avoid inline type casting before method calls. Use `typeof filter.in === 'string'` as the guard instead of just truthy check to safely narrow the type.</comment>
<file context>
@@ -389,6 +661,609 @@ export class LemlistV2 implements INodeType {
+ for (const filter of filtersInput.filterValues as IDataObject[]) {
+ const filterObj: IDataObject = {
+ filterId: filter.filterId,
+ in: filter.in ? (filter.in as string).split(',').map((v) => v.trim()) : [],
+ out: filter.out ? (filter.out as string).split(',').map((v) => v.trim()) : [],
+ };
</file context>
| in: filter.in ? (filter.in as string).split(',').map((v) => v.trim()) : [], | |
| in: typeof filter.in === 'string' ? filter.in.split(',').map((v) => v.trim()) : [], |
✅ Addressed in 7d60da5
| body.name = updateFields.name; | ||
| } | ||
| if (updateFields.labels) { | ||
| body.labels = (updateFields.labels as string).split(',').map((l) => l.trim()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Rule violated: Prefer Typeguards over Type casting
Avoid inline type casting before method calls. Use a type guard or validate the type before calling .split() to prevent potential runtime errors.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts, line 324:
<comment>Avoid inline type casting before method calls. Use a type guard or validate the type before calling `.split()` to prevent potential runtime errors.</comment>
<file context>
@@ -196,7 +280,91 @@ export class LemlistV2 implements INodeType {
+ body.name = updateFields.name;
+ }
+ if (updateFields.labels) {
+ body.labels = (updateFields.labels as string).split(',').map((l) => l.trim());
+ }
+
</file context>
| body.labels = (updateFields.labels as string).split(',').map((l) => l.trim()); | |
| const labels = updateFields.labels; | |
| if (typeof labels === 'string') { | |
| body.labels = labels.split(',').map((l) => l.trim()); | |
| } |
✅ Addressed in 7d60da5
| const body: IDataObject = { name }; | ||
|
|
||
| if (additionalFields.labels) { | ||
| body.labels = (additionalFields.labels as string).split(',').map((l) => l.trim()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Rule violated: Prefer Typeguards over Type casting
Avoid inline type casting before method calls. Use a type guard or validate the type before calling .split(). If additionalFields.labels is not a string, this will cause a runtime error.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts, line 238:
<comment>Avoid inline type casting before method calls. Use a type guard or validate the type before calling `.split()`. If `additionalFields.labels` is not a string, this will cause a runtime error.</comment>
<file context>
@@ -160,12 +224,33 @@ export class LemlistV2 implements INodeType {
+ const body: IDataObject = { name };
+
+ if (additionalFields.labels) {
+ body.labels = (additionalFields.labels as string).split(',').map((l) => l.trim());
+ }
+
</file context>
| body.labels = (additionalFields.labels as string).split(',').map((l) => l.trim()); | |
| const labels = additionalFields.labels; | |
| if (typeof labels === 'string') { | |
| body.labels = labels.split(',').map((l) => l.trim()); | |
| } |
✅ Addressed in 7d60da5
- Add required: true for campaignId in markInterested and markNotInterested - Replace type casts with type guards in LemlistV2.node.ts - Rewrite tests using it.todo() instead of empty test bodies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts">
<violation number="1" location="packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts:1219">
P2: Empty string handling changed: `''` now produces `['']` instead of `[]`. Consider filtering out empty values to maintain previous behavior and avoid sending empty string elements to the API:
```javascript
filter.in.split(',').map((v) => v.trim()).filter(Boolean)
```</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const filterObj: IDataObject = { | ||
| filterId: filter.filterId, | ||
| in: | ||
| typeof filter.in === 'string' ? filter.in.split(',').map((v) => v.trim()) : [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Empty string handling changed: '' now produces [''] instead of []. Consider filtering out empty values to maintain previous behavior and avoid sending empty string elements to the API:
filter.in.split(',').map((v) => v.trim()).filter(Boolean)Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/LemlistV2.node.ts, line 1219:
<comment>Empty string handling changed: `''` now produces `['']` instead of `[]`. Consider filtering out empty values to maintain previous behavior and avoid sending empty string elements to the API:
```javascript
filter.in.split(',').map((v) => v.trim()).filter(Boolean)
```</comment>
<file context>
@@ -1215,8 +1215,12 @@ export class LemlistV2 implements INodeType {
- in: filter.in ? (filter.in as string).split(',').map((v) => v.trim()) : [],
- out: filter.out ? (filter.out as string).split(',').map((v) => v.trim()) : [],
+ in:
+ typeof filter.in === 'string' ? filter.in.split(',').map((v) => v.trim()) : [],
+ out:
+ typeof filter.out === 'string'
</file context>
| typeof filter.in === 'string' ? filter.in.split(',').map((v) => v.trim()) : [], | |
| typeof filter.in === 'string' | |
| ? filter.in.split(',').map((v) => v.trim()).filter(Boolean) | |
| : [], |
✅ Addressed in 76acd32
Add .filter(Boolean) to prevent empty strings from producing [''] instead of []
|
@nicolasmondain I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 18 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/nodes-base/nodes/Lemlist/v2/descriptions/TaskDescription.ts">
<violation number="1" location="packages/nodes-base/nodes/Lemlist/v2/descriptions/TaskDescription.ts:77">
P2: The `status` field has `default: ''` but the options only include 'pending' and 'done'. This mismatch means the dropdown will have an invalid default value when the user adds this field. Either set `default: 'pending'` to use a valid option, or add an empty placeholder option if the API supports omitting the status.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| name: 'entityId', | ||
| type: 'string', | ||
| required: true, | ||
| default: '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The status field has default: '' but the options only include 'pending' and 'done'. This mismatch means the dropdown will have an invalid default value when the user adds this field. Either set default: 'pending' to use a valid option, or add an empty placeholder option if the API supports omitting the status.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/nodes-base/nodes/Lemlist/v2/descriptions/TaskDescription.ts, line 77:
<comment>The `status` field has `default: ''` but the options only include 'pending' and 'done'. This mismatch means the dropdown will have an invalid default value when the user adds this field. Either set `default: 'pending'` to use a valid option, or add an empty placeholder option if the API supports omitting the status.</comment>
<file context>
@@ -0,0 +1,312 @@
+ name: 'entityId',
+ type: 'string',
+ required: true,
+ default: '',
+ description: 'ID of the contact or lead to associate the task with',
+ displayOptions: {
</file context>
| default: '', | |
| default: 'pending', |
✅ Addressed in d0564ff
Summary
This PR updates the LemlistV2 node to cover all current Lemlist API endpoints based on the official documentation at https://developer.lemlist.com.
New Resources Added (8)
Updated Existing Resources
Database Search Operations
The database search operations (searchPeople, searchCompanies) now use the correct API format with:
filterId,in, andoutpropertiespageandsizeparametersexcludesarray for excluding properties from resultsgetFiltersoperation to retrieve available filter IDsTests
Lemlist.node.test.ts,apiResponses.ts,workflow.jsonRelated Linear tickets, Github issues, and Community forum posts
Review / Merge checklist
release/backport(if the PR is an urgent fix that needs to be backported)