n8n-nodes-cala 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,13 +26,9 @@ You need a Cala API key to use this node:
26
26
  3. In n8n, create new credentials of type **Cala API**
27
27
  4. Enter your API key
28
28
 
29
- ## Operations
29
+ ## Endpoint
30
30
 
31
- ### Knowledge
32
-
33
- | Operation | Description |
34
- |-----------|-------------|
35
- | **Search** | Search verified knowledge using natural language queries |
31
+ The node provides access to the **Knowledge Search** endpoint, which searches trusted knowledge using natural language queries.
36
32
 
37
33
  ### Example
38
34
 
@@ -73,50 +69,6 @@ You need a Cala API key to use this node:
73
69
  - [Cala Console](https://console.cala.ai)
74
70
  - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)
75
71
 
76
- ## Development
77
-
78
- ### Requirements
79
-
80
- - Node.js >= 22
81
- - pnpm >= 10
82
-
83
- ### Project Structure
84
-
85
- ```text
86
- cala-n8n/
87
- ├── credentials/
88
- │ └── CalaApi.credentials.ts # API credentials definition
89
- ├── nodes/
90
- │ └── Cala/
91
- │ ├── Cala.node.ts # Main node logic
92
- │ └── cala.svg # Node icon
93
- ├── dist/ # Compiled output
94
- ├── Makefile # Development commands
95
- ├── package.json
96
- ├── tsconfig.json
97
- └── gulpfile.js
98
- ```
99
-
100
- ### Quick Start
101
-
102
- ```bash
103
- make start # Build + start n8n at http://localhost:5678
104
- make stop # Stop n8n
105
- ```
106
-
107
- ### Commands
108
-
109
- | Command | Description |
110
- | ------- | ----------- |
111
- | `make install` | Install dependencies |
112
- | `make build` | Build the project |
113
- | `make dev` | Development mode (watch) |
114
- | `make start` | Start n8n locally |
115
- | `make stop` | Stop n8n |
116
- | `make publish` | Publish to npm |
117
- | `make verify` | Run n8n linter |
118
- | `make clean` | Remove build artifacts |
119
-
120
72
  ## License
121
73
 
122
74
  MIT
@@ -18,14 +18,6 @@ class CalaApi {
18
18
  required: true,
19
19
  description: 'Your Cala API key',
20
20
  },
21
- {
22
- displayName: 'Base URL',
23
- name: 'baseUrl',
24
- type: 'string',
25
- default: 'https://api.cala.ai',
26
- required: true,
27
- description: 'The base URL of the Cala API',
28
- },
29
21
  ];
30
22
  }
31
23
  }
@@ -9,8 +9,8 @@ class Cala {
9
9
  icon: 'file:cala.svg',
10
10
  group: ['transform'],
11
11
  version: 1,
12
- subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
13
- description: 'Search verified knowledge with Cala AI',
12
+ subtitle: 'Knowledge Search',
13
+ description: 'Search trusted knowledge with Cala AI',
14
14
  defaults: {
15
15
  name: 'Cala',
16
16
  },
@@ -23,53 +23,14 @@ class Cala {
23
23
  },
24
24
  ],
25
25
  properties: [
26
- {
27
- displayName: 'Resource',
28
- name: 'resource',
29
- type: 'options',
30
- noDataExpression: true,
31
- options: [
32
- {
33
- name: 'Knowledge',
34
- value: 'knowledge',
35
- },
36
- ],
37
- default: 'knowledge',
38
- },
39
- {
40
- displayName: 'Operation',
41
- name: 'operation',
42
- type: 'options',
43
- noDataExpression: true,
44
- displayOptions: {
45
- show: {
46
- resource: ['knowledge'],
47
- },
48
- },
49
- options: [
50
- {
51
- name: 'Search',
52
- value: 'search',
53
- description: 'Search verified knowledge',
54
- action: 'Search verified knowledge',
55
- },
56
- ],
57
- default: 'search',
58
- },
59
26
  {
60
27
  displayName: 'Query',
61
28
  name: 'query',
62
29
  type: 'string',
63
30
  required: true,
64
- displayOptions: {
65
- show: {
66
- resource: ['knowledge'],
67
- operation: ['search'],
68
- },
69
- },
70
31
  default: '',
71
- placeholder: 'What is the refund policy?',
72
- description: 'The search query to find verified knowledge',
32
+ placeholder: "i.e. What were Toyota's total sales in 2023?",
33
+ description: 'The search query to find knowledge',
73
34
  },
74
35
  ],
75
36
  };
@@ -78,33 +39,28 @@ class Cala {
78
39
  const items = this.getInputData();
79
40
  const returnData = [];
80
41
  const credentials = await this.getCredentials('calaApi');
81
- const baseUrl = credentials.baseUrl.replace(/\/$/, '');
82
42
  const apiKey = credentials.apiKey;
83
43
  for (let i = 0; i < items.length; i++) {
84
- const resource = this.getNodeParameter('resource', i);
85
- const operation = this.getNodeParameter('operation', i);
86
- if (resource === 'knowledge' && operation === 'search') {
87
- const query = this.getNodeParameter('query', i);
88
- const headers = {
89
- 'Content-Type': 'application/json',
90
- };
91
- if (apiKey) {
92
- headers['X-API-KEY'] = apiKey;
93
- }
94
- const response = await this.helpers.httpRequest({
95
- method: 'POST',
96
- url: `${baseUrl}/v1/knowledge/search`,
97
- headers,
98
- body: {
99
- input: query,
100
- },
101
- json: true,
102
- });
103
- returnData.push({
104
- json: response,
105
- pairedItem: { item: i },
106
- });
44
+ const query = this.getNodeParameter('query', i);
45
+ const headers = {
46
+ 'Content-Type': 'application/json',
47
+ };
48
+ if (apiKey) {
49
+ headers['X-API-KEY'] = apiKey;
107
50
  }
51
+ const response = await this.helpers.httpRequest({
52
+ method: 'POST',
53
+ url: 'https://api.cala.ai/v1/knowledge/search',
54
+ headers,
55
+ body: {
56
+ input: query,
57
+ },
58
+ json: true,
59
+ });
60
+ returnData.push({
61
+ json: response,
62
+ pairedItem: { item: i },
63
+ });
108
64
  }
109
65
  return [returnData];
110
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-cala",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "n8n nodes for Cala AI knowledge search",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -17,11 +17,11 @@
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "https://gitlab.com/cala-ai/cala-n8n.git"
20
+ "url": "https://github.com/cala-ai/cala-n8n-node.git"
21
21
  },
22
- "homepage": "https://gitlab.com/cala-ai/cala-n8n",
22
+ "homepage": "https://github.com/cala-ai/cala-n8n-node",
23
23
  "bugs": {
24
- "url": "https://gitlab.com/cala-ai/cala-n8n/-/issues"
24
+ "url": "https://github.com/cala-ai/cala-n8n-node/issues"
25
25
  },
26
26
  "files": [
27
27
  "dist",
@@ -44,7 +44,6 @@
44
44
  "eslint": "^9.39.2",
45
45
  "gulp": "^5.0.0",
46
46
  "jest": "^30.2.0",
47
- "n8n-workflow": "^1.0.0",
48
47
  "ts-jest": "^29.4.6",
49
48
  "typescript": "^5.3.0",
50
49
  "typescript-eslint": "^8.53.1"
@@ -1 +0,0 @@
1
- export {};
@@ -1,32 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const CalaApi_credentials_1 = require("./CalaApi.credentials");
4
- describe('CalaApi Credentials', () => {
5
- let credentials;
6
- beforeEach(() => {
7
- credentials = new CalaApi_credentials_1.CalaApi();
8
- });
9
- it('should have correct name', () => {
10
- expect(credentials.name).toBe('calaApi');
11
- });
12
- it('should have correct display name', () => {
13
- expect(credentials.displayName).toBe('Cala API');
14
- });
15
- it('should have documentation URL', () => {
16
- expect(credentials.documentationUrl).toBe('https://docs.cala.ai');
17
- });
18
- it('should have API key property', () => {
19
- var _a;
20
- const apiKeyProp = credentials.properties.find(p => p.name === 'apiKey');
21
- expect(apiKeyProp).toBeDefined();
22
- expect(apiKeyProp === null || apiKeyProp === void 0 ? void 0 : apiKeyProp.type).toBe('string');
23
- expect(apiKeyProp === null || apiKeyProp === void 0 ? void 0 : apiKeyProp.required).toBe(true);
24
- expect((_a = apiKeyProp === null || apiKeyProp === void 0 ? void 0 : apiKeyProp.typeOptions) === null || _a === void 0 ? void 0 : _a.password).toBe(true);
25
- });
26
- it('should have base URL property with default value', () => {
27
- const baseUrlProp = credentials.properties.find(p => p.name === 'baseUrl');
28
- expect(baseUrlProp).toBeDefined();
29
- expect(baseUrlProp === null || baseUrlProp === void 0 ? void 0 : baseUrlProp.type).toBe('string');
30
- expect(baseUrlProp === null || baseUrlProp === void 0 ? void 0 : baseUrlProp.default).toBe('https://api.cala.ai');
31
- });
32
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,128 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const Cala_node_1 = require("./Cala.node");
4
- describe('Cala Node', () => {
5
- let node;
6
- beforeEach(() => {
7
- node = new Cala_node_1.Cala();
8
- });
9
- describe('execute', () => {
10
- const createExecutionContext = ({ baseUrl = 'https://api.cala.ai/', apiKey = 'test-key', resource = 'knowledge', operation = 'search', query = 'What is Cala?', response = { content: 'ok' }, } = {}) => {
11
- const httpRequest = jest.fn(async () => response);
12
- return {
13
- getInputData: jest.fn(() => [{ json: { input: 'one' } }]),
14
- getNodeParameter: jest.fn((name, _index) => {
15
- if (name === 'resource') {
16
- return resource;
17
- }
18
- if (name === 'operation') {
19
- return operation;
20
- }
21
- if (name === 'query') {
22
- return query;
23
- }
24
- throw new Error(`Unexpected parameter name: ${name}`);
25
- }),
26
- getCredentials: jest.fn(async () => ({ baseUrl, apiKey })),
27
- helpers: {
28
- httpRequest,
29
- },
30
- };
31
- };
32
- it('should call Cala API with normalized base URL', async () => {
33
- const context = createExecutionContext();
34
- const result = await node.execute.call(context);
35
- expect(context.helpers.httpRequest).toHaveBeenCalledWith({
36
- method: 'POST',
37
- url: 'https://api.cala.ai/v1/knowledge/search',
38
- headers: {
39
- 'Content-Type': 'application/json',
40
- 'X-API-KEY': 'test-key',
41
- },
42
- body: {
43
- input: 'What is Cala?',
44
- },
45
- json: true,
46
- });
47
- expect(result).toEqual([[{ json: { content: 'ok' }, pairedItem: { item: 0 } }]]);
48
- });
49
- it('should omit API key header when missing', async () => {
50
- const context = createExecutionContext({ apiKey: '' });
51
- await node.execute.call(context);
52
- expect(context.helpers.httpRequest).toHaveBeenCalledWith({
53
- method: 'POST',
54
- url: 'https://api.cala.ai/v1/knowledge/search',
55
- headers: {
56
- 'Content-Type': 'application/json',
57
- },
58
- body: {
59
- input: 'What is Cala?',
60
- },
61
- json: true,
62
- });
63
- });
64
- it('should process multiple items', async () => {
65
- const queries = ['Query 1', 'Query 2', 'Query 3'];
66
- const httpRequest = jest.fn()
67
- .mockResolvedValueOnce({ answer: 'Answer 1' })
68
- .mockResolvedValueOnce({ answer: 'Answer 2' })
69
- .mockResolvedValueOnce({ answer: 'Answer 3' });
70
- const context = {
71
- getInputData: jest.fn(() => queries.map(q => ({ json: { query: q } }))),
72
- getNodeParameter: jest.fn((name, index) => {
73
- if (name === 'resource')
74
- return 'knowledge';
75
- if (name === 'operation')
76
- return 'search';
77
- if (name === 'query')
78
- return queries[index];
79
- throw new Error(`Unexpected parameter: ${name}`);
80
- }),
81
- getCredentials: jest.fn(async () => ({ baseUrl: 'https://api.cala.ai', apiKey: 'test-key' })),
82
- helpers: { httpRequest },
83
- };
84
- const result = await node.execute.call(context);
85
- expect(httpRequest).toHaveBeenCalledTimes(3);
86
- expect(result[0]).toHaveLength(3);
87
- expect(result[0][0].json).toEqual({ answer: 'Answer 1' });
88
- expect(result[0][1].json).toEqual({ answer: 'Answer 2' });
89
- expect(result[0][2].json).toEqual({ answer: 'Answer 3' });
90
- });
91
- it('should propagate HTTP errors', async () => {
92
- const httpRequest = jest.fn().mockRejectedValue(new Error('API Error: 500 Internal Server Error'));
93
- const context = {
94
- getInputData: jest.fn(() => [{ json: {} }]),
95
- getNodeParameter: jest.fn((name) => {
96
- if (name === 'resource')
97
- return 'knowledge';
98
- if (name === 'operation')
99
- return 'search';
100
- if (name === 'query')
101
- return 'test query';
102
- throw new Error(`Unexpected parameter: ${name}`);
103
- }),
104
- getCredentials: jest.fn(async () => ({ baseUrl: 'https://api.cala.ai', apiKey: 'test-key' })),
105
- helpers: { httpRequest },
106
- };
107
- await expect(node.execute.call(context)).rejects.toThrow('API Error: 500 Internal Server Error');
108
- });
109
- it('should return empty array for unsupported resource/operation', async () => {
110
- const httpRequest = jest.fn();
111
- const context = {
112
- getInputData: jest.fn(() => [{ json: {} }]),
113
- getNodeParameter: jest.fn((name) => {
114
- if (name === 'resource')
115
- return 'unsupported';
116
- if (name === 'operation')
117
- return 'unknown';
118
- throw new Error(`Unexpected parameter: ${name}`);
119
- }),
120
- getCredentials: jest.fn(async () => ({ baseUrl: 'https://api.cala.ai', apiKey: 'test-key' })),
121
- helpers: { httpRequest },
122
- };
123
- const result = await node.execute.call(context);
124
- expect(httpRequest).not.toHaveBeenCalled();
125
- expect(result).toEqual([[]]);
126
- });
127
- });
128
- });