@aws-amplify/api-graphql 4.0.1-api-v6-models.c1977f8.0 → 4.0.1-api-v6-models.891fe0d.0

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.
Files changed (99) hide show
  1. package/internals/server/package.json +8 -0
  2. package/lib/GraphQLAPI.d.ts +1 -1
  3. package/lib/GraphQLAPI.js +23 -28
  4. package/lib/Providers/AWSAppSyncRealTimeProvider/index.js +467 -582
  5. package/lib/index.js +1 -1
  6. package/lib/internals/APIClient.d.ts +16 -1
  7. package/lib/internals/APIClient.js +232 -206
  8. package/lib/internals/InternalGraphQLAPI.d.ts +1 -1
  9. package/lib/internals/InternalGraphQLAPI.js +205 -247
  10. package/lib/internals/generateClient.js +14 -11
  11. package/lib/internals/generateModelsProperty.js +19 -302
  12. package/lib/internals/operations/get.d.ts +2 -0
  13. package/lib/internals/operations/get.js +59 -0
  14. package/lib/internals/operations/list.d.ts +2 -0
  15. package/lib/internals/operations/list.js +71 -0
  16. package/lib/internals/operations/observeQuery.d.ts +2 -0
  17. package/lib/internals/operations/observeQuery.js +119 -0
  18. package/lib/internals/operations/subscription.d.ts +1 -0
  19. package/lib/internals/operations/subscription.js +27 -0
  20. package/lib/internals/server/generateClient.d.ts +11 -0
  21. package/lib/internals/server/generateClient.js +30 -0
  22. package/lib/internals/server/generateModelsProperty.d.ts +3 -0
  23. package/lib/internals/server/generateModelsProperty.js +45 -0
  24. package/lib/internals/server/index.d.ts +1 -0
  25. package/lib/internals/server/index.js +7 -0
  26. package/lib/internals/types.d.ts +8 -0
  27. package/lib/internals/v6.js +3 -3
  28. package/lib/tsconfig.tsbuildinfo +1 -1
  29. package/lib/types/index.d.ts +41 -4
  30. package/lib/types/index.js +3 -1
  31. package/lib/utils/ConnectionStateMonitor.js +51 -64
  32. package/lib/utils/ReachabilityMonitor/index.js +2 -2
  33. package/lib/utils/ReachabilityMonitor/index.native.js +4 -6
  34. package/lib/utils/ReconnectionMonitor.js +20 -24
  35. package/lib/utils/errors/APIError.js +7 -11
  36. package/lib/utils/errors/assertValidationError.js +4 -4
  37. package/lib/utils/errors/validation.js +5 -6
  38. package/lib/utils/findIndexByFields.js +4 -9
  39. package/lib/utils/resolveConfig.js +9 -10
  40. package/lib/utils/resolveLibraryOptions.js +4 -5
  41. package/lib/utils/resolveOwnerFields.js +10 -33
  42. package/lib/utils/resolvePKFields.js +2 -3
  43. package/lib-esm/GraphQLAPI.d.ts +1 -1
  44. package/lib-esm/GraphQLAPI.js +23 -29
  45. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js +459 -575
  46. package/lib-esm/Providers/constants.js +10 -10
  47. package/lib-esm/internals/APIClient.d.ts +16 -1
  48. package/lib-esm/internals/APIClient.js +230 -205
  49. package/lib-esm/internals/InternalGraphQLAPI.d.ts +1 -1
  50. package/lib-esm/internals/InternalGraphQLAPI.js +201 -244
  51. package/lib-esm/internals/generateClient.js +12 -9
  52. package/lib-esm/internals/generateModelsProperty.js +19 -302
  53. package/lib-esm/internals/operations/get.d.ts +2 -0
  54. package/lib-esm/internals/operations/get.js +55 -0
  55. package/lib-esm/internals/operations/list.d.ts +2 -0
  56. package/lib-esm/internals/operations/list.js +67 -0
  57. package/lib-esm/internals/operations/observeQuery.d.ts +2 -0
  58. package/lib-esm/internals/operations/observeQuery.js +115 -0
  59. package/lib-esm/internals/operations/subscription.d.ts +1 -0
  60. package/lib-esm/internals/operations/subscription.js +23 -0
  61. package/lib-esm/internals/server/generateClient.d.ts +11 -0
  62. package/lib-esm/internals/server/generateClient.js +26 -0
  63. package/lib-esm/internals/server/generateModelsProperty.d.ts +3 -0
  64. package/lib-esm/internals/server/generateModelsProperty.js +41 -0
  65. package/lib-esm/internals/server/index.d.ts +1 -0
  66. package/lib-esm/internals/server/index.js +3 -0
  67. package/lib-esm/internals/types.d.ts +8 -0
  68. package/lib-esm/internals/v6.js +1 -1
  69. package/lib-esm/tsconfig.tsbuildinfo +1 -1
  70. package/lib-esm/types/index.d.ts +41 -4
  71. package/lib-esm/types/index.js +3 -1
  72. package/lib-esm/utils/ConnectionStateMonitor.js +49 -63
  73. package/lib-esm/utils/ReachabilityMonitor/index.js +1 -1
  74. package/lib-esm/utils/ReachabilityMonitor/index.native.js +1 -3
  75. package/lib-esm/utils/ReconnectionMonitor.js +19 -24
  76. package/lib-esm/utils/errors/APIError.js +6 -11
  77. package/lib-esm/utils/errors/assertValidationError.js +2 -2
  78. package/lib-esm/utils/errors/validation.js +5 -6
  79. package/lib-esm/utils/findIndexByFields.js +4 -9
  80. package/lib-esm/utils/resolveConfig.js +8 -9
  81. package/lib-esm/utils/resolveLibraryOptions.js +4 -5
  82. package/lib-esm/utils/resolveOwnerFields.js +10 -33
  83. package/lib-esm/utils/resolvePKFields.js +2 -3
  84. package/package.json +8 -7
  85. package/src/GraphQLAPI.ts +6 -2
  86. package/src/Providers/AWSAppSyncRealTimeProvider/index.ts +6 -5
  87. package/src/internals/APIClient.ts +123 -36
  88. package/src/internals/InternalGraphQLAPI.ts +35 -10
  89. package/src/internals/generateClient.ts +3 -2
  90. package/src/internals/generateModelsProperty.ts +28 -314
  91. package/src/internals/operations/get.ts +121 -0
  92. package/src/internals/operations/list.ts +111 -0
  93. package/src/internals/operations/observeQuery.ts +140 -0
  94. package/src/internals/operations/subscription.ts +59 -0
  95. package/src/internals/server/generateClient.ts +45 -0
  96. package/src/internals/server/generateModelsProperty.ts +70 -0
  97. package/src/internals/server/index.ts +4 -0
  98. package/src/internals/types.ts +9 -0
  99. package/src/types/index.ts +71 -9
@@ -2,11 +2,10 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Observable, SubscriptionLike } from 'rxjs';
4
4
  import { GraphQLError } from 'graphql';
5
- import * as url from 'url';
6
- import { Buffer } from 'buffer';
7
5
  import { Hub, fetchAuthSession, ConsoleLogger } from '@aws-amplify/core';
8
6
  import { signRequest } from '@aws-amplify/core/internals/aws-client-utils';
9
7
  import {
8
+ base64Encoder,
10
9
  GraphQLAuthMode,
11
10
  CustomUserAgentDetails,
12
11
  NonRetryableError,
@@ -700,9 +699,9 @@ export class AWSAppSyncRealTimeProvider {
700
699
  });
701
700
 
702
701
  const headerString = authHeader ? JSON.stringify(authHeader) : '';
703
- const headerQs = Buffer.from(headerString).toString('base64');
702
+ const headerQs = base64Encoder.convert(headerString);
704
703
 
705
- const payloadQs = Buffer.from(payloadString).toString('base64');
704
+ const payloadQs = base64Encoder.convert(payloadString);
706
705
 
707
706
  let discoverableEndpoint = appSyncGraphqlEndpoint ?? '';
708
707
 
@@ -900,7 +899,9 @@ export class AWSAppSyncRealTimeProvider {
900
899
  } else {
901
900
  const handler = headerHandler[authenticationType];
902
901
 
903
- const { host } = url.parse(appSyncGraphqlEndpoint ?? '');
902
+ const host = appSyncGraphqlEndpoint
903
+ ? new AmplifyUrl(appSyncGraphqlEndpoint).host
904
+ : undefined;
904
905
 
905
906
  logger.debug(`Authenticating with ${JSON.stringify(authenticationType)}`);
906
907
  let resolvedApiKey;
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { resolveOwnerFields } from '../utils/resolveOwnerFields';
4
4
  import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils';
5
+ import { V6Client, __authMode, __authToken } from '../types';
6
+ import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core';
5
7
 
6
8
  type ListArgs = { selectionSet?: string[]; filter?: {} };
7
9
 
@@ -48,8 +50,9 @@ export function initializeModel(
48
50
  modelName: string,
49
51
  result: any[],
50
52
  modelIntrospection: any,
51
- authMode: GraphQLAuthMode,
52
- authToken: string | undefined
53
+ authMode: GraphQLAuthMode | undefined,
54
+ authToken: string | undefined,
55
+ context = false
53
56
  ): any[] {
54
57
  const introModel = modelIntrospection.models[modelName];
55
58
  const introModelFields = introModel.fields;
@@ -91,21 +94,45 @@ export function initializeModel(
91
94
  {}
92
95
  );
93
96
 
94
- initializedRelationalFields[field] = (options?: LazyLoadOptions) => {
95
- if (record[targetNames[0]]) {
96
- return client.models[relatedModelName].get(
97
- {
98
- [relatedModelPKFieldName]: record[targetNames[0]],
99
- ...sortKeyValues,
100
- },
101
- {
102
- authMode: options?.authMode || authMode,
103
- authToken: options?.authToken || authToken,
104
- }
105
- );
106
- }
107
- return undefined;
108
- };
97
+ if (context) {
98
+ initializedRelationalFields[field] = (
99
+ contextSpec: AmplifyServer.ContextSpec,
100
+ options?: LazyLoadOptions
101
+ ) => {
102
+ if (record[targetNames[0]]) {
103
+ return client.models[relatedModelName].get(
104
+ contextSpec,
105
+ {
106
+ [relatedModelPKFieldName]: record[targetNames[0]],
107
+ ...sortKeyValues,
108
+ },
109
+ {
110
+ authMode: options?.authMode || authMode,
111
+ authToken: options?.authToken || authToken,
112
+ }
113
+ );
114
+ }
115
+ return undefined;
116
+ };
117
+ } else {
118
+ initializedRelationalFields[field] = (
119
+ options?: LazyLoadOptions
120
+ ) => {
121
+ if (record[targetNames[0]]) {
122
+ return client.models[relatedModelName].get(
123
+ {
124
+ [relatedModelPKFieldName]: record[targetNames[0]],
125
+ ...sortKeyValues,
126
+ },
127
+ {
128
+ authMode: options?.authMode || authMode,
129
+ authToken: options?.authToken || authToken,
130
+ }
131
+ );
132
+ }
133
+ return undefined;
134
+ };
135
+ }
109
136
 
110
137
  break;
111
138
  case connectionType.HAS_MANY:
@@ -125,6 +152,61 @@ export function initializeModel(
125
152
  return { [field]: { eq: record[parentSK[idx - 1]] } };
126
153
  });
127
154
 
155
+ if (context) {
156
+ initializedRelationalFields[field] = (
157
+ contextSpec: AmplifyServer.ContextSpec,
158
+ options?: LazyLoadOptions
159
+ ) => {
160
+ if (record[parentPk]) {
161
+ return client.models[relatedModelName].list(contextSpec, {
162
+ filter: { and: hasManyFilter },
163
+ authMode: options?.authMode || authMode,
164
+ authToken: options?.authToken || authToken,
165
+ });
166
+ }
167
+ return [];
168
+ };
169
+ } else {
170
+ initializedRelationalFields[field] = (
171
+ options?: LazyLoadOptions
172
+ ) => {
173
+ if (record[parentPk]) {
174
+ return client.models[relatedModelName].list({
175
+ filter: { and: hasManyFilter },
176
+ authMode: options?.authMode || authMode,
177
+ authToken: options?.authToken || authToken,
178
+ });
179
+ }
180
+ return [];
181
+ };
182
+ }
183
+
184
+ break;
185
+ }
186
+
187
+ const hasManyFilter = connectionFields.map((field, idx) => {
188
+ if (idx === 0) {
189
+ return { [field]: { eq: record[parentPk] } };
190
+ }
191
+
192
+ return { [field]: { eq: record[parentSK[idx - 1]] } };
193
+ });
194
+
195
+ if (context) {
196
+ initializedRelationalFields[field] = (
197
+ contextSpec: AmplifyServer.ContextSpec,
198
+ options?: LazyLoadOptions
199
+ ) => {
200
+ if (record[parentPk]) {
201
+ return client.models[relatedModelName].list(contextSpec, {
202
+ filter: { and: hasManyFilter },
203
+ authMode: options?.authMode || authMode,
204
+ authToken: options?.authToken || authToken,
205
+ });
206
+ }
207
+ return [];
208
+ };
209
+ } else {
128
210
  initializedRelationalFields[field] = (
129
211
  options?: LazyLoadOptions
130
212
  ) => {
@@ -137,27 +219,8 @@ export function initializeModel(
137
219
  }
138
220
  return [];
139
221
  };
140
- break;
141
222
  }
142
223
 
143
- const hasManyFilter = connectionFields.map((field, idx) => {
144
- if (idx === 0) {
145
- return { [field]: { eq: record[parentPk] } };
146
- }
147
-
148
- return { [field]: { eq: record[parentSK[idx - 1]] } };
149
- });
150
-
151
- initializedRelationalFields[field] = (options?: LazyLoadOptions) => {
152
- if (record[parentPk]) {
153
- return client.models[relatedModelName].list({
154
- filter: { and: hasManyFilter },
155
- authMode: options?.authMode || authMode,
156
- authToken: options?.authToken || authToken,
157
- });
158
- }
159
- return [];
160
- };
161
224
  break;
162
225
  default:
163
226
  break;
@@ -622,3 +685,27 @@ export function normalizeMutationInput(
622
685
 
623
686
  return normalized;
624
687
  }
688
+
689
+ type AuthModeParams = {
690
+ authMode?: GraphQLAuthMode;
691
+ authToken?: string;
692
+ };
693
+
694
+ /**
695
+ * Produces a parameter object that can contains auth mode/token overrides
696
+ * only if present in either `options` (first) or configured on the `client`
697
+ * as a fallback.
698
+ *
699
+ * @param client Configured client from `generateClient`
700
+ * @param options Args/Options obect from call site.
701
+ * @returns
702
+ */
703
+ export function authModeParams(
704
+ client: V6Client,
705
+ options: AuthModeParams = {}
706
+ ): AuthModeParams {
707
+ return {
708
+ authMode: options.authMode || client[__authMode],
709
+ authToken: options.authToken || client[__authToken],
710
+ };
711
+ }
@@ -45,6 +45,10 @@ export const graphqlOperation = (
45
45
  authToken,
46
46
  });
47
47
 
48
+ const isAmplifyInstance = (amplify): amplify is AmplifyClassV6 => {
49
+ return typeof amplify !== 'function';
50
+ };
51
+
48
52
  /**
49
53
  * Export Cloud Logic APIs
50
54
  */
@@ -163,7 +167,9 @@ export class InternalGraphQLAPIClass {
163
167
  * @returns An Observable if the query is a subscription query, else a promise of the graphql result.
164
168
  */
165
169
  graphql<T = any>(
166
- amplify: AmplifyClassV6,
170
+ amplify:
171
+ | AmplifyClassV6
172
+ | ((fn: (amplify: any) => Promise<any>) => Promise<AmplifyClassV6>),
167
173
  { query: paramQuery, variables = {}, authMode, authToken }: GraphQLOptions,
168
174
  additionalHeaders?: { [key: string]: string },
169
175
  customUserAgentDetails?: CustomUserAgentDetails
@@ -190,13 +196,32 @@ export class InternalGraphQLAPIClass {
190
196
  case 'query':
191
197
  case 'mutation':
192
198
  const abortController = new AbortController();
193
- const responsePromise = this._graphql<T>(
194
- amplify,
195
- { query, variables, authMode },
196
- headers,
197
- abortController,
198
- customUserAgentDetails
199
- );
199
+
200
+ let responsePromise: Promise<GraphQLResult<T>>;
201
+
202
+ if (isAmplifyInstance(amplify)) {
203
+ responsePromise = this._graphql<T>(
204
+ amplify,
205
+ { query, variables, authMode },
206
+ headers,
207
+ abortController,
208
+ customUserAgentDetails
209
+ );
210
+ } else {
211
+ const wrapper = amplifyInstance =>
212
+ this._graphql<T>(
213
+ amplifyInstance,
214
+ { query, variables, authMode },
215
+ headers,
216
+ abortController,
217
+ customUserAgentDetails
218
+ );
219
+
220
+ responsePromise = amplify(wrapper) as unknown as Promise<
221
+ GraphQLResult<T>
222
+ >;
223
+ }
224
+
200
225
  this._api.updateRequestToBeCancellable(
201
226
  responsePromise,
202
227
  abortController
@@ -204,7 +229,7 @@ export class InternalGraphQLAPIClass {
204
229
  return responsePromise;
205
230
  case 'subscription':
206
231
  return this._graphqlSubscribe(
207
- amplify,
232
+ amplify as AmplifyClassV6,
208
233
  { query, variables, authMode },
209
234
  headers,
210
235
  customUserAgentDetails
@@ -321,7 +346,7 @@ export class InternalGraphQLAPIClass {
321
346
  // If the exception is because user intentionally
322
347
  // cancelled the request, do not modify the exception
323
348
  // so that clients can identify the exception correctly.
324
- if (this._api.isCancelErrorREST(err)) {
349
+ if (this.isCancelError(err)) {
325
350
  throw err;
326
351
  }
327
352
 
@@ -1,9 +1,8 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { AmplifyClassV6 } from '@aws-amplify/core';
4
3
  import { graphql, cancel, isCancelError } from './v6';
5
4
  import { generateModelsProperty } from './generateModelsProperty';
6
- import { V6Client, __amplify } from '../types';
5
+ import { V6Client, __amplify, __authMode, __authToken } from '../types';
7
6
  import { ClientGenerationParams } from './types';
8
7
 
9
8
  /**
@@ -20,6 +19,8 @@ export function generateClient<T extends Record<any, any> = never>(
20
19
  ): V6Client<T> {
21
20
  const client = {
22
21
  [__amplify]: params.amplify,
22
+ [__authMode]: params.authMode,
23
+ [__authToken]: params.authToken,
23
24
  graphql,
24
25
  cancel,
25
26
  isCancelError,
@@ -1,18 +1,14 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { ModelTypes } from '@aws-amplify/amplify-api-next-types-alpha';
4
- import {
5
- initializeModel,
6
- generateGraphQLDocument,
7
- buildGraphQLVariables,
8
- graphQLOperationsInfo,
9
- ModelOperation,
10
- flattenItems,
11
- } from './APIClient';
4
+ import { graphQLOperationsInfo, ModelOperation } from './APIClient';
12
5
  import { ClientGenerationParams } from './types';
13
- import { V6Client, GraphqlSubscriptionResult } from '../types';
14
- import { findIndexByFields, resolvePKFields } from '../utils';
15
- import { Observable, map } from 'rxjs';
6
+ import { V6Client, __authMode, __authToken } from '../types';
7
+
8
+ import { listFactory } from './operations/list';
9
+ import { getFactory } from './operations/get';
10
+ import { subscriptionFactory } from './operations/subscription';
11
+ import { observeQueryFactory } from './operations/observeQuery';
16
12
 
17
13
  export function generateModelsProperty<T extends Record<any, any> = never>(
18
14
  client: V6Client,
@@ -26,8 +22,8 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
26
22
  return {} as any;
27
23
  }
28
24
 
29
- // TODO: refactor this to use separate methods for each CRUDL.
30
- // Doesn't make sense to gen the methods dynamically given the different args and return values
25
+ const SUBSCRIPTION_OPS = ['ONCREATE', 'ONUPDATE', 'ONDELETE'];
26
+
31
27
  for (const model of Object.values(modelIntrospection.models)) {
32
28
  const { name } = model as any;
33
29
 
@@ -38,309 +34,27 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
38
34
  const operation = key as ModelOperation;
39
35
 
40
36
  if (operation === 'LIST') {
41
- models[name][operationPrefix] = async (args?: any) => {
42
- const query = generateGraphQLDocument(
43
- modelIntrospection.models,
44
- name,
45
- 'LIST',
46
- args
47
- );
48
- const variables = buildGraphQLVariables(
49
- model,
50
- 'LIST',
51
- args,
52
- modelIntrospection
53
- );
54
-
55
- try {
56
- const options: Record<string, any> = {};
57
- if (args?.authMode) options.authMode = args?.authMode;
58
- if (args?.authToken) options.authToken = args?.authToken;
59
-
60
- const { data, extensions } = (await client.graphql({
61
- ...options,
62
- query,
63
- variables,
64
- })) as any;
65
-
66
- // flatten response
67
- if (data !== undefined) {
68
- const [key] = Object.keys(data);
69
-
70
- if (data[key].items) {
71
- const flattenedResult = flattenItems(data)[key];
72
-
73
- // don't init if custom selection set
74
- if (args?.selectionSet) {
75
- return {
76
- data: flattenedResult,
77
- nextToken: data[key].nextToken,
78
- extensions,
79
- };
80
- } else {
81
- const initialized = initializeModel(
82
- client,
83
- name,
84
- flattenedResult,
85
- modelIntrospection,
86
- options.authMode,
87
- options.authToken
88
- );
89
-
90
- return {
91
- data: initialized,
92
- nextToken: data[key].nextToken,
93
- extensions,
94
- };
95
- }
96
- }
97
-
98
- return {
99
- data: data[key],
100
- nextToken: data[key].nextToken,
101
- extensions,
102
- };
103
- }
104
- } catch (error) {
105
- if (error.errors) {
106
- // graphql errors pass through
107
- return error as any;
108
- } else {
109
- // non-graphql errors re re-thrown
110
- throw error;
111
- }
112
- }
113
- };
114
- } else if (['ONCREATE', 'ONUPDATE', 'ONDELETE'].includes(operation)) {
115
- models[name][operationPrefix] = (args?: any) => {
116
- const query = generateGraphQLDocument(
117
- modelIntrospection.models,
118
- name,
119
- operation
120
- );
121
- const variables = buildGraphQLVariables(
122
- model,
123
- operation,
124
- args,
125
- modelIntrospection
126
- );
127
-
128
- const options: Record<string, any> = {};
129
- if (args?.authMode) options.authMode = args?.authMode;
130
- if (args?.authToken) options.authToken = args?.authToken;
131
-
132
- const observable = client.graphql({
133
- ...options,
134
- query,
135
- variables,
136
- }) as GraphqlSubscriptionResult<object>;
137
-
138
- return observable.pipe(
139
- map(value => {
140
- const [key] = Object.keys(value.data);
141
- const [initialized] = initializeModel(
142
- client,
143
- name,
144
- [value.data[key]],
145
- modelIntrospection,
146
- options?.authMode,
147
- options?.authToken
148
- );
149
- return initialized;
150
- })
151
- );
152
- };
37
+ models[name][operationPrefix] = listFactory(
38
+ client,
39
+ modelIntrospection,
40
+ model
41
+ );
42
+ } else if (SUBSCRIPTION_OPS.includes(operation)) {
43
+ models[name][operationPrefix] = subscriptionFactory(
44
+ client,
45
+ modelIntrospection,
46
+ model,
47
+ operation
48
+ );
153
49
  } else if (operation === 'OBSERVE_QUERY') {
154
- models[name][operationPrefix] = (arg?: any) =>
155
- new Observable(subscriber => {
156
- // what we'll be sending to our subscribers
157
- const items: object[] = [];
158
-
159
- // To enqueue subscription messages while we collect our initial
160
- // result set.
161
- const messageQueue = [] as {
162
- type: 'create' | 'update' | 'delete';
163
- item: object;
164
- }[];
165
-
166
- // operation to take when message(s) arrive.
167
- // this operation will be swapped out once initial "sync" is complete
168
- // to immediately ingest messsages.
169
- let receiveMessages = (...messages: typeof messageQueue) => {
170
- return messageQueue.push(...messages);
171
- };
172
-
173
- // start subscriptions
174
- const onCreateSub = models[name].onCreate(arg).subscribe({
175
- next(item) {
176
- receiveMessages({ item, type: 'create' });
177
- },
178
- error(error) {
179
- subscriber.error({ type: 'onCreate', error });
180
- },
181
- });
182
- const onUpdateSub = models[name].onUpdate(arg).subscribe({
183
- next(item) {
184
- receiveMessages({ item, type: 'update' });
185
- },
186
- error(error) {
187
- subscriber.error({ type: 'onUpdate', error });
188
- },
189
- });
190
- const onDeleteSub = models[name].onDelete(arg).subscribe({
191
- next(item) {
192
- receiveMessages({ item, type: 'delete' });
193
- },
194
- error(error) {
195
- subscriber.error({ type: 'onDelete', error });
196
- },
197
- });
198
-
199
- // consumes a list of messages and sends a snapshot
200
- function ingestMessages(messages: typeof messageQueue) {
201
- for (const message of messages) {
202
- const idx = findIndexByFields(
203
- message.item,
204
- items,
205
- pkFields as any
206
- );
207
- switch (message.type) {
208
- case 'create':
209
- if (idx < 0) items.push(message.item);
210
- break;
211
- case 'update':
212
- if (idx >= 0) items[idx] = message.item;
213
- break;
214
- case 'delete':
215
- if (idx >= 0) items.splice(idx, 1);
216
- break;
217
- default:
218
- console.error(
219
- 'Unrecognized message in observeQuery.',
220
- message
221
- );
222
- }
223
- }
224
- subscriber.next({
225
- items,
226
- isSynced: true,
227
- });
228
- }
229
-
230
- const pkFields = resolvePKFields(model);
231
-
232
- // initial results
233
- (async () => {
234
- let firstPage = true;
235
- let nextToken: string | null = null;
236
- while (!subscriber.closed && (firstPage || nextToken)) {
237
- firstPage = false;
238
-
239
- const {
240
- data: page,
241
- errors,
242
- nextToken: _nextToken,
243
- } = await models[name].list({ ...arg, nextToken });
244
- nextToken = _nextToken;
245
-
246
- items.push(...page);
247
-
248
- // if there are no more pages and no items we already know about
249
- // that need to be merged in from sub, we're "synced"
250
- const isSynced =
251
- messageQueue.length === 0 &&
252
- (nextToken === null || nextToken === undefined);
253
-
254
- subscriber.next({
255
- items,
256
- isSynced,
257
- });
258
-
259
- if (Array.isArray(errors)) {
260
- for (const error of errors) {
261
- subscriber.error(error);
262
- }
263
- }
264
- }
265
-
266
- // play through the queue
267
- if (messageQueue.length > 0) {
268
- ingestMessages(messageQueue);
269
- }
270
-
271
- // switch the queue to write directly to the items collection
272
- receiveMessages = (...messages: typeof messageQueue) => {
273
- ingestMessages(messages);
274
- return items.length;
275
- };
276
- })();
277
-
278
- // when subscriber unsubscribes, tear down internal subs
279
- return () => {
280
- // 1. tear down internal subs
281
- onCreateSub.unsubscribe();
282
- onUpdateSub.unsubscribe();
283
- onDeleteSub.unsubscribe();
284
-
285
- // 2. there is no need to explicitly stop paging. instead, we
286
- // just check `subscriber.closed` above before fetching each page.
287
- };
288
- });
50
+ models[name][operationPrefix] = observeQueryFactory(models, model);
289
51
  } else {
290
- models[name][operationPrefix] = async (arg?: any, options?: any) => {
291
- const query = generateGraphQLDocument(
292
- modelIntrospection.models,
293
- name,
294
- operation,
295
- options
296
- );
297
- const variables = buildGraphQLVariables(
298
- model,
299
- operation,
300
- arg,
301
- modelIntrospection
302
- );
303
-
304
- try {
305
- const { data, extensions } = (await client.graphql({
306
- ...options,
307
- query,
308
- variables,
309
- })) as any;
310
-
311
- // flatten response
312
- if (data !== undefined) {
313
- const [key] = Object.keys(data);
314
- const flattenedResult = flattenItems(data)[key];
315
-
316
- if (options?.selectionSet) {
317
- return { data: flattenedResult, extensions };
318
- } else {
319
- // TODO: refactor to avoid destructuring here
320
- const [initialized] = initializeModel(
321
- client,
322
- name,
323
- [flattenedResult],
324
- modelIntrospection,
325
- options?.authMode,
326
- options?.authToken
327
- );
328
-
329
- return { data: initialized, extensions };
330
- }
331
- } else {
332
- return { data: null, extensions };
333
- }
334
- } catch (error) {
335
- if (error.errors) {
336
- // graphql errors pass through
337
- return error as any;
338
- } else {
339
- // non-graphql errors re re-thrown
340
- throw error;
341
- }
342
- }
343
- };
52
+ models[name][operationPrefix] = getFactory(
53
+ client,
54
+ modelIntrospection,
55
+ model,
56
+ operation
57
+ );
344
58
  }
345
59
  }
346
60
  );