@unito/integration-sdk 1.0.3 → 1.0.4

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.
@@ -1,6 +1,6 @@
1
1
  import { Router } from 'express';
2
2
  import * as API from '@unito/integration-api';
3
- import { GetItemContext, GetCollectionContext, CreateItemContext, UpdateItemContext, DeleteItemContext, GetCredentialAccountContext, ParseWebhooksContext, UpdateWebhookSubscriptionsContext, AcknowledgeWebhooksContext } from './resources/context.js';
3
+ import { GetBlobContext, GetItemContext, GetCollectionContext, CreateItemContext, UpdateItemContext, DeleteItemContext, GetCredentialAccountContext, ParseWebhooksContext, UpdateWebhookSubscriptionsContext, AcknowledgeWebhooksContext } from './resources/context.js';
4
4
  /**
5
5
  * Handler called to get an individual item.
6
6
  *
@@ -35,6 +35,13 @@ export type UpdateItemHandler = (context: UpdateItemContext<any, any, any>) => P
35
35
  * @param context {@link DeleteItemContext}
36
36
  */
37
37
  export type DeleteItemHandler = (context: DeleteItemContext<any, any>) => Promise<void>;
38
+ /**
39
+ * Handler called to get a Binary Large Object.
40
+ *
41
+ * @param context {@link BlobItemContext}
42
+ * @returns A {@link ReadableStream} of the Blob.
43
+ */
44
+ export type GetBlobHandler = (context: GetBlobContext<any, any>) => Promise<ReadableStream<Uint8Array>>;
38
45
  /**
39
46
  * Handler called to retrieve the account details associated with the credentials.
40
47
  *
@@ -83,6 +90,9 @@ export type ItemHandlers = {
83
90
  updateItem?: UpdateItemHandler;
84
91
  deleteItem?: DeleteItemHandler;
85
92
  };
93
+ export type BlobHandlers = {
94
+ getBlob: GetBlobHandler;
95
+ };
86
96
  export type CredentialAccountHandlers = {
87
97
  getCredentialAccount: GetCredentialAccountHandler;
88
98
  };
@@ -95,7 +105,7 @@ export type WebhookSubscriptionHandlers = {
95
105
  export type AcknowledgeWebhookHandlers = {
96
106
  acknowledgeWebhooks: AcknowledgeWebhooksHandler;
97
107
  };
98
- export type HandlersInput = ItemHandlers | CredentialAccountHandlers | ParseWebhookHandlers | WebhookSubscriptionHandlers | AcknowledgeWebhookHandlers;
108
+ export type HandlersInput = ItemHandlers | BlobHandlers | CredentialAccountHandlers | ParseWebhookHandlers | WebhookSubscriptionHandlers | AcknowledgeWebhookHandlers;
99
109
  export declare class Handler {
100
110
  private path;
101
111
  private pathWithIdentifier;
@@ -177,6 +177,41 @@ export class Handler {
177
177
  res.status(204).send(null);
178
178
  });
179
179
  }
180
+ if (this.handlers.getBlob) {
181
+ console.debug(` Enabling getBlob at GET ${this.pathWithIdentifier}`);
182
+ const handler = this.handlers.getBlob;
183
+ router.get(this.pathWithIdentifier, async (req, res) => {
184
+ if (!res.locals.credentials) {
185
+ throw new UnauthorizedError();
186
+ }
187
+ const blob = await handler({
188
+ credentials: res.locals.credentials,
189
+ secrets: res.locals.secrets,
190
+ logger: res.locals.logger,
191
+ signal: res.locals.signal,
192
+ params: req.params,
193
+ query: req.query,
194
+ });
195
+ res.writeHead(200, {
196
+ 'Content-Type': 'application/octet-stream',
197
+ });
198
+ const reader = blob.getReader();
199
+ let isDone = false;
200
+ try {
201
+ while (!isDone) {
202
+ const chunk = await reader.read();
203
+ isDone = chunk.done;
204
+ if (chunk.value) {
205
+ res.write(chunk.value);
206
+ }
207
+ }
208
+ }
209
+ finally {
210
+ reader.releaseLock();
211
+ }
212
+ res.end();
213
+ });
214
+ }
180
215
  if (this.handlers.getCredentialAccount) {
181
216
  const handler = this.handlers.getCredentialAccount;
182
217
  console.debug(` Enabling getCredentialAccount at GET ${this.pathWithIdentifier}`);
@@ -692,6 +692,41 @@ class Handler {
692
692
  res.status(204).send(null);
693
693
  });
694
694
  }
695
+ if (this.handlers.getBlob) {
696
+ console.debug(` Enabling getBlob at GET ${this.pathWithIdentifier}`);
697
+ const handler = this.handlers.getBlob;
698
+ router.get(this.pathWithIdentifier, async (req, res) => {
699
+ if (!res.locals.credentials) {
700
+ throw new UnauthorizedError();
701
+ }
702
+ const blob = await handler({
703
+ credentials: res.locals.credentials,
704
+ secrets: res.locals.secrets,
705
+ logger: res.locals.logger,
706
+ signal: res.locals.signal,
707
+ params: req.params,
708
+ query: req.query,
709
+ });
710
+ res.writeHead(200, {
711
+ 'Content-Type': 'application/octet-stream',
712
+ });
713
+ const reader = blob.getReader();
714
+ let isDone = false;
715
+ try {
716
+ while (!isDone) {
717
+ const chunk = await reader.read();
718
+ isDone = chunk.done;
719
+ if (chunk.value) {
720
+ res.write(chunk.value);
721
+ }
722
+ }
723
+ }
724
+ finally {
725
+ reader.releaseLock();
726
+ }
727
+ res.end();
728
+ });
729
+ }
695
730
  if (this.handlers.getCredentialAccount) {
696
731
  const handler = this.handlers.getCredentialAccount;
697
732
  console.debug(` Enabling getCredentialAccount at GET ${this.pathWithIdentifier}`);
@@ -61,6 +61,12 @@ export type Context<P extends Maybe<Params> = Params, Q extends Maybe<Query> = Q
61
61
  * @see {@link Context}
62
62
  */
63
63
  export type GetItemContext<P extends Maybe<Params> = Empty, Q extends Query = Empty> = Context<P, Q>;
64
+ /**
65
+ * Context received by the `GetBlobHandler`, same as `GetItemContext`.
66
+ *
67
+ * @see {@link Context}
68
+ */
69
+ export type GetBlobContext<P extends Maybe<Params> = Empty, Q extends Query = Empty> = Context<P, Q>;
64
70
  /**
65
71
  * Context received by the `GetCollectionHandler`.
66
72
  *
@@ -3,6 +3,11 @@ import { describe, it } from 'node:test';
3
3
  import { Provider } from '../../src/resources/provider.js';
4
4
  import * as HttpErrors from '../../src/httpErrors.js';
5
5
  import Logger from '../../src/resources/logger.js';
6
+ // There is currently an issue with node 20.12 and fetch mocking. A quick fix is to first call fetch so it's getter
7
+ // get properly instantiated, which allow it to be mocked properly.
8
+ // Issue: https://github.com/nodejs/node/issues/52015
9
+ // PR fix: https://github.com/nodejs/node/pull/52275
10
+ globalThis.fetch = fetch;
6
11
  describe('Provider', () => {
7
12
  const provider = new Provider({
8
13
  prepareRequest: requestOptions => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unito/integration-sdk",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Integration SDK",
5
5
  "type": "module",
6
6
  "types": "dist/src/index.d.ts",
package/src/handler.ts CHANGED
@@ -3,6 +3,7 @@ import * as API from '@unito/integration-api';
3
3
  import { InvalidHandler } from './errors.js';
4
4
  import { UnauthorizedError, BadRequestError } from './httpErrors.js';
5
5
  import {
6
+ GetBlobContext,
6
7
  GetItemContext,
7
8
  GetCollectionContext,
8
9
  CreateItemContext,
@@ -53,6 +54,14 @@ export type UpdateItemHandler = (context: UpdateItemContext<any, any, any>) => P
53
54
  */
54
55
  export type DeleteItemHandler = (context: DeleteItemContext<any, any>) => Promise<void>;
55
56
 
57
+ /**
58
+ * Handler called to get a Binary Large Object.
59
+ *
60
+ * @param context {@link BlobItemContext}
61
+ * @returns A {@link ReadableStream} of the Blob.
62
+ */
63
+ export type GetBlobHandler = (context: GetBlobContext<any, any>) => Promise<ReadableStream<Uint8Array>>;
64
+
56
65
  /**
57
66
  * Handler called to retrieve the account details associated with the credentials.
58
67
  *
@@ -114,6 +123,10 @@ export type ItemHandlers = {
114
123
  deleteItem?: DeleteItemHandler;
115
124
  };
116
125
 
126
+ export type BlobHandlers = {
127
+ getBlob: GetBlobHandler;
128
+ };
129
+
117
130
  export type CredentialAccountHandlers = {
118
131
  getCredentialAccount: GetCredentialAccountHandler;
119
132
  };
@@ -132,6 +145,7 @@ export type AcknowledgeWebhookHandlers = {
132
145
 
133
146
  export type HandlersInput =
134
147
  | ItemHandlers
148
+ | BlobHandlers
135
149
  | CredentialAccountHandlers
136
150
  | ParseWebhookHandlers
137
151
  | WebhookSubscriptionHandlers
@@ -139,6 +153,7 @@ export type HandlersInput =
139
153
 
140
154
  type Handlers = Partial<
141
155
  ItemHandlers &
156
+ BlobHandlers &
142
157
  CredentialAccountHandlers &
143
158
  ParseWebhookHandlers &
144
159
  WebhookSubscriptionHandlers &
@@ -380,6 +395,50 @@ export class Handler {
380
395
  });
381
396
  }
382
397
 
398
+ if (this.handlers.getBlob) {
399
+ console.debug(` Enabling getBlob at GET ${this.pathWithIdentifier}`);
400
+
401
+ const handler = this.handlers.getBlob;
402
+
403
+ router.get(this.pathWithIdentifier, async (req, res) => {
404
+ if (!res.locals.credentials) {
405
+ throw new UnauthorizedError();
406
+ }
407
+
408
+ const blob = await handler({
409
+ credentials: res.locals.credentials,
410
+ secrets: res.locals.secrets,
411
+ logger: res.locals.logger,
412
+ signal: res.locals.signal,
413
+ params: req.params,
414
+ query: req.query,
415
+ });
416
+
417
+ res.writeHead(200, {
418
+ 'Content-Type': 'application/octet-stream',
419
+ });
420
+
421
+ const reader = blob.getReader();
422
+
423
+ let isDone = false;
424
+
425
+ try {
426
+ while (!isDone) {
427
+ const chunk = await reader.read();
428
+ isDone = chunk.done;
429
+
430
+ if (chunk.value) {
431
+ res.write(chunk.value);
432
+ }
433
+ }
434
+ } finally {
435
+ reader.releaseLock();
436
+ }
437
+
438
+ res.end();
439
+ });
440
+ }
441
+
383
442
  if (this.handlers.getCredentialAccount) {
384
443
  const handler = this.handlers.getCredentialAccount;
385
444
 
@@ -67,6 +67,13 @@ export type Context<P extends Maybe<Params> = Params, Q extends Maybe<Query> = Q
67
67
  */
68
68
  export type GetItemContext<P extends Maybe<Params> = Empty, Q extends Query = Empty> = Context<P, Q>;
69
69
 
70
+ /**
71
+ * Context received by the `GetBlobHandler`, same as `GetItemContext`.
72
+ *
73
+ * @see {@link Context}
74
+ */
75
+ export type GetBlobContext<P extends Maybe<Params> = Empty, Q extends Query = Empty> = Context<P, Q>;
76
+
70
77
  /**
71
78
  * Context received by the `GetCollectionHandler`.
72
79
  *
@@ -5,6 +5,12 @@ import { Provider } from '../../src/resources/provider.js';
5
5
  import * as HttpErrors from '../../src/httpErrors.js';
6
6
  import Logger from '../../src/resources/logger.js';
7
7
 
8
+ // There is currently an issue with node 20.12 and fetch mocking. A quick fix is to first call fetch so it's getter
9
+ // get properly instantiated, which allow it to be mocked properly.
10
+ // Issue: https://github.com/nodejs/node/issues/52015
11
+ // PR fix: https://github.com/nodejs/node/pull/52275
12
+ globalThis.fetch = fetch;
13
+
8
14
  describe('Provider', () => {
9
15
  const provider = new Provider({
10
16
  prepareRequest: requestOptions => {