@restorecommerce/facade 1.3.2 → 1.3.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,26 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.3.4](https://github.com/restorecommerce/libs/compare/@restorecommerce/facade@1.3.3...@restorecommerce/facade@1.3.4) (2023-06-16)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **facade:** convert stream to async iterable so that file is read in chunksand passed ([d73ebf6](https://github.com/restorecommerce/libs/commit/d73ebf6d1d8b5c8d32b30968074aa4e89ca08c0d))
12
+ * **facade:** service client and async iterable for file upload request ([a0759b8](https://github.com/restorecommerce/libs/commit/a0759b86a5efd4dbad0e68af67a7d42889f228ef))
13
+
14
+
15
+
16
+
17
+
18
+ ## [1.3.3](https://github.com/restorecommerce/libs/compare/@restorecommerce/facade@1.3.2...@restorecommerce/facade@1.3.3) (2023-06-14)
19
+
20
+ **Note:** Version bump only for package @restorecommerce/facade
21
+
22
+
23
+
24
+
25
+
6
26
  ## [1.3.2](https://github.com/restorecommerce/libs/compare/@restorecommerce/facade@1.3.1...@restorecommerce/facade@1.3.2) (2023-06-14)
7
27
 
8
28
 
@@ -7,18 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- var __asyncValues = (this && this.__asyncValues) || function (o) {
11
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
12
- var m = o[Symbol.asyncIterator], i;
13
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
14
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
15
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
16
- };
17
10
  import { GraphQLList, GraphQLNonNull, GraphQLObjectType, } from 'graphql';
18
11
  import { GraphQLEnumType, GraphQLInputObjectType, GraphQLScalarType, } from 'graphql/type/definition.js';
19
12
  export const Mutate = ['Create', 'Update', 'Upsert'];
20
13
  export const preprocessGQLInput = (data, model) => __awaiter(void 0, void 0, void 0, function* () {
21
- var _a, e_1, _b, _c;
22
14
  if (model instanceof GraphQLEnumType) {
23
15
  return data;
24
16
  }
@@ -65,23 +57,7 @@ export const preprocessGQLInput = (data, model) => __awaiter(void 0, void 0, voi
65
57
  let fileData = yield data;
66
58
  const upload = yield fileData.promise;
67
59
  const stream = upload.createReadStream();
68
- const chunks = [];
69
- try {
70
- for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
71
- _c = stream_1_1.value;
72
- _d = false;
73
- let chunk = _c;
74
- chunks.push(chunk);
75
- }
76
- }
77
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
78
- finally {
79
- try {
80
- if (!_d && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
81
- }
82
- finally { if (e_1) throw e_1.error; }
83
- }
84
- return Buffer.concat(chunks);
60
+ return stream;
85
61
  }
86
62
  }
87
63
  return data;
@@ -7,6 +7,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
11
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
12
+ var m = o[Symbol.asyncIterator], i;
13
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
14
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
15
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
16
+ };
17
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
18
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
19
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
20
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
21
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
22
+ function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
23
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
24
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
25
+ function fulfill(value) { resume("next", value); }
26
+ function reject(value) { resume("throw", value); }
27
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
28
+ };
10
29
  import { authSubjectType } from './types.js';
11
30
  import flat from 'array.prototype.flat';
12
31
  import { getTyping } from './registry.js';
@@ -17,7 +36,29 @@ import { Filter_Operation, Filter_ValueType, FilterOp_Operator } from '@restorec
17
36
  import * as stream from 'node:stream';
18
37
  import _ from 'lodash';
19
38
  import { Events } from '@restorecommerce/kafka-client';
39
+ import S2A from './stream-to-async-iterator.js';
20
40
  const inputMethodType = new Map();
41
+ const streamToAsyncIterable = function (request, readableStreamKey) {
42
+ return __asyncGenerator(this, arguments, function* () {
43
+ var _a, e_1, _b, _c;
44
+ const readStream = _.clone(request[readableStreamKey]);
45
+ try {
46
+ for (var _d = true, _e = __asyncValues(new S2A(readStream)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a; _d = true) {
47
+ _c = _f.value;
48
+ _d = false;
49
+ const chunk = _c;
50
+ yield yield __await(Object.assign({}, request, { [readableStreamKey]: chunk }));
51
+ }
52
+ }
53
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
54
+ finally {
55
+ try {
56
+ if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
57
+ }
58
+ finally { if (e_1) throw e_1.error; }
59
+ }
60
+ });
61
+ };
21
62
  export const getGQLResolverFunctions = (service, key, serviceKey, cfg) => {
22
63
  if (!service.method) {
23
64
  return {};
@@ -60,7 +101,7 @@ export const getGQLResolverFunctions = (service, key, serviceKey, cfg) => {
60
101
  obj[methodName] = (args, context) => __awaiter(void 0, void 0, void 0, function* () {
61
102
  var _e, _f;
62
103
  const client = context[key].client;
63
- const service = client[serviceKey];
104
+ const service = client[key];
64
105
  try {
65
106
  const converted = yield preprocessGQLInput(args.input, typing.input);
66
107
  const scope = (_e = args === null || args === void 0 ? void 0 : args.input) === null || _e === void 0 ? void 0 : _e.scope;
@@ -94,6 +135,16 @@ export const getGQLResolverFunctions = (service, key, serviceKey, cfg) => {
94
135
  }
95
136
  }
96
137
  const methodFunc = service[camelCase(realMethod)] || service[realMethod];
138
+ if (method.clientStreaming) {
139
+ const readableStreamKey = Object.keys(req).filter((key) => {
140
+ if (req[key] instanceof stream.Stream.Readable) {
141
+ return key;
142
+ }
143
+ });
144
+ if (readableStreamKey.length > 0) {
145
+ req = streamToAsyncIterable(req, readableStreamKey[0]);
146
+ }
147
+ }
97
148
  const rawResult = yield methodFunc(req);
98
149
  const result = postProcessGQLValue(rawResult, outputTyping.output);
99
150
  const grpcClientConfig = cfg.client;
@@ -0,0 +1,50 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Readable } from "stream";
3
+ export type StreamToAsyncIteratorOptions = {
4
+ /** The size of each read from the stream for each iteration */
5
+ size?: number;
6
+ };
7
+ /**
8
+ * Wraps a stream into an object that can be used as an async iterator.
9
+ *
10
+ * This will keep a stream in a paused state, and will only read from the stream on each
11
+ * iteration. A size can be supplied to set an explicit call to `stream.read([size])` in
12
+ * the options for each iteration.
13
+ */
14
+ export default class StreamToAsyncIterator<T = unknown> implements AsyncIterableIterator<T> {
15
+ /** The underlying readable stream */
16
+ private _stream;
17
+ /** Contains stream's error when stream has error'ed out */
18
+ private _error;
19
+ /** The current state of the iterator (not readable, readable, ended, errored) */
20
+ private _state;
21
+ private _size;
22
+ /** The rejections of promises to call when stream errors out */
23
+ private _rejections;
24
+ get closed(): boolean;
25
+ constructor(stream: Readable, { size }?: StreamToAsyncIteratorOptions);
26
+ [Symbol.asyncIterator](): this;
27
+ /**
28
+ * Returns the next iteration of data. Rejects if the stream errored out.
29
+ */
30
+ next(): Promise<IteratorResult<T, void>>;
31
+ /**
32
+ * Waits until the stream is readable. Rejects if the stream errored out.
33
+ * @returns Promise when stream is readable
34
+ */
35
+ private _untilReadable;
36
+ /**
37
+ * Waits until the stream is ended. Rejects if the stream errored out.
38
+ * @returns Promise when stream is finished
39
+ */
40
+ private _untilEnd;
41
+ return(): Promise<IteratorResult<T, void>>;
42
+ throw(err?: Error): Promise<IteratorResult<T, void>>;
43
+ /**
44
+ * Destroy the stream
45
+ * @param err An optional error to pass to the stream for an error event
46
+ */
47
+ close(err?: Error): void;
48
+ private _handleStreamError;
49
+ private _handleStreamEnd;
50
+ }
@@ -0,0 +1,190 @@
1
+ // The MIT License (MIT)
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ const NOT_READABLE = Symbol("not readable");
12
+ const READABLE = Symbol("readable");
13
+ const ENDED = Symbol("ended");
14
+ const ERRORED = Symbol("errored");
15
+ const STATES = {
16
+ notReadable: NOT_READABLE,
17
+ readable: READABLE,
18
+ ended: ENDED,
19
+ errored: ERRORED,
20
+ };
21
+ /**
22
+ * Wraps a stream into an object that can be used as an async iterator.
23
+ *
24
+ * This will keep a stream in a paused state, and will only read from the stream on each
25
+ * iteration. A size can be supplied to set an explicit call to `stream.read([size])` in
26
+ * the options for each iteration.
27
+ */
28
+ export default class StreamToAsyncIterator {
29
+ get closed() {
30
+ return this._state === STATES.ended;
31
+ }
32
+ constructor(stream, { size } = {}) {
33
+ /** The current state of the iterator (not readable, readable, ended, errored) */
34
+ this._state = STATES.notReadable;
35
+ /** The rejections of promises to call when stream errors out */
36
+ this._rejections = new Set();
37
+ this._stream = stream;
38
+ this._size = size;
39
+ const bindMethods = ["_handleStreamEnd", "_handleStreamError"];
40
+ for (const method of bindMethods) {
41
+ Object.defineProperty(this, method, {
42
+ configurable: true,
43
+ writable: true,
44
+ value: this[method].bind(this),
45
+ });
46
+ }
47
+ // eslint-disable-next-line @typescript-eslint/unbound-method
48
+ stream.once("error", this._handleStreamError);
49
+ // eslint-disable-next-line @typescript-eslint/unbound-method
50
+ stream.once("end", this._handleStreamEnd);
51
+ }
52
+ [Symbol.asyncIterator]() {
53
+ return this;
54
+ }
55
+ /**
56
+ * Returns the next iteration of data. Rejects if the stream errored out.
57
+ */
58
+ next() {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ switch (this._state) {
61
+ case STATES.notReadable: {
62
+ let untilReadable;
63
+ let untilEnd;
64
+ try {
65
+ untilReadable = this._untilReadable();
66
+ untilEnd = this._untilEnd();
67
+ yield Promise.race([
68
+ untilReadable.promise,
69
+ untilEnd.promise,
70
+ ]);
71
+ }
72
+ finally {
73
+ // need to clean up any hanging event listeners
74
+ if (untilReadable != null) {
75
+ untilReadable.close();
76
+ }
77
+ if (untilEnd != null) {
78
+ untilEnd.close();
79
+ }
80
+ }
81
+ return this.next();
82
+ }
83
+ case STATES.ended: {
84
+ this.close();
85
+ return { done: true, value: undefined };
86
+ }
87
+ case STATES.errored: {
88
+ this.close();
89
+ throw this._error;
90
+ }
91
+ case STATES.readable: {
92
+ // stream.read returns null if not readable or when stream has ended
93
+ // todo: Could add a way to ensure data-type/shape of reads to make this type safe
94
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
95
+ const data = this._size
96
+ ? this._stream.read(this._size)
97
+ : this._stream.read();
98
+ if (data !== null) {
99
+ return { done: false, value: data };
100
+ }
101
+ else {
102
+ //we're no longer readable, need to find out what state we're in
103
+ this._state = STATES.notReadable;
104
+ // need to let event loop run to fill stream buffer
105
+ yield new Promise(setImmediate);
106
+ return this.next();
107
+ }
108
+ }
109
+ }
110
+ });
111
+ }
112
+ /**
113
+ * Waits until the stream is readable. Rejects if the stream errored out.
114
+ * @returns Promise when stream is readable
115
+ */
116
+ _untilReadable() {
117
+ //let is used here instead of const because the exact reference is
118
+ //required to remove it, this is why it is not a curried function that
119
+ //accepts resolve & reject as parameters.
120
+ let handleReadable = undefined;
121
+ const promise = new Promise((resolve, reject) => {
122
+ handleReadable = () => {
123
+ this._state = STATES.readable;
124
+ this._rejections.delete(reject);
125
+ resolve();
126
+ };
127
+ this._stream.once("readable", handleReadable);
128
+ this._rejections.add(reject);
129
+ });
130
+ const cleanup = () => {
131
+ if (handleReadable != null) {
132
+ this._stream.removeListener("readable", handleReadable);
133
+ }
134
+ };
135
+ return { close: cleanup, promise };
136
+ }
137
+ /**
138
+ * Waits until the stream is ended. Rejects if the stream errored out.
139
+ * @returns Promise when stream is finished
140
+ */
141
+ _untilEnd() {
142
+ let handleEnd = undefined;
143
+ const promise = new Promise((resolve, reject) => {
144
+ handleEnd = () => {
145
+ this._state = STATES.ended;
146
+ this._rejections.delete(reject);
147
+ resolve();
148
+ };
149
+ this._stream.once("end", handleEnd);
150
+ this._rejections.add(reject);
151
+ });
152
+ const cleanup = () => {
153
+ if (handleEnd != null) {
154
+ this._stream.removeListener("end", handleEnd);
155
+ }
156
+ };
157
+ return { close: cleanup, promise };
158
+ }
159
+ return() {
160
+ this._state = STATES.ended;
161
+ return this.next();
162
+ }
163
+ throw(err) {
164
+ this._error = err;
165
+ this._state = STATES.errored;
166
+ return this.next();
167
+ }
168
+ /**
169
+ * Destroy the stream
170
+ * @param err An optional error to pass to the stream for an error event
171
+ */
172
+ close(err) {
173
+ // eslint-disable-next-line @typescript-eslint/unbound-method
174
+ this._stream.removeListener("end", this._handleStreamEnd);
175
+ // eslint-disable-next-line @typescript-eslint/unbound-method
176
+ this._stream.removeListener("error", this._handleStreamError);
177
+ this._state = STATES.ended;
178
+ this._stream.destroy(err);
179
+ }
180
+ _handleStreamError(err) {
181
+ this._error = err;
182
+ this._state = STATES.errored;
183
+ for (const reject of this._rejections) {
184
+ reject(err);
185
+ }
186
+ }
187
+ _handleStreamEnd() {
188
+ this._state = STATES.ended;
189
+ }
190
+ }
package/dist/index.js CHANGED
@@ -265,7 +265,7 @@ export class RestoreCommerceFacade {
265
265
  },
266
266
  });
267
267
  yield gqlServer.start();
268
- // TODO set maxFile size and maximum files via config
268
+ // TODO set maxFile size and maximum files via Facade config of `createFacade`
269
269
  this.koa.use(graphqlUploadKoa({
270
270
  maxFileSize: 10000000,
271
271
  maxFiles: 20,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@restorecommerce/facade",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Facade for Restorecommerce microservices",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -18,7 +18,7 @@
18
18
  "type": "module",
19
19
  "dependencies": {
20
20
  "@apollo/federation": "^0.38.1",
21
- "@apollo/gateway": "^2.4.7",
21
+ "@apollo/gateway": "<2.4.0",
22
22
  "@apollo/server": "^4.7.1",
23
23
  "@as-integrations/koa": "^1.1.0",
24
24
  "@cloudnative/health": "^2.1.2",
@@ -136,5 +136,5 @@
136
136
  }
137
137
  }
138
138
  },
139
- "gitHead": "4202cabe00d8363f62e3f53159755dca71781328"
139
+ "gitHead": "7c98ad5fc5e58f2f92c22f70b1c1c524090f8bfb"
140
140
  }