@unito/integration-sdk 0.1.1 → 0.1.3

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
@@ -6,7 +6,7 @@ This SDK can be used to produce integrations that are compliant with the Integra
6
6
  ## Getting started
7
7
 
8
8
  ```sh
9
- npm install --save @unitoio/integration-sdk
9
+ npm install --save @unito/integration-sdk
10
10
  ```
11
11
 
12
12
  ## Terms and Conditions
@@ -9,6 +9,17 @@ function assertValidPath(path) {
9
9
  throw new InvalidHandler(`The provided path '${path}' is invalid. Paths must not end with a '/'.`);
10
10
  }
11
11
  }
12
+ function assertValidConfiguration(path, pathWithIdentifier, handlers) {
13
+ if (path === pathWithIdentifier) {
14
+ const individualHandlers = ['getItem', 'updateItem', 'deleteItem'];
15
+ const collectionHandlers = ['getCollection', 'createItem'];
16
+ const hasIndividualHandlers = individualHandlers.some(handler => handler in handlers);
17
+ const hasCollectionHandlers = collectionHandlers.some(handler => handler in handlers);
18
+ if (hasIndividualHandlers && hasCollectionHandlers) {
19
+ throw new InvalidHandler(`The provided path '${path}' doesn't differentiate between individual and collection level operation, so you cannot define both. `);
20
+ }
21
+ }
22
+ }
12
23
  function parsePath(path) {
13
24
  const pathParts = path.split('/');
14
25
  const lastPart = pathParts.at(-1);
@@ -60,9 +71,9 @@ export class Handler {
60
71
  pathWithIdentifier;
61
72
  handlers;
62
73
  constructor(inputPath, handlers) {
63
- // Build paths.
64
74
  assertValidPath(inputPath);
65
75
  const { pathWithIdentifier, path } = parsePath(inputPath);
76
+ assertValidConfiguration(path, pathWithIdentifier, handlers);
66
77
  this.pathWithIdentifier = pathWithIdentifier;
67
78
  this.path = path;
68
79
  this.handlers = handlers;
@@ -28,7 +28,7 @@ export interface RequestOptions {
28
28
  };
29
29
  }
30
30
  export interface Response<T> {
31
- data: T | undefined;
31
+ data: T;
32
32
  status: number;
33
33
  headers: Headers;
34
34
  }
@@ -54,6 +54,6 @@ export declare class Provider {
54
54
  post<T>(endpoint: string, body: Record<string, unknown>, options: RequestOptions): Promise<Response<T>>;
55
55
  put<T>(endpoint: string, body: Record<string, unknown>, options: RequestOptions): Promise<Response<T>>;
56
56
  patch<T>(endpoint: string, body: Record<string, unknown>, options: RequestOptions): Promise<Response<T>>;
57
- delete<T>(endpoint: string, options: RequestOptions): Promise<Response<T>>;
57
+ delete<T = undefined>(endpoint: string, options: RequestOptions): Promise<Response<T>>;
58
58
  private fetchWrapper;
59
59
  }
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { afterEach, beforeEach, describe, it, mock } from 'node:test';
3
- import { Handler } from '../src/handler.js';
3
+ import { Handler, } from '../src/handler.js';
4
4
  import { BadRequestError } from '../src/httpErrors.js';
5
5
  describe('Handler', () => {
6
6
  beforeEach(() => {
@@ -36,6 +36,30 @@ describe('Handler', () => {
36
36
  }
37
37
  }
38
38
  });
39
+ it('validates configuration', () => {
40
+ const getItem = (() => { });
41
+ const updateItem = (() => { });
42
+ const deleteItem = (() => { });
43
+ const getCollection = (() => { });
44
+ const createItem = (() => { });
45
+ const paths = [
46
+ ['/foo', { getItem }, true],
47
+ ['/foo', { getCollection }, true],
48
+ ['/foo', { getItem, getCollection }, false],
49
+ ['/foo', { updateItem, createItem }, false],
50
+ ['/foo', { deleteItem, createItem }, false],
51
+ ['/foo/:bar', { getItem, getCollection }, true],
52
+ ['/foo/:bar', { getItem, updateItem, deleteItem, createItem, getCollection }, true],
53
+ ];
54
+ for (const [path, handlers, valid] of paths) {
55
+ if (valid) {
56
+ assert.doesNotThrow(() => new Handler(path, handlers));
57
+ }
58
+ else {
59
+ assert.throws(() => new Handler(path, handlers));
60
+ }
61
+ }
62
+ });
39
63
  });
40
64
  describe('generate', () => {
41
65
  async function executeHandler(handler, request = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unito/integration-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Integration SDK",
5
5
  "type": "module",
6
6
  "types": "dist/src/index.d.ts",
package/src/handler.ts CHANGED
@@ -118,6 +118,22 @@ function assertValidPath(path: string): asserts path is Path {
118
118
  }
119
119
  }
120
120
 
121
+ function assertValidConfiguration(path: Path, pathWithIdentifier: Path, handlers: Handlers) {
122
+ if (path === pathWithIdentifier) {
123
+ const individualHandlers = ['getItem', 'updateItem', 'deleteItem'];
124
+ const collectionHandlers = ['getCollection', 'createItem'];
125
+
126
+ const hasIndividualHandlers = individualHandlers.some(handler => handler in handlers);
127
+ const hasCollectionHandlers = collectionHandlers.some(handler => handler in handlers);
128
+
129
+ if (hasIndividualHandlers && hasCollectionHandlers) {
130
+ throw new InvalidHandler(
131
+ `The provided path '${path}' doesn't differentiate between individual and collection level operation, so you cannot define both. `,
132
+ );
133
+ }
134
+ }
135
+ }
136
+
121
137
  function parsePath(path: Path) {
122
138
  const pathParts = path.split('/');
123
139
 
@@ -185,11 +201,12 @@ export class Handler {
185
201
  private handlers: Handlers;
186
202
 
187
203
  constructor(inputPath: string, handlers: HandlersInput) {
188
- // Build paths.
189
204
  assertValidPath(inputPath);
190
205
 
191
206
  const { pathWithIdentifier, path } = parsePath(inputPath);
192
207
 
208
+ assertValidConfiguration(path, pathWithIdentifier, handlers);
209
+
193
210
  this.pathWithIdentifier = pathWithIdentifier;
194
211
  this.path = path;
195
212
 
@@ -29,7 +29,7 @@ export interface RequestOptions {
29
29
  }
30
30
 
31
31
  export interface Response<T> {
32
- data: T | undefined;
32
+ data: T;
33
33
  status: number;
34
34
  headers: Headers;
35
35
  }
@@ -100,7 +100,7 @@ export class Provider {
100
100
  });
101
101
  }
102
102
 
103
- public async delete<T>(endpoint: string, options: RequestOptions): Promise<Response<T>> {
103
+ public async delete<T = undefined>(endpoint: string, options: RequestOptions): Promise<Response<T>> {
104
104
  return this.fetchWrapper<T>(endpoint, null, {
105
105
  ...options,
106
106
  method: 'DELETE',
@@ -149,8 +149,7 @@ export class Provider {
149
149
  }
150
150
 
151
151
  try {
152
- const data: T | undefined = response.body ? await response.json() : undefined;
153
-
152
+ const data: T = response.body ? await response.json() : undefined;
154
153
  return { status: response.status, headers: response.headers, data };
155
154
  } catch {
156
155
  throw buildHttpError(400, 'Invalid JSON response');
@@ -1,7 +1,15 @@
1
1
  import { Request, Response } from 'express';
2
2
  import assert from 'node:assert/strict';
3
3
  import { afterEach, beforeEach, describe, it, mock } from 'node:test';
4
- import { Handler } from '../src/handler.js';
4
+ import {
5
+ Handler,
6
+ HandlersInput,
7
+ GetItemHandler,
8
+ GetCollectionHandler,
9
+ UpdateItemHandler,
10
+ DeleteItemHandler,
11
+ CreateItemHandler,
12
+ } from '../src/handler.js';
5
13
  import { BadRequestError } from '../src/httpErrors.js';
6
14
 
7
15
  describe('Handler', () => {
@@ -42,6 +50,32 @@ describe('Handler', () => {
42
50
  }
43
51
  }
44
52
  });
53
+
54
+ it('validates configuration', () => {
55
+ const getItem = (() => {}) as unknown as GetItemHandler;
56
+ const updateItem = (() => {}) as unknown as UpdateItemHandler;
57
+ const deleteItem = (() => {}) as unknown as DeleteItemHandler;
58
+ const getCollection = (() => {}) as unknown as GetCollectionHandler;
59
+ const createItem = (() => {}) as unknown as CreateItemHandler;
60
+
61
+ const paths: [string, HandlersInput, boolean][] = [
62
+ ['/foo', { getItem }, true],
63
+ ['/foo', { getCollection }, true],
64
+ ['/foo', { getItem, getCollection }, false],
65
+ ['/foo', { updateItem, createItem }, false],
66
+ ['/foo', { deleteItem, createItem }, false],
67
+ ['/foo/:bar', { getItem, getCollection }, true],
68
+ ['/foo/:bar', { getItem, updateItem, deleteItem, createItem, getCollection }, true],
69
+ ];
70
+
71
+ for (const [path, handlers, valid] of paths) {
72
+ if (valid) {
73
+ assert.doesNotThrow(() => new Handler(path, handlers));
74
+ } else {
75
+ assert.throws(() => new Handler(path, handlers));
76
+ }
77
+ }
78
+ });
45
79
  });
46
80
 
47
81
  describe('generate', () => {