Skip to content

Commit 69aecbd

Browse files
JulianPscheidclaude
andcommitted
Release v1.1.0: Add Topics support and improve reliability
## New Features - Add Topics resource with three operations: - Get Topic: Retrieve a specific topic by ID - Get Many Topics: List all available topics - Get Topic Sessions: Get all sessions associated with a topic - Implement client-side pagination for endpoints without server-side support ## Improvements - Remove console.log statements for production readiness - Clean up internal documentation and test files - Simplify credential display name from "Hedy API" to "Hedy" - Add Hedy logo to both action and trigger nodes - Remove HTTP Request node option to avoid confusion ## Bug Fixes - Fix webhook signature verification to handle missing secrets gracefully - Fix authentication test success/error message confusion - Relax HTTPS requirement for localhost webhook URLs (development) - Fix limit parameter for all resources with client-side slicing when needed: - /topics endpoint - /todos endpoint - /topics/{id}/sessions endpoint - /sessions/{id}/todos endpoint ## Technical Changes - Update package version to 1.1.0 - Remove all debugging console statements - Clean up codebase for public repository release Ready for npm publication. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent abd3fe2 commit 69aecbd

7 files changed

Lines changed: 243 additions & 82 deletions

File tree

CHANGELOG.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.1.0] - 2025-01-09
11+
12+
### Added
13+
- Topics resource with three operations:
14+
- Get Topic: Retrieve a specific topic by ID
15+
- Get Many Topics: List all available topics
16+
- Get Topic Sessions: Get all sessions associated with a specific topic
17+
- Client-side pagination for endpoints that don't support server-side limits:
18+
- `/topics` endpoint
19+
- `/todos` endpoint
20+
- `/topics/{id}/sessions` endpoint
21+
- `/sessions/{id}/todos` endpoint
22+
23+
### Fixed
24+
- Webhook signature verification now handles missing signing secrets gracefully
25+
- Improved error messages for webhook registration failures
26+
- Signature verification defaults to disabled for easier testing
27+
- HTTPS requirement relaxed for localhost webhook URLs
28+
- Fixed confusing authentication test success/error message
29+
- Limit parameter now works correctly for all resources (client-side when API doesn't support it)
30+
31+
### Changed
32+
- Removed HTTP Request node option to avoid confusion (users should use dedicated Hedy nodes)
33+
- Credential display name simplified from "Hedy API" to "Hedy"
34+
- Added Hedy logo to both action and trigger nodes
35+
1036
## [1.0.0] - 2025-01-09
1137

1238
### Added
@@ -60,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6086
### Upgrade Notes
6187
When upgrading between major versions, please review the migration guide in the documentation.
6288

63-
[Unreleased]: https://github.com/HedyAI/n8n-nodes-hedy/compare/v1.0.0...HEAD
89+
[Unreleased]: https://github.com/HedyAI/n8n-nodes-hedy/compare/v1.1.0...HEAD
90+
[1.1.0]: https://github.com/HedyAI/n8n-nodes-hedy/compare/v1.0.0...v1.1.0
6491
[1.0.0]: https://github.com/HedyAI/n8n-nodes-hedy/releases/tag/v1.0.0
6592
[0.1.0]: https://github.com/HedyAI/n8n-nodes-hedy/releases/tag/v0.1.0

credentials/HedyApi.credentials.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ import {
77

88
export class HedyApi implements ICredentialType {
99
name = 'hedyApi';
10-
displayName = 'Hedy API';
10+
displayName = 'Hedy';
1111
documentationUrl = 'https://api.hedy.bot/docs';
12-
httpRequestNode = {
13-
name: 'Hedy',
14-
docsUrl: 'https://api.hedy.bot/docs',
15-
apiBaseUrl: 'https://api.hedy.bot/',
16-
};
12+
// Removed httpRequestNode to prevent generic HTTP Request node from appearing
13+
// Users should use the dedicated Hedy node for all operations
1714
properties: INodeProperties[] = [
1815
{
1916
displayName: 'API Key',
@@ -45,9 +42,9 @@ export class HedyApi implements ICredentialType {
4542
{
4643
type: 'responseSuccessBody',
4744
properties: {
45+
message: 'Connected successfully!',
4846
key: 'success',
4947
value: true,
50-
message: 'Authentication successful!',
5148
},
5249
},
5350
],

nodes/Hedy/GenericFunctions.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export async function hedyApiRequest(
6868
const apiError = error as any;
6969
const errorCode = apiError.error?.code || 'unknown_error';
7070
const errorMessage = apiError.error?.message || 'An unknown error occurred';
71+
7172

7273
// Provide user-friendly error messages
7374
switch (errorCode) {
@@ -86,6 +87,16 @@ export async function hedyApiRequest(
8687
this.getNode(),
8788
'Invalid event type. Valid events: session.created, session.ended, highlight.created, todo.exported',
8889
);
90+
case ErrorCode.InvalidWebhookUrl:
91+
throw new NodeOperationError(
92+
this.getNode(),
93+
'The Hedy API requires a publicly accessible webhook URL. For local testing, use a tunneling service like ngrok (https://ngrok.com) to expose your local n8n instance. Example: ngrok http 5678',
94+
);
95+
case ErrorCode.InvalidParameter:
96+
throw new NodeOperationError(
97+
this.getNode(),
98+
`Invalid parameter: ${errorMessage}. Please check your webhook configuration.`,
99+
);
89100
default:
90101
throw new NodeOperationError(this.getNode(), errorMessage);
91102
}
@@ -140,7 +151,7 @@ export async function hedyApiRequestAllItems(
140151

141152
// Safety check to prevent infinite loops
142153
if (returnData.length >= 1000) {
143-
console.warn('Reached maximum item limit of 1000');
154+
// Reached maximum item limit of 1000
144155
break;
145156
}
146157
}
@@ -202,7 +213,7 @@ export async function unregisterWebhook(
202213
);
203214
} catch (error) {
204215
// Ignore errors when deleting webhooks (webhook might already be deleted)
205-
console.warn(`Failed to delete webhook ${webhookId}:`, error);
216+
// Failed to delete webhook - this is expected if webhook was already deleted
206217
}
207218
}
208219

nodes/Hedy/Hedy.node.ts

Lines changed: 159 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class Hedy implements INodeType {
1919
description: INodeTypeDescription = {
2020
displayName: 'Hedy',
2121
name: 'hedy',
22-
icon: 'file:hedy.svg',
22+
icon: 'file:hedy.png',
2323
group: ['output'],
2424
version: 1,
2525
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
@@ -60,6 +60,11 @@ export class Hedy implements INodeType {
6060
value: 'todo',
6161
description: 'Todo item operations',
6262
},
63+
{
64+
name: 'Topic',
65+
value: 'topic',
66+
description: 'Topic operations for organizing sessions',
67+
},
6368
],
6469
},
6570

@@ -150,6 +155,41 @@ export class Hedy implements INodeType {
150155
],
151156
},
152157

158+
// Topic operations
159+
{
160+
displayName: 'Operation',
161+
name: 'operation',
162+
type: 'options',
163+
noDataExpression: true,
164+
displayOptions: {
165+
show: {
166+
resource: ['topic'],
167+
},
168+
},
169+
default: 'get',
170+
required: true,
171+
options: [
172+
{
173+
name: 'Get',
174+
value: 'get',
175+
description: 'Get a specific topic by ID',
176+
action: 'Get a topic',
177+
},
178+
{
179+
name: 'Get Many',
180+
value: 'getAll',
181+
description: 'Get all topics',
182+
action: 'Get many topics',
183+
},
184+
{
185+
name: 'Get Topic Sessions',
186+
value: 'getSessions',
187+
description: 'Get all sessions for a specific topic',
188+
action: 'Get sessions by topic',
189+
},
190+
],
191+
},
192+
153193
// Session ID parameter
154194
{
155195
displayName: 'Session ID',
@@ -201,15 +241,32 @@ export class Hedy implements INodeType {
201241
placeholder: 'high_xyz789',
202242
},
203243

244+
// Topic ID parameter for get operation
245+
{
246+
displayName: 'Topic ID',
247+
name: 'topicId',
248+
type: 'string',
249+
required: true,
250+
displayOptions: {
251+
show: {
252+
resource: ['topic'],
253+
operation: ['get', 'getSessions'],
254+
},
255+
},
256+
default: '',
257+
description: 'The ID of the topic',
258+
placeholder: 'topic_abc123',
259+
},
260+
204261
// Return All parameter
205262
{
206263
displayName: 'Return All',
207264
name: 'returnAll',
208265
type: 'boolean',
209266
displayOptions: {
210267
show: {
211-
resource: ['session', 'highlight', 'todo'],
212-
operation: ['getAll', 'getBySession'],
268+
resource: ['session', 'highlight', 'todo', 'topic'],
269+
operation: ['getAll', 'getBySession', 'getSessions'],
213270
},
214271
},
215272
default: false,
@@ -223,8 +280,8 @@ export class Hedy implements INodeType {
223280
type: 'number',
224281
displayOptions: {
225282
show: {
226-
resource: ['session', 'highlight', 'todo'],
227-
operation: ['getAll', 'getBySession'],
283+
resource: ['session', 'highlight', 'todo', 'topic'],
284+
operation: ['getAll', 'getBySession', 'getSessions'],
228285
returnAll: [false],
229286
},
230287
},
@@ -406,28 +463,19 @@ export class Hedy implements INodeType {
406463
// Get all todos
407464
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
408465

409-
if (returnAll) {
410-
responseData = await hedyApiRequestAllItems.call(
411-
this,
412-
'/todos',
413-
);
414-
} else {
415-
const limit = this.getNodeParameter('limit', i) as number;
416-
const qs: IDataObject = {
417-
limit,
418-
};
419-
420-
responseData = await hedyApiRequest.call(
421-
this,
422-
'GET',
423-
'/todos',
424-
undefined,
425-
qs,
426-
);
466+
// Note: The todos endpoint returns all todos as a flat array
467+
// and doesn't support server-side pagination
468+
responseData = await hedyApiRequest.call(
469+
this,
470+
'GET',
471+
'/todos',
472+
);
427473

428-
// Handle pagination response
429-
if (responseData && typeof responseData === 'object' && 'data' in responseData) {
430-
responseData = responseData.data;
474+
// Apply client-side limit if not returning all
475+
if (!returnAll) {
476+
const limit = this.getNodeParameter('limit', i) as number;
477+
if (Array.isArray(responseData) && responseData.length > limit) {
478+
responseData = responseData.slice(0, limit);
431479
}
432480
}
433481
} else if (operation === 'getBySession') {
@@ -440,28 +488,94 @@ export class Hedy implements INodeType {
440488

441489
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
442490

443-
if (returnAll) {
444-
responseData = await hedyApiRequestAllItems.call(
445-
this,
446-
`/sessions/${sessionId}/todos`,
447-
);
448-
} else {
491+
// Note: The session todos endpoint doesn't support server-side pagination
492+
// We always fetch all todos for the session and slice client-side if needed
493+
responseData = await hedyApiRequest.call(
494+
this,
495+
'GET',
496+
`/sessions/${sessionId}/todos`,
497+
);
498+
499+
// Handle response format
500+
if (responseData && typeof responseData === 'object' && 'data' in responseData) {
501+
responseData = responseData.data;
502+
}
503+
504+
// Apply client-side limit if not returning all
505+
if (!returnAll) {
449506
const limit = this.getNodeParameter('limit', i) as number;
450-
const qs: IDataObject = {
451-
limit,
452-
};
507+
if (Array.isArray(responseData) && responseData.length > limit) {
508+
responseData = responseData.slice(0, limit);
509+
}
510+
}
511+
}
512+
} else if (resource === 'topic') {
513+
// Topic operations
514+
if (operation === 'get') {
515+
// Get a specific topic
516+
const topicId = this.getNodeParameter('topicId', i) as string;
517+
518+
if (!topicId) {
519+
throw new NodeOperationError(this.getNode(), 'Topic ID is required');
520+
}
453521

454-
responseData = await hedyApiRequest.call(
455-
this,
456-
'GET',
457-
`/sessions/${sessionId}/todos`,
458-
undefined,
459-
qs,
460-
);
522+
responseData = await hedyApiRequest.call(
523+
this,
524+
'GET',
525+
`/topics/${topicId}`,
526+
);
527+
} else if (operation === 'getAll') {
528+
// Get all topics
529+
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
461530

462-
// Handle pagination response
463-
if (responseData && typeof responseData === 'object' && 'data' in responseData) {
464-
responseData = responseData.data;
531+
// Note: The topics endpoint doesn't support server-side pagination
532+
// We always fetch all topics and then slice client-side if needed
533+
responseData = await hedyApiRequest.call(
534+
this,
535+
'GET',
536+
'/topics',
537+
);
538+
539+
// Handle response format
540+
if (responseData && typeof responseData === 'object' && 'data' in responseData) {
541+
responseData = responseData.data;
542+
}
543+
544+
// Apply client-side limit if not returning all
545+
if (!returnAll) {
546+
const limit = this.getNodeParameter('limit', i) as number;
547+
if (Array.isArray(responseData) && responseData.length > limit) {
548+
responseData = responseData.slice(0, limit);
549+
}
550+
}
551+
} else if (operation === 'getSessions') {
552+
// Get sessions for a specific topic
553+
const topicId = this.getNodeParameter('topicId', i) as string;
554+
555+
if (!topicId) {
556+
throw new NodeOperationError(this.getNode(), 'Topic ID is required');
557+
}
558+
559+
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
560+
561+
// Note: The topic sessions endpoint doesn't support server-side pagination
562+
// We always fetch all sessions for the topic and slice client-side if needed
563+
responseData = await hedyApiRequest.call(
564+
this,
565+
'GET',
566+
`/topics/${topicId}/sessions`,
567+
);
568+
569+
// Handle response format
570+
if (responseData && typeof responseData === 'object' && 'data' in responseData) {
571+
responseData = responseData.data;
572+
}
573+
574+
// Apply client-side limit if not returning all
575+
if (!returnAll) {
576+
const limit = this.getNodeParameter('limit', i) as number;
577+
if (Array.isArray(responseData) && responseData.length > limit) {
578+
responseData = responseData.slice(0, limit);
465579
}
466580
}
467581
}

0 commit comments

Comments
 (0)