@unito/integration-sdk 1.8.5 → 2.0.1

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.
@@ -92,6 +92,7 @@ export class Handler {
92
92
  const collection = await handler({
93
93
  credentials: res.locals.credentials,
94
94
  secrets: res.locals.secrets,
95
+ search: res.locals.search,
95
96
  selects: res.locals.selects,
96
97
  filters: res.locals.filters,
97
98
  logger: res.locals.logger,
@@ -523,6 +523,7 @@ class Handler {
523
523
  const collection = await handler({
524
524
  credentials: res.locals.credentials,
525
525
  secrets: res.locals.secrets,
526
+ search: res.locals.search,
526
527
  selects: res.locals.selects,
527
528
  filters: res.locals.filters,
528
529
  logger: res.locals.logger,
@@ -828,7 +829,7 @@ function onError(err, _req, res, next) {
828
829
  // a subset of the symbol of another operator.
829
830
  //
830
831
  // For example, the symbol "=" (EQUAL) is a subset of the symbol "!=" (NOT_EQUAL).
831
- const ORDERED_OPERATORS = Object.values(integrationApi.OperatorType).sort((o1, o2) => o2.length - o1.length);
832
+ const ORDERED_OPERATORS = Object.values(integrationApi.OperatorTypes).sort((o1, o2) => o2.length - o1.length);
832
833
  function extractFilters(req, res, next) {
833
834
  const rawFilters = req.query.filter;
834
835
  res.locals.filters = [];
@@ -929,6 +930,17 @@ function extractSecrets(req, res, next) {
929
930
  next();
930
931
  }
931
932
 
933
+ function extractSearch(req, res, next) {
934
+ const rawSearch = req.query.search;
935
+ if (typeof rawSearch === 'string') {
936
+ res.locals.search = rawSearch;
937
+ }
938
+ else {
939
+ res.locals.search = null;
940
+ }
941
+ next();
942
+ }
943
+
932
944
  function extractSelects(req, res, next) {
933
945
  const rawSelect = req.query.select;
934
946
  if (typeof rawSelect === 'string') {
@@ -1081,6 +1093,7 @@ class Integration {
1081
1093
  app.use(extractCredentials);
1082
1094
  app.use(extractSecrets);
1083
1095
  app.use(extractFilters);
1096
+ app.use(extractSearch);
1084
1097
  app.use(extractSelects);
1085
1098
  app.use(extractRelations);
1086
1099
  app.use(extractOperationDeadline);
@@ -10,6 +10,7 @@ import notFoundMiddleware from './middlewares/notFound.js';
10
10
  import loggerMiddleware from './middlewares/logger.js';
11
11
  import startMiddleware from './middlewares/start.js';
12
12
  import secretsMiddleware from './middlewares/secrets.js';
13
+ import searchMiddleware from './middlewares/search.js';
13
14
  import selectsMiddleware from './middlewares/selects.js';
14
15
  import relationsMiddleware from './middlewares/relations.js';
15
16
  import signalMiddleware from './middlewares/signal.js';
@@ -131,6 +132,7 @@ export default class Integration {
131
132
  app.use(credentialsMiddleware);
132
133
  app.use(secretsMiddleware);
133
134
  app.use(filtersMiddleware);
135
+ app.use(searchMiddleware);
134
136
  app.use(selectsMiddleware);
135
137
  app.use(relationsMiddleware);
136
138
  app.use(signalMiddleware);
@@ -1,10 +1,10 @@
1
- import { OperatorType } from '@unito/integration-api';
1
+ import { OperatorTypes } from '@unito/integration-api';
2
2
  // The operators are ordered by their symbol length, in descending order.
3
3
  // This is necessary because the symbol of an operator can be
4
4
  // a subset of the symbol of another operator.
5
5
  //
6
6
  // For example, the symbol "=" (EQUAL) is a subset of the symbol "!=" (NOT_EQUAL).
7
- const ORDERED_OPERATORS = Object.values(OperatorType).sort((o1, o2) => o2.length - o1.length);
7
+ const ORDERED_OPERATORS = Object.values(OperatorTypes).sort((o1, o2) => o2.length - o1.length);
8
8
  function extractFilters(req, res, next) {
9
9
  const rawFilters = req.query.filter;
10
10
  res.locals.filters = [];
@@ -0,0 +1,10 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ declare global {
3
+ namespace Express {
4
+ interface Locals {
5
+ search: string | null;
6
+ }
7
+ }
8
+ }
9
+ declare function extractSearch(req: Request, res: Response, next: NextFunction): void;
10
+ export default extractSearch;
@@ -0,0 +1,11 @@
1
+ function extractSearch(req, res, next) {
2
+ const rawSearch = req.query.search;
3
+ if (typeof rawSearch === 'string') {
4
+ res.locals.search = rawSearch;
5
+ }
6
+ else {
7
+ res.locals.search = null;
8
+ }
9
+ next();
10
+ }
11
+ export default extractSearch;
@@ -90,6 +90,16 @@ export type GetCollectionContext<P extends Maybe<Params> = Empty, Q extends Quer
90
90
  * @see {@link Filter}
91
91
  */
92
92
  filters: Filter[];
93
+ /**
94
+ * Parsed search query param yielding a free-text search query.
95
+ *
96
+ * Given a search query param:
97
+ * `search=John`
98
+ *
99
+ * Context.search will be:
100
+ * 'John'
101
+ */
102
+ search: string | null;
93
103
  /**
94
104
  * Parsed select query param yielding a list of fields to select.
95
105
  *
@@ -1,4 +1,4 @@
1
- import { FieldValueType, OperatorType, Semantic } from '@unito/integration-api';
1
+ import { FieldValueTypes, OperatorTypes, Semantics } from '@unito/integration-api';
2
2
  import assert from 'node:assert/strict';
3
3
  import { describe, it } from 'node:test';
4
4
  import { getApplicableFilters } from '../src/helpers.js';
@@ -7,48 +7,48 @@ describe('Helpers', () => {
7
7
  it('returns only filters for defined fields', () => {
8
8
  const actual = getApplicableFilters({
9
9
  filters: [
10
- { field: 'status', operator: OperatorType.EQUAL, values: ['active', 'pending'] },
11
- { field: 'email', operator: OperatorType.IS_NOT_NULL, values: [] },
10
+ { field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] },
11
+ { field: 'email', operator: OperatorTypes.IS_NOT_NULL, values: [] },
12
12
  ],
13
13
  }, [
14
14
  {
15
15
  name: 'status',
16
16
  label: 'Status',
17
- type: FieldValueType.STRING,
17
+ type: FieldValueTypes.STRING,
18
18
  },
19
19
  ]);
20
- const expected = [{ field: 'status', operator: OperatorType.EQUAL, values: ['active', 'pending'] }];
20
+ const expected = [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] }];
21
21
  assert.deepEqual(actual, expected);
22
22
  });
23
23
  it('translates semantics into field names', () => {
24
24
  const actual = getApplicableFilters({
25
25
  filters: [
26
- { field: 'semantic:displayName', operator: OperatorType.START_WITH, values: ['Bob'] },
27
- { field: 'semantic:createdAt', operator: OperatorType.EQUAL, values: ['2021-01-01'] },
26
+ { field: 'semantic:displayName', operator: OperatorTypes.START_WITH, values: ['Bob'] },
27
+ { field: 'semantic:createdAt', operator: OperatorTypes.EQUAL, values: ['2021-01-01'] },
28
28
  ],
29
29
  }, [
30
30
  {
31
31
  name: 'name',
32
32
  label: 'Name',
33
- type: FieldValueType.STRING,
34
- semantic: Semantic.DISPLAY_NAME,
33
+ type: FieldValueTypes.STRING,
34
+ semantic: Semantics.DISPLAY_NAME,
35
35
  },
36
36
  ]);
37
- const expected = [{ field: 'name', operator: OperatorType.START_WITH, values: ['Bob'] }];
37
+ const expected = [{ field: 'name', operator: OperatorTypes.START_WITH, values: ['Bob'] }];
38
38
  assert.deepEqual(actual, expected);
39
39
  });
40
40
  it('gracefully handle garbage', () => {
41
41
  const actual = getApplicableFilters({
42
42
  filters: [
43
- { field: '...', operator: OperatorType.EQUAL, values: [] },
44
- { field: ':', operator: OperatorType.EQUAL, values: [] },
45
- { field: '', operator: OperatorType.EQUAL, values: [] },
43
+ { field: '...', operator: OperatorTypes.EQUAL, values: [] },
44
+ { field: ':', operator: OperatorTypes.EQUAL, values: [] },
45
+ { field: '', operator: OperatorTypes.EQUAL, values: [] },
46
46
  ],
47
47
  }, [
48
48
  {
49
49
  name: 'status',
50
50
  label: 'Status',
51
- type: FieldValueType.STRING,
51
+ type: FieldValueTypes.STRING,
52
52
  },
53
53
  ]);
54
54
  assert.deepEqual(actual, []);
@@ -1,10 +1,10 @@
1
- import { OperatorType } from '@unito/integration-api';
1
+ import { OperatorTypes } from '@unito/integration-api';
2
2
  import assert from 'node:assert/strict';
3
3
  import { describe, it } from 'node:test';
4
4
  import extractFilters from '../../src/middlewares/filters.js';
5
5
  describe('filters middleware', () => {
6
6
  it('properly parse operators', () => {
7
- Object.values(OperatorType).forEach(operator => {
7
+ Object.values(OperatorTypes).forEach(operator => {
8
8
  const request = {
9
9
  query: { filter: `aKey${operator}value` },
10
10
  };
@@ -20,7 +20,7 @@ describe('filters middleware', () => {
20
20
  const response = { locals: {} };
21
21
  extractFilters(request, response, () => { });
22
22
  assert.deepEqual(response.locals, {
23
- filters: [{ field: 'aKey', operator: OperatorType.IS_NULL, values: [] }],
23
+ filters: [{ field: 'aKey', operator: OperatorTypes.IS_NULL, values: [] }],
24
24
  });
25
25
  });
26
26
  it('decodes URI components', () => {
@@ -28,7 +28,7 @@ describe('filters middleware', () => {
28
28
  const response = { locals: {} };
29
29
  extractFilters(request, response, () => { });
30
30
  assert.deepEqual(response.locals, {
31
- filters: [{ field: 'status', operator: OperatorType.EQUAL, values: ['foo,bar!!,?baz=!>qux'] }],
31
+ filters: [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['foo,bar!!,?baz=!>qux'] }],
32
32
  });
33
33
  });
34
34
  it('no data', () => {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import assert from 'node:assert/strict';
2
+ import { describe, it } from 'node:test';
3
+ import extractSearch from '../../src/middlewares/search.js';
4
+ describe('search middleware', () => {
5
+ it('data', () => {
6
+ const request = { query: { search: 'foo bar spam baz' } };
7
+ const response = { locals: {} };
8
+ extractSearch(request, response, () => { });
9
+ assert.deepEqual(response.locals, {
10
+ search: 'foo bar spam baz',
11
+ });
12
+ });
13
+ it('no data', () => {
14
+ const request = { query: {} };
15
+ const response = { locals: {} };
16
+ extractSearch(request, response, () => { });
17
+ assert.deepEqual(response.locals, {
18
+ search: null,
19
+ });
20
+ });
21
+ });
package/package.json CHANGED
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "name": "@unito/integration-sdk",
3
- "version": "1.8.5",
3
+ "version": "2.0.1",
4
4
  "description": "Integration SDK",
5
5
  "type": "module",
6
6
  "types": "dist/src/index.d.ts",
7
7
  "exports": {
8
8
  "./package.json": "./package.json",
9
9
  ".": {
10
- "import": "./dist/src/index.js",
11
- "require": "./dist/src/index.cjs"
10
+ "import": {
11
+ "types": "./dist/src/index.d.ts",
12
+ "default": "./dist/src/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/src/index.d.ts",
16
+ "default": "./dist/src/index.cjs"
17
+ }
12
18
  }
13
19
  },
14
20
  "license": "LicenseRef-LICENSE",
@@ -46,7 +52,7 @@
46
52
  "typescript": "5.x"
47
53
  },
48
54
  "dependencies": {
49
- "@unito/integration-api": "1.x",
55
+ "@unito/integration-api": "3.x",
50
56
  "busboy": "^1.6.0",
51
57
  "cachette": "2.x",
52
58
  "express": "^5.x",
package/src/handler.ts CHANGED
@@ -297,6 +297,7 @@ export class Handler {
297
297
  const collection = await handler({
298
298
  credentials: res.locals.credentials,
299
299
  secrets: res.locals.secrets,
300
+ search: res.locals.search,
300
301
  selects: res.locals.selects,
301
302
  filters: res.locals.filters,
302
303
  logger: res.locals.logger,
@@ -12,6 +12,7 @@ import notFoundMiddleware from './middlewares/notFound.js';
12
12
  import loggerMiddleware from './middlewares/logger.js';
13
13
  import startMiddleware from './middlewares/start.js';
14
14
  import secretsMiddleware from './middlewares/secrets.js';
15
+ import searchMiddleware from './middlewares/search.js';
15
16
  import selectsMiddleware from './middlewares/selects.js';
16
17
  import relationsMiddleware from './middlewares/relations.js';
17
18
  import signalMiddleware from './middlewares/signal.js';
@@ -149,6 +150,7 @@ export default class Integration {
149
150
  app.use(credentialsMiddleware);
150
151
  app.use(secretsMiddleware);
151
152
  app.use(filtersMiddleware);
153
+ app.use(searchMiddleware);
152
154
  app.use(selectsMiddleware);
153
155
  app.use(relationsMiddleware);
154
156
  app.use(signalMiddleware);
@@ -1,4 +1,4 @@
1
- import { OperatorType } from '@unito/integration-api';
1
+ import { OperatorType, OperatorTypes } from '@unito/integration-api';
2
2
  import { Request, Response, NextFunction } from 'express';
3
3
 
4
4
  declare global {
@@ -32,7 +32,7 @@ export type Filter = {
32
32
  // a subset of the symbol of another operator.
33
33
  //
34
34
  // For example, the symbol "=" (EQUAL) is a subset of the symbol "!=" (NOT_EQUAL).
35
- const ORDERED_OPERATORS = Object.values(OperatorType).sort((o1, o2) => o2.length - o1.length);
35
+ const ORDERED_OPERATORS = Object.values(OperatorTypes).sort((o1, o2) => o2.length - o1.length);
36
36
 
37
37
  function extractFilters(req: Request, res: Response, next: NextFunction) {
38
38
  const rawFilters = req.query.filter;
@@ -0,0 +1,31 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ declare global {
4
+ // eslint-disable-next-line @typescript-eslint/no-namespace
5
+ namespace Express {
6
+ interface Locals {
7
+ // When the query params contains...
8
+ //
9
+ // search=foo
10
+ //
11
+ // ... it becomes available as follow in handlers:
12
+ //
13
+ // 'foo'
14
+ search: string | null;
15
+ }
16
+ }
17
+ }
18
+
19
+ function extractSearch(req: Request, res: Response, next: NextFunction) {
20
+ const rawSearch = req.query.search;
21
+
22
+ if (typeof rawSearch === 'string') {
23
+ res.locals.search = rawSearch;
24
+ } else {
25
+ res.locals.search = null;
26
+ }
27
+
28
+ next();
29
+ }
30
+
31
+ export default extractSearch;
@@ -96,6 +96,16 @@ export type GetCollectionContext<P extends Maybe<Params> = Empty, Q extends Quer
96
96
  * @see {@link Filter}
97
97
  */
98
98
  filters: Filter[];
99
+ /**
100
+ * Parsed search query param yielding a free-text search query.
101
+ *
102
+ * Given a search query param:
103
+ * `search=John`
104
+ *
105
+ * Context.search will be:
106
+ * 'John'
107
+ */
108
+ search: string | null;
99
109
  /**
100
110
  * Parsed select query param yielding a list of fields to select.
101
111
  *
@@ -1,4 +1,4 @@
1
- import { FieldValueType, OperatorType, Semantic } from '@unito/integration-api';
1
+ import { FieldValueTypes, OperatorTypes, Semantics } from '@unito/integration-api';
2
2
 
3
3
  import assert from 'node:assert/strict';
4
4
  import { describe, it } from 'node:test';
@@ -10,20 +10,20 @@ describe('Helpers', () => {
10
10
  const actual = getApplicableFilters(
11
11
  {
12
12
  filters: [
13
- { field: 'status', operator: OperatorType.EQUAL, values: ['active', 'pending'] },
14
- { field: 'email', operator: OperatorType.IS_NOT_NULL, values: [] },
13
+ { field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] },
14
+ { field: 'email', operator: OperatorTypes.IS_NOT_NULL, values: [] },
15
15
  ],
16
16
  },
17
17
  [
18
18
  {
19
19
  name: 'status',
20
20
  label: 'Status',
21
- type: FieldValueType.STRING,
21
+ type: FieldValueTypes.STRING,
22
22
  },
23
23
  ],
24
24
  );
25
25
 
26
- const expected = [{ field: 'status', operator: OperatorType.EQUAL, values: ['active', 'pending'] }];
26
+ const expected = [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] }];
27
27
 
28
28
  assert.deepEqual(actual, expected);
29
29
  });
@@ -32,21 +32,21 @@ describe('Helpers', () => {
32
32
  const actual = getApplicableFilters(
33
33
  {
34
34
  filters: [
35
- { field: 'semantic:displayName', operator: OperatorType.START_WITH, values: ['Bob'] },
36
- { field: 'semantic:createdAt', operator: OperatorType.EQUAL, values: ['2021-01-01'] },
35
+ { field: 'semantic:displayName', operator: OperatorTypes.START_WITH, values: ['Bob'] },
36
+ { field: 'semantic:createdAt', operator: OperatorTypes.EQUAL, values: ['2021-01-01'] },
37
37
  ],
38
38
  },
39
39
  [
40
40
  {
41
41
  name: 'name',
42
42
  label: 'Name',
43
- type: FieldValueType.STRING,
44
- semantic: Semantic.DISPLAY_NAME,
43
+ type: FieldValueTypes.STRING,
44
+ semantic: Semantics.DISPLAY_NAME,
45
45
  },
46
46
  ],
47
47
  );
48
48
 
49
- const expected = [{ field: 'name', operator: OperatorType.START_WITH, values: ['Bob'] }];
49
+ const expected = [{ field: 'name', operator: OperatorTypes.START_WITH, values: ['Bob'] }];
50
50
 
51
51
  assert.deepEqual(actual, expected);
52
52
  });
@@ -55,16 +55,16 @@ describe('Helpers', () => {
55
55
  const actual = getApplicableFilters(
56
56
  {
57
57
  filters: [
58
- { field: '...', operator: OperatorType.EQUAL, values: [] },
59
- { field: ':', operator: OperatorType.EQUAL, values: [] },
60
- { field: '', operator: OperatorType.EQUAL, values: [] },
58
+ { field: '...', operator: OperatorTypes.EQUAL, values: [] },
59
+ { field: ':', operator: OperatorTypes.EQUAL, values: [] },
60
+ { field: '', operator: OperatorTypes.EQUAL, values: [] },
61
61
  ],
62
62
  },
63
63
  [
64
64
  {
65
65
  name: 'status',
66
66
  label: 'Status',
67
- type: FieldValueType.STRING,
67
+ type: FieldValueTypes.STRING,
68
68
  },
69
69
  ],
70
70
  );
@@ -1,4 +1,4 @@
1
- import { OperatorType } from '@unito/integration-api';
1
+ import { OperatorTypes } from '@unito/integration-api';
2
2
  import express from 'express';
3
3
  import assert from 'node:assert/strict';
4
4
  import { describe, it } from 'node:test';
@@ -6,7 +6,7 @@ import extractFilters from '../../src/middlewares/filters.js';
6
6
 
7
7
  describe('filters middleware', () => {
8
8
  it('properly parse operators', () => {
9
- Object.values(OperatorType).forEach(operator => {
9
+ Object.values(OperatorTypes).forEach(operator => {
10
10
  const request = {
11
11
  query: { filter: `aKey${operator}value` },
12
12
  } as express.Request<
@@ -41,7 +41,7 @@ describe('filters middleware', () => {
41
41
  extractFilters(request, response, () => {});
42
42
 
43
43
  assert.deepEqual(response.locals, {
44
- filters: [{ field: 'aKey', operator: OperatorType.IS_NULL, values: [] }],
44
+ filters: [{ field: 'aKey', operator: OperatorTypes.IS_NULL, values: [] }],
45
45
  });
46
46
  });
47
47
 
@@ -58,7 +58,7 @@ describe('filters middleware', () => {
58
58
  extractFilters(request, response, () => {});
59
59
 
60
60
  assert.deepEqual(response.locals, {
61
- filters: [{ field: 'status', operator: OperatorType.EQUAL, values: ['foo,bar!!,?baz=!>qux'] }],
61
+ filters: [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['foo,bar!!,?baz=!>qux'] }],
62
62
  });
63
63
  });
64
64
 
@@ -0,0 +1,34 @@
1
+ import express from 'express';
2
+ import assert from 'node:assert/strict';
3
+ import { describe, it } from 'node:test';
4
+ import extractSearch from '../../src/middlewares/search.js';
5
+
6
+ describe('search middleware', () => {
7
+ it('data', () => {
8
+ const request = { query: { search: 'foo bar spam baz' } } as express.Request<
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ any,
11
+ object,
12
+ object,
13
+ { search: string }
14
+ >;
15
+ const response = { locals: {} } as express.Response;
16
+
17
+ extractSearch(request, response, () => {});
18
+
19
+ assert.deepEqual(response.locals, {
20
+ search: 'foo bar spam baz',
21
+ });
22
+ });
23
+
24
+ it('no data', () => {
25
+ const request = { query: {} } as express.Request;
26
+ const response = { locals: {} } as express.Response;
27
+
28
+ extractSearch(request, response, () => {});
29
+
30
+ assert.deepEqual(response.locals, {
31
+ search: null,
32
+ });
33
+ });
34
+ });