@aloma.io/integration-sdk 3.8.61 → 3.8.63
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.
|
@@ -20,6 +20,7 @@ export function buildResolvers(methods, controller) {
|
|
|
20
20
|
return controller[method](args);
|
|
21
21
|
};
|
|
22
22
|
if (method.includes('.')) {
|
|
23
|
+
// Register nested tree for array-based resolution: ["crm", "contacts", "getPage"]
|
|
23
24
|
const parts = method.split('.');
|
|
24
25
|
let node = resolvers;
|
|
25
26
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
@@ -29,6 +30,8 @@ export function buildResolvers(methods, controller) {
|
|
|
29
30
|
node = node[parts[i]];
|
|
30
31
|
}
|
|
31
32
|
node[parts[parts.length - 1]] = handler;
|
|
33
|
+
// Also register flat dotted key for string-based resolution: ["crm.contacts.getPage"]
|
|
34
|
+
resolvers[method] = handler;
|
|
32
35
|
}
|
|
33
36
|
else {
|
|
34
37
|
resolvers[method] = handler;
|
|
@@ -38,15 +38,18 @@ export class OpenAPIToConnector {
|
|
|
38
38
|
if (!this.options.nestedPaths) {
|
|
39
39
|
return this.generateMethodName({ method: httpMethod, path: urlPath, operationId });
|
|
40
40
|
}
|
|
41
|
-
const VERSION_RE = /^v\d
|
|
41
|
+
const VERSION_RE = /^v\d+$|^\d{4}-\d{2}$/;
|
|
42
42
|
const STRIP = new Set(['objects', 'items']);
|
|
43
|
+
// Convert hyphenated segment to camelCase identifier
|
|
44
|
+
const toCamel = (s) => s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
43
45
|
// Build namespace parts from the URL path
|
|
44
46
|
const parts = urlPath
|
|
45
47
|
.replace(/\{[^}]+\}/g, '')
|
|
46
48
|
.split('/')
|
|
47
49
|
.filter(Boolean)
|
|
48
50
|
.filter((p) => !VERSION_RE.test(p))
|
|
49
|
-
.filter((p) => !STRIP.has(p))
|
|
51
|
+
.filter((p) => !STRIP.has(p))
|
|
52
|
+
.map(toCamel);
|
|
50
53
|
// Extract the leaf action from operationId suffix
|
|
51
54
|
let suffix = operationId.includes('_')
|
|
52
55
|
? operationId.split('_').pop()
|
|
@@ -59,10 +62,13 @@ export class OpenAPIToConnector {
|
|
|
59
62
|
.split('/')
|
|
60
63
|
.filter(Boolean)
|
|
61
64
|
.filter((p) => !VERSION_RE.test(p))
|
|
62
|
-
.filter((p) => !STRIP.has(p))
|
|
63
|
-
|
|
65
|
+
.filter((p) => !STRIP.has(p))
|
|
66
|
+
.map(toCamel);
|
|
64
67
|
suffix = suffixParts.length > 0 ? suffixParts[suffixParts.length - 1] : httpMethod.toLowerCase();
|
|
65
68
|
}
|
|
69
|
+
else {
|
|
70
|
+
suffix = toCamel(suffix);
|
|
71
|
+
}
|
|
66
72
|
// Dedup: if suffix equals last path segment, don't repeat it
|
|
67
73
|
const last = parts[parts.length - 1];
|
|
68
74
|
if (suffix === last)
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ export function buildResolvers(methods: string[], controller: any): any {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
if (method.includes('.')) {
|
|
25
|
+
// Register nested tree for array-based resolution: ["crm", "contacts", "getPage"]
|
|
25
26
|
const parts = method.split('.');
|
|
26
27
|
let node = resolvers;
|
|
27
28
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
@@ -31,6 +32,8 @@ export function buildResolvers(methods: string[], controller: any): any {
|
|
|
31
32
|
node = node[parts[i]];
|
|
32
33
|
}
|
|
33
34
|
node[parts[parts.length - 1]] = handler;
|
|
35
|
+
// Also register flat dotted key for string-based resolution: ["crm.contacts.getPage"]
|
|
36
|
+
resolvers[method] = handler;
|
|
34
37
|
} else {
|
|
35
38
|
resolvers[method] = handler;
|
|
36
39
|
}
|
|
@@ -62,16 +62,21 @@ export class OpenAPIToConnector {
|
|
|
62
62
|
return this.generateMethodName({method: httpMethod, path: urlPath, operationId});
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
const VERSION_RE = /^v\d
|
|
65
|
+
const VERSION_RE = /^v\d+$|^\d{4}-\d{2}$/;
|
|
66
66
|
const STRIP = new Set(['objects', 'items']);
|
|
67
67
|
|
|
68
|
+
// Convert hyphenated segment to camelCase identifier
|
|
69
|
+
const toCamel = (s: string): string =>
|
|
70
|
+
s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
71
|
+
|
|
68
72
|
// Build namespace parts from the URL path
|
|
69
73
|
const parts = urlPath
|
|
70
74
|
.replace(/\{[^}]+\}/g, '')
|
|
71
75
|
.split('/')
|
|
72
76
|
.filter(Boolean)
|
|
73
77
|
.filter((p) => !VERSION_RE.test(p))
|
|
74
|
-
.filter((p) => !STRIP.has(p))
|
|
78
|
+
.filter((p) => !STRIP.has(p))
|
|
79
|
+
.map(toCamel);
|
|
75
80
|
|
|
76
81
|
// Extract the leaf action from operationId suffix
|
|
77
82
|
let suffix = operationId.includes('_')
|
|
@@ -86,9 +91,11 @@ export class OpenAPIToConnector {
|
|
|
86
91
|
.split('/')
|
|
87
92
|
.filter(Boolean)
|
|
88
93
|
.filter((p) => !VERSION_RE.test(p))
|
|
89
|
-
.filter((p) => !STRIP.has(p))
|
|
90
|
-
|
|
94
|
+
.filter((p) => !STRIP.has(p))
|
|
95
|
+
.map(toCamel);
|
|
91
96
|
suffix = suffixParts.length > 0 ? suffixParts[suffixParts.length - 1] : httpMethod.toLowerCase();
|
|
97
|
+
} else {
|
|
98
|
+
suffix = toCamel(suffix);
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
// Dedup: if suffix equals last path segment, don't repeat it
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import assert from 'assert';
|
|
2
|
+
|
|
3
|
+
const { OpenAPIToConnector } = await import('../build/openapi-to-connector.mjs');
|
|
4
|
+
|
|
5
|
+
let passed = 0;
|
|
6
|
+
let failed = 0;
|
|
7
|
+
|
|
8
|
+
function test(name, fn) {
|
|
9
|
+
try { fn(); console.log(`✓ PASS: ${name}`); passed++; }
|
|
10
|
+
catch(e) { console.log(`✗ FAIL: ${name} — ${e.message}`); failed++; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const minimalSpec = { openapi: '3.0.0', info: { title: 'Test', version: '1.0' }, paths: {} };
|
|
14
|
+
const gen = new OpenAPIToConnector(minimalSpec, 'test', { nestedPaths: true });
|
|
15
|
+
|
|
16
|
+
// Bug 4: hyphenated path segment must not appear in dotted method path
|
|
17
|
+
test('hyphenated path segment sanitised in nested method path', () => {
|
|
18
|
+
const path = gen.deriveMethodPath('PUT',
|
|
19
|
+
'/crm/lists/2025-09/{listId}/memberships/add-and-remove',
|
|
20
|
+
'put-/crm/lists/2025-09/{listId}/memberships/add-and-remove_addAndRemove');
|
|
21
|
+
const segments = path.split('.');
|
|
22
|
+
segments.forEach(seg => {
|
|
23
|
+
assert(!seg.includes('-'), `Method path segment contains hyphen: "${seg}" in "${path}"`);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Bug 4: path with object-type-id (hyphenated) must produce clean segments
|
|
28
|
+
test('object-type-id hyphenated path sanitised to camelCase', () => {
|
|
29
|
+
const path = gen.deriveMethodPath('GET',
|
|
30
|
+
'/crm/lists/2025-09/object-type-id/{objectTypeId}/name/{listName}',
|
|
31
|
+
'get-/crm/lists/2025-09/object-type-id/{objectTypeId}/name/{listName}_getByName');
|
|
32
|
+
const segments = path.split('.');
|
|
33
|
+
segments.forEach(seg => {
|
|
34
|
+
assert(!seg.includes('-'), `Method path segment contains hyphen: "${seg}" in "${path}"`);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Bug 5: operationId suffix that IS a raw URL path must not duplicate the full path
|
|
39
|
+
test('raw URL suffix does not produce duplicated path in method name', () => {
|
|
40
|
+
const path = gen.deriveMethodPath('GET',
|
|
41
|
+
'/crm/lists/2025-09',
|
|
42
|
+
'get-/crm/lists/2025-09_/crm/lists/2025-09');
|
|
43
|
+
// Should be short and clean, not a duplicated path
|
|
44
|
+
assert(path.length < 40, `Path too long (duplication): "${path}" (${path.length} chars)`);
|
|
45
|
+
assert(!path.includes('_'), `Path contains underscores (non-nested fallback used): "${path}"`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Bug 5: same duplication test for the batch/read endpoint
|
|
49
|
+
test('batch/read raw URL suffix does not produce duplicated path', () => {
|
|
50
|
+
const path = gen.deriveMethodPath('POST',
|
|
51
|
+
'/crm/lists/2025-09/records/memberships/batch/read',
|
|
52
|
+
'post-/crm/lists/2025-09/records/memberships/batch/read_/crm/lists/2025-09/records/memberships/batch/read');
|
|
53
|
+
assert(path.length < 60, `Path too long (duplication): "${path}" (${path.length} chars)`);
|
|
54
|
+
assert(!path.includes('_'), `Path contains underscores (non-nested fallback used): "${path}"`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Bug 4: generateMethodName fallback (no operationId) must sanitise hyphens
|
|
58
|
+
test('generateMethodName fallback sanitises hyphens to camelCase', () => {
|
|
59
|
+
const name = gen.generateMethodName({
|
|
60
|
+
method: 'PUT',
|
|
61
|
+
path: '/crm/lists/2025-09/folders/move-list',
|
|
62
|
+
operationId: undefined
|
|
63
|
+
});
|
|
64
|
+
assert(!name.includes('-'), `Fallback name contains hyphen: "${name}"`);
|
|
65
|
+
assert(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name), `Not a valid identifier: "${name}"`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
console.log(`\n${passed} passing / ${failed} failing`);
|
|
69
|
+
if (failed > 0) process.exit(1);
|