@aws-amplify/api-graphql 4.0.1-api-v6-models.3eb7748.0 → 4.0.1-api-v6-models.01b2bb8.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.
- package/lib/internals/APIClient.d.ts +4 -0
- package/lib/internals/APIClient.js +60 -60
- package/lib/internals/generateModelsProperty.js +163 -5
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/findIndexByFields.d.ts +9 -0
- package/lib/utils/findIndexByFields.js +28 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +5 -1
- package/lib/utils/resolvePKFields.d.ts +13 -0
- package/lib/utils/resolvePKFields.js +16 -0
- package/lib-esm/internals/APIClient.d.ts +4 -0
- package/lib-esm/internals/APIClient.js +60 -60
- package/lib-esm/internals/generateModelsProperty.js +165 -7
- package/lib-esm/tsconfig.tsbuildinfo +1 -1
- package/lib-esm/utils/findIndexByFields.d.ts +9 -0
- package/lib-esm/utils/findIndexByFields.js +24 -0
- package/lib-esm/utils/index.d.ts +2 -0
- package/lib-esm/utils/index.js +2 -0
- package/lib-esm/utils/resolvePKFields.d.ts +13 -0
- package/lib-esm/utils/resolvePKFields.js +12 -0
- package/package.json +5 -5
- package/src/internals/APIClient.ts +69 -56
- package/src/internals/generateModelsProperty.ts +147 -4
- package/src/utils/findIndexByFields.ts +29 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/resolvePKFields.ts +22 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Iterates through a collection to find a matching item and returns the index.
|
|
3
|
+
*
|
|
4
|
+
* @param needle The item to search for
|
|
5
|
+
* @param haystack The collection to search
|
|
6
|
+
* @param keyFields The fields used to indicate a match
|
|
7
|
+
* @returns Index of `needle` in `haystack`, otherwise -1 if not found.
|
|
8
|
+
*/
|
|
9
|
+
export declare function findIndexByFields<T>(needle: T, haystack: T[], keyFields: Array<keyof T>): number;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Iterates through a collection to find a matching item and returns the index.
|
|
5
|
+
*
|
|
6
|
+
* @param needle The item to search for
|
|
7
|
+
* @param haystack The collection to search
|
|
8
|
+
* @param keyFields The fields used to indicate a match
|
|
9
|
+
* @returns Index of `needle` in `haystack`, otherwise -1 if not found.
|
|
10
|
+
*/
|
|
11
|
+
export function findIndexByFields(needle, haystack, keyFields) {
|
|
12
|
+
var searchObject = Object.fromEntries(keyFields.map(function (fieldName) { return [fieldName, needle[fieldName]]; }));
|
|
13
|
+
var _loop_1 = function (i) {
|
|
14
|
+
if (Object.keys(searchObject).every(function (k) { return searchObject[k] === haystack[i][k]; })) {
|
|
15
|
+
return { value: i };
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
for (var i = 0; i < haystack.length; i++) {
|
|
19
|
+
var state_1 = _loop_1(i);
|
|
20
|
+
if (typeof state_1 === "object")
|
|
21
|
+
return state_1.value;
|
|
22
|
+
}
|
|
23
|
+
return -1;
|
|
24
|
+
}
|
package/lib-esm/utils/index.d.ts
CHANGED
package/lib-esm/utils/index.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ResourcesConfig } from '@aws-amplify/core';
|
|
2
|
+
type GraphQLConfig = Exclude<ResourcesConfig['API'], undefined>['GraphQL'];
|
|
3
|
+
type ModelIntrospectionSchema = Exclude<Exclude<GraphQLConfig, undefined>['modelIntrospection'], undefined>;
|
|
4
|
+
type SchemaModel = ModelIntrospectionSchema['models'][string];
|
|
5
|
+
/**
|
|
6
|
+
* Given a SchemaModel from a ModelIntrospectionSchema, returns the primary key
|
|
7
|
+
* as an array of field names.
|
|
8
|
+
*
|
|
9
|
+
* @param model The model object
|
|
10
|
+
* @returns Array of field names
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolvePKFields(model: SchemaModel): string[];
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { __read, __spreadArray } from "tslib";
|
|
2
|
+
/**
|
|
3
|
+
* Given a SchemaModel from a ModelIntrospectionSchema, returns the primary key
|
|
4
|
+
* as an array of field names.
|
|
5
|
+
*
|
|
6
|
+
* @param model The model object
|
|
7
|
+
* @returns Array of field names
|
|
8
|
+
*/
|
|
9
|
+
export function resolvePKFields(model) {
|
|
10
|
+
var _a = model.primaryKeyInfo, primaryKeyFieldName = _a.primaryKeyFieldName, sortKeyFieldNames = _a.sortKeyFieldNames;
|
|
11
|
+
return __spreadArray([primaryKeyFieldName], __read(sortKeyFieldNames), false);
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/api-graphql",
|
|
3
|
-
"version": "4.0.1-api-v6-models.
|
|
3
|
+
"version": "4.0.1-api-v6-models.01b2bb8.0+01b2bb8",
|
|
4
4
|
"description": "Api-graphql category of aws-amplify",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"module": "./lib-esm/index.js",
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
],
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@aws-amplify/amplify-api-next-types-alpha": "^0.2.0",
|
|
55
|
-
"@aws-amplify/api-rest": "4.0.1-api-v6-models.
|
|
56
|
-
"@aws-amplify/auth": "6.0.1-api-v6-models.
|
|
57
|
-
"@aws-amplify/core": "6.0.1-api-v6-models.
|
|
55
|
+
"@aws-amplify/api-rest": "4.0.1-api-v6-models.01b2bb8.0+01b2bb8",
|
|
56
|
+
"@aws-amplify/auth": "6.0.1-api-v6-models.01b2bb8.0+01b2bb8",
|
|
57
|
+
"@aws-amplify/core": "6.0.1-api-v6-models.01b2bb8.0+01b2bb8",
|
|
58
58
|
"@aws-sdk/types": "3.387.0",
|
|
59
59
|
"graphql": "15.8.0",
|
|
60
60
|
"rxjs": "^7.8.1",
|
|
@@ -126,5 +126,5 @@
|
|
|
126
126
|
"<rootDir>/__tests__"
|
|
127
127
|
]
|
|
128
128
|
},
|
|
129
|
-
"gitHead": "
|
|
129
|
+
"gitHead": "01b2bb8503ffd202c858da1ff6afeb7c008a4563"
|
|
130
130
|
}
|
|
@@ -157,6 +157,7 @@ export const graphQLOperationsInfo = {
|
|
|
157
157
|
ONCREATE: { operationPrefix: 'onCreate' as const, usePlural: false },
|
|
158
158
|
ONUPDATE: { operationPrefix: 'onUpdate' as const, usePlural: false },
|
|
159
159
|
ONDELETE: { operationPrefix: 'onDelete' as const, usePlural: false },
|
|
160
|
+
OBSERVE_QUERY: { operationPrefix: 'observeQuery' as const, usePlural: false },
|
|
160
161
|
};
|
|
161
162
|
export type ModelOperation = keyof typeof graphQLOperationsInfo;
|
|
162
163
|
|
|
@@ -164,7 +165,7 @@ type OperationPrefix =
|
|
|
164
165
|
(typeof graphQLOperationsInfo)[ModelOperation]['operationPrefix'];
|
|
165
166
|
|
|
166
167
|
const graphQLDocumentsCache = new Map<string, Map<ModelOperation, string>>();
|
|
167
|
-
const
|
|
168
|
+
const SELECTION_SET_WILDCARD = '*';
|
|
168
169
|
|
|
169
170
|
function defaultSelectionSetForModel(modelDefinition: any): string[] {
|
|
170
171
|
// fields that are explicitly part of the graphql schema; not
|
|
@@ -208,78 +209,70 @@ export function customSelectionSetToIR(
|
|
|
208
209
|
selectionSet: string[]
|
|
209
210
|
): Record<string, string | object> {
|
|
210
211
|
const modelDefinition = modelIntrospection[modelName];
|
|
211
|
-
const { fields } = modelDefinition;
|
|
212
|
-
|
|
213
|
-
const intermediateSelectionSet: Record<string, string | object> = {};
|
|
212
|
+
const { fields: modelFields } = modelDefinition;
|
|
214
213
|
|
|
215
|
-
|
|
216
|
-
const nested =
|
|
214
|
+
return selectionSet.reduce((resultObj, path) => {
|
|
215
|
+
const [fieldName, nested, ...rest] = path.split('.');
|
|
217
216
|
|
|
218
217
|
if (nested) {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
const relatedModel = fields[modelFieldName]?.type?.model;
|
|
218
|
+
const relatedModel = modelFields[fieldName]?.type?.model;
|
|
222
219
|
|
|
223
220
|
if (!relatedModel) {
|
|
224
221
|
// TODO: may need to change this to support custom types
|
|
225
|
-
throw Error(`${
|
|
222
|
+
throw Error(`${fieldName} is not a model field`);
|
|
226
223
|
}
|
|
227
224
|
|
|
228
|
-
|
|
229
|
-
const relatedModelDefinition = modelIntrospection[relatedModel];
|
|
225
|
+
const relatedModelDefinition = modelIntrospection[relatedModel];
|
|
230
226
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
const selectionSet =
|
|
228
|
+
nested === SELECTION_SET_WILDCARD
|
|
229
|
+
? defaultSelectionSetIR(relatedModelDefinition)
|
|
230
|
+
: // if we have a path like 'field.anotherField' recursively build up selection set IR
|
|
231
|
+
customSelectionSetToIR(modelIntrospection, relatedModel, [
|
|
232
|
+
[nested, ...rest].join('.'),
|
|
233
|
+
]);
|
|
234
234
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
235
|
+
if (modelFields[fieldName]?.isArray) {
|
|
236
|
+
const existing = resultObj[fieldName] || {
|
|
237
|
+
items: {},
|
|
238
|
+
};
|
|
239
|
+
const merged = { ...existing.items, ...selectionSet };
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
};
|
|
244
|
-
} else {
|
|
245
|
-
intermediateSelectionSet[modelFieldName] = reduced;
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
const getNestedSelSet = customSelectionSetToIR(
|
|
249
|
-
modelIntrospection,
|
|
250
|
-
relatedModel,
|
|
251
|
-
[selectedField]
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
if (fields[modelFieldName]?.isArray) {
|
|
255
|
-
const existing = (intermediateSelectionSet as any)[
|
|
256
|
-
modelFieldName
|
|
257
|
-
] || { items: {} };
|
|
258
|
-
const merged = { ...existing.items, ...getNestedSelSet };
|
|
241
|
+
resultObj[fieldName] = { items: merged };
|
|
242
|
+
return resultObj;
|
|
243
|
+
}
|
|
259
244
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const existingItems =
|
|
263
|
-
(intermediateSelectionSet as any)[modelFieldName] || {};
|
|
264
|
-
const merged = { ...existingItems, ...getNestedSelSet };
|
|
245
|
+
const existingItems = resultObj[fieldName] || {};
|
|
246
|
+
const merged = { ...existingItems, ...selectionSet };
|
|
265
247
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
} else {
|
|
270
|
-
const exists = Boolean(fields[f]);
|
|
248
|
+
resultObj[fieldName] = merged;
|
|
249
|
+
return resultObj;
|
|
250
|
+
}
|
|
271
251
|
|
|
272
|
-
|
|
273
|
-
throw Error(`${f} is not a field of model ${modelName}`);
|
|
274
|
-
}
|
|
252
|
+
const exists = Boolean(modelFields[fieldName]);
|
|
275
253
|
|
|
276
|
-
|
|
254
|
+
if (!exists) {
|
|
255
|
+
throw Error(`${fieldName} is not a field of model ${modelName}`);
|
|
277
256
|
}
|
|
278
|
-
}
|
|
279
257
|
|
|
280
|
-
|
|
258
|
+
resultObj[fieldName] = FIELD_IR;
|
|
259
|
+
return resultObj;
|
|
260
|
+
}, {});
|
|
281
261
|
}
|
|
282
262
|
|
|
263
|
+
const defaultSelectionSetIR = relatedModelDefinition => {
|
|
264
|
+
const defaultSelectionSet = defaultSelectionSetForModel(
|
|
265
|
+
relatedModelDefinition
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const reduced = defaultSelectionSet.reduce((acc, curVal) => {
|
|
269
|
+
acc[curVal] = FIELD_IR;
|
|
270
|
+
return acc;
|
|
271
|
+
}, {});
|
|
272
|
+
|
|
273
|
+
return reduced;
|
|
274
|
+
};
|
|
275
|
+
|
|
283
276
|
/**
|
|
284
277
|
* Stringifies selection set IR
|
|
285
278
|
* * @example
|
|
@@ -419,10 +412,12 @@ export function generateGraphQLDocument(
|
|
|
419
412
|
graphQLArguments ??
|
|
420
413
|
(graphQLArguments = {
|
|
421
414
|
filter: `Model${name}FilterInput`,
|
|
415
|
+
limit: 'Int',
|
|
416
|
+
nextToken: 'String',
|
|
422
417
|
});
|
|
423
418
|
graphQLOperationType ?? (graphQLOperationType = 'query');
|
|
424
419
|
graphQLSelectionSet ??
|
|
425
|
-
(graphQLSelectionSet = `items { ${selectionSetFields} }`);
|
|
420
|
+
(graphQLSelectionSet = `items { ${selectionSetFields} } nextToken __typename`);
|
|
426
421
|
case 'ONCREATE':
|
|
427
422
|
case 'ONUPDATE':
|
|
428
423
|
case 'ONDELETE':
|
|
@@ -432,6 +427,12 @@ export function generateGraphQLDocument(
|
|
|
432
427
|
});
|
|
433
428
|
graphQLOperationType ?? (graphQLOperationType = 'subscription');
|
|
434
429
|
graphQLSelectionSet ?? (graphQLSelectionSet = selectionSetFields);
|
|
430
|
+
break;
|
|
431
|
+
case 'OBSERVE_QUERY':
|
|
432
|
+
default:
|
|
433
|
+
throw new Error(
|
|
434
|
+
'Internal error: Attempted to generate graphql document for observeQuery. Please report this error.'
|
|
435
|
+
);
|
|
435
436
|
}
|
|
436
437
|
|
|
437
438
|
const graphQLDocument = `${graphQLOperationType}${
|
|
@@ -468,7 +469,7 @@ export function buildGraphQLVariables(
|
|
|
468
469
|
},
|
|
469
470
|
} = modelDefinition;
|
|
470
471
|
|
|
471
|
-
let variables = {};
|
|
472
|
+
let variables: Record<string, any> = {};
|
|
472
473
|
|
|
473
474
|
// TODO: process input
|
|
474
475
|
switch (operation) {
|
|
@@ -510,6 +511,13 @@ export function buildGraphQLVariables(
|
|
|
510
511
|
}
|
|
511
512
|
break;
|
|
512
513
|
case 'LIST':
|
|
514
|
+
if (arg?.filter) {
|
|
515
|
+
variables.filter = arg.filter;
|
|
516
|
+
}
|
|
517
|
+
if (arg?.nextToken) {
|
|
518
|
+
variables.nextToken = arg.nextToken;
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
513
521
|
case 'ONCREATE':
|
|
514
522
|
case 'ONUPDATE':
|
|
515
523
|
case 'ONDELETE':
|
|
@@ -517,6 +525,11 @@ export function buildGraphQLVariables(
|
|
|
517
525
|
variables = { filter: arg.filter };
|
|
518
526
|
}
|
|
519
527
|
break;
|
|
528
|
+
case 'OBSERVE_QUERY':
|
|
529
|
+
throw new Error(
|
|
530
|
+
'Internal error: Attempted to build variables for observeQuery. Please report this error.'
|
|
531
|
+
);
|
|
532
|
+
break;
|
|
520
533
|
default:
|
|
521
534
|
const exhaustiveCheck: never = operation;
|
|
522
535
|
throw new Error(`Unhandled operation case: ${exhaustiveCheck}`);
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from './APIClient';
|
|
12
12
|
import { ClientGenerationParams } from './types';
|
|
13
13
|
import { V6Client, GraphqlSubscriptionResult } from '../types';
|
|
14
|
+
import { findIndexByFields, resolvePKFields } from '../utils';
|
|
14
15
|
import { Observable, map } from 'rxjs';
|
|
15
16
|
|
|
16
17
|
export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
@@ -52,7 +53,7 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
|
52
53
|
);
|
|
53
54
|
|
|
54
55
|
try {
|
|
55
|
-
const { data,
|
|
56
|
+
const { data, extensions } = (await client.graphql({
|
|
56
57
|
query,
|
|
57
58
|
variables,
|
|
58
59
|
})) as any;
|
|
@@ -68,7 +69,7 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
|
68
69
|
if (args?.selectionSet) {
|
|
69
70
|
return {
|
|
70
71
|
data: flattenedResult,
|
|
71
|
-
nextToken,
|
|
72
|
+
nextToken: data[key].nextToken,
|
|
72
73
|
extensions,
|
|
73
74
|
};
|
|
74
75
|
} else {
|
|
@@ -81,7 +82,7 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
|
81
82
|
|
|
82
83
|
return {
|
|
83
84
|
data: initialized,
|
|
84
|
-
nextToken,
|
|
85
|
+
nextToken: data[key].nextToken,
|
|
85
86
|
extensions,
|
|
86
87
|
};
|
|
87
88
|
}
|
|
@@ -89,7 +90,7 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
|
89
90
|
|
|
90
91
|
return {
|
|
91
92
|
data: data[key],
|
|
92
|
-
nextToken,
|
|
93
|
+
nextToken: data[key].nextToken,
|
|
93
94
|
extensions,
|
|
94
95
|
};
|
|
95
96
|
}
|
|
@@ -129,6 +130,148 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
|
129
130
|
})
|
|
130
131
|
);
|
|
131
132
|
};
|
|
133
|
+
} else if (operation === 'OBSERVE_QUERY') {
|
|
134
|
+
models[name][operationPrefix] = (arg?: any, options?: any) =>
|
|
135
|
+
new Observable(subscriber => {
|
|
136
|
+
// what we'll be sending to our subscribers
|
|
137
|
+
const items: object[] = [];
|
|
138
|
+
|
|
139
|
+
// To enqueue subscription messages while we collect our initial
|
|
140
|
+
// result set.
|
|
141
|
+
const messageQueue = [] as {
|
|
142
|
+
type: 'create' | 'update' | 'delete';
|
|
143
|
+
item: object;
|
|
144
|
+
}[];
|
|
145
|
+
|
|
146
|
+
// operation to take when message(s) arrive.
|
|
147
|
+
// this operation will be swapped out once initial "sync" is complete
|
|
148
|
+
// to immediately ingest messsages.
|
|
149
|
+
let receiveMessages = (...messages: typeof messageQueue) => {
|
|
150
|
+
return messageQueue.push(...messages);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// start subscriptions
|
|
154
|
+
const onCreateSub = models[name]
|
|
155
|
+
.onCreate(arg, options)
|
|
156
|
+
.subscribe({
|
|
157
|
+
next(item) {
|
|
158
|
+
receiveMessages({ item, type: 'create' });
|
|
159
|
+
},
|
|
160
|
+
error(error) {
|
|
161
|
+
subscriber.error({ type: 'onCreate', error });
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const onUpdateSub = models[name]
|
|
165
|
+
.onUpdate(arg, options)
|
|
166
|
+
.subscribe({
|
|
167
|
+
next(item) {
|
|
168
|
+
receiveMessages({ item, type: 'update' });
|
|
169
|
+
},
|
|
170
|
+
error(error) {
|
|
171
|
+
subscriber.error({ type: 'onUpdate', error });
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
const onDeleteSub = models[name]
|
|
175
|
+
.onDelete(arg, options)
|
|
176
|
+
.subscribe({
|
|
177
|
+
next(item) {
|
|
178
|
+
receiveMessages({ item, type: 'delete' });
|
|
179
|
+
},
|
|
180
|
+
error(error) {
|
|
181
|
+
subscriber.error({ type: 'onDelete', error });
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// consumes a list of messages and sends a snapshot
|
|
186
|
+
function ingestMessages(messages: typeof messageQueue) {
|
|
187
|
+
for (const message of messages) {
|
|
188
|
+
const idx = findIndexByFields(
|
|
189
|
+
message.item,
|
|
190
|
+
items,
|
|
191
|
+
pkFields as any
|
|
192
|
+
);
|
|
193
|
+
switch (message.type) {
|
|
194
|
+
case 'create':
|
|
195
|
+
if (idx < 0) items.push(message.item);
|
|
196
|
+
break;
|
|
197
|
+
case 'update':
|
|
198
|
+
if (idx >= 0) items[idx] = message.item;
|
|
199
|
+
break;
|
|
200
|
+
case 'delete':
|
|
201
|
+
if (idx >= 0) items.splice(idx, 1);
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
console.error(
|
|
205
|
+
'Unrecognized message in observeQuery.',
|
|
206
|
+
message
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
subscriber.next({
|
|
211
|
+
items,
|
|
212
|
+
isSynced: true,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const pkFields = resolvePKFields(model);
|
|
217
|
+
|
|
218
|
+
// initial results
|
|
219
|
+
(async () => {
|
|
220
|
+
let firstPage = true;
|
|
221
|
+
let nextToken: string | null = null;
|
|
222
|
+
while (!subscriber.closed && (firstPage || nextToken)) {
|
|
223
|
+
firstPage = false;
|
|
224
|
+
|
|
225
|
+
const {
|
|
226
|
+
data: page,
|
|
227
|
+
errors,
|
|
228
|
+
nextToken: _nextToken,
|
|
229
|
+
} = await models[name].list({ ...arg, nextToken }, options);
|
|
230
|
+
nextToken = _nextToken;
|
|
231
|
+
|
|
232
|
+
items.push(...page);
|
|
233
|
+
|
|
234
|
+
// if there are no more pages and no items we already know about
|
|
235
|
+
// that need to be merged in from sub, we're "synced"
|
|
236
|
+
const isSynced =
|
|
237
|
+
messageQueue.length === 0 &&
|
|
238
|
+
(nextToken === null || nextToken === undefined);
|
|
239
|
+
|
|
240
|
+
subscriber.next({
|
|
241
|
+
items,
|
|
242
|
+
isSynced,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (Array.isArray(errors)) {
|
|
246
|
+
for (const error of errors) {
|
|
247
|
+
subscriber.error(error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// play through the queue
|
|
253
|
+
if (messageQueue.length > 0) {
|
|
254
|
+
ingestMessages(messageQueue);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// switch the queue to write directly to the items collection
|
|
258
|
+
receiveMessages = (...messages: typeof messageQueue) => {
|
|
259
|
+
ingestMessages(messages);
|
|
260
|
+
return items.length;
|
|
261
|
+
};
|
|
262
|
+
})();
|
|
263
|
+
|
|
264
|
+
// when subscriber unsubscribes, tear down internal subs
|
|
265
|
+
return () => {
|
|
266
|
+
// 1. tear down internal subs
|
|
267
|
+
onCreateSub.unsubscribe();
|
|
268
|
+
onUpdateSub.unsubscribe();
|
|
269
|
+
onDeleteSub.unsubscribe();
|
|
270
|
+
|
|
271
|
+
// 2. there is no need to explicitly stop paging. instead, we
|
|
272
|
+
// just check `subscriber.closed` above before fetching each page.
|
|
273
|
+
};
|
|
274
|
+
});
|
|
132
275
|
} else {
|
|
133
276
|
models[name][operationPrefix] = async (arg?: any, options?: any) => {
|
|
134
277
|
const query = generateGraphQLDocument(
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Iterates through a collection to find a matching item and returns the index.
|
|
5
|
+
*
|
|
6
|
+
* @param needle The item to search for
|
|
7
|
+
* @param haystack The collection to search
|
|
8
|
+
* @param keyFields The fields used to indicate a match
|
|
9
|
+
* @returns Index of `needle` in `haystack`, otherwise -1 if not found.
|
|
10
|
+
*/
|
|
11
|
+
export function findIndexByFields<T>(
|
|
12
|
+
needle: T,
|
|
13
|
+
haystack: T[],
|
|
14
|
+
keyFields: Array<keyof T>
|
|
15
|
+
): number {
|
|
16
|
+
const searchObject = Object.fromEntries(
|
|
17
|
+
keyFields.map(fieldName => [fieldName, needle[fieldName]])
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < haystack.length; i++) {
|
|
21
|
+
if (
|
|
22
|
+
Object.keys(searchObject).every(k => searchObject[k] === haystack[i][k])
|
|
23
|
+
) {
|
|
24
|
+
return i;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { ResourcesConfig } from '@aws-amplify/core';
|
|
4
|
+
|
|
5
|
+
type GraphQLConfig = Exclude<ResourcesConfig['API'], undefined>['GraphQL'];
|
|
6
|
+
type ModelIntrospectionSchema = Exclude<
|
|
7
|
+
Exclude<GraphQLConfig, undefined>['modelIntrospection'],
|
|
8
|
+
undefined
|
|
9
|
+
>;
|
|
10
|
+
type SchemaModel = ModelIntrospectionSchema['models'][string];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Given a SchemaModel from a ModelIntrospectionSchema, returns the primary key
|
|
14
|
+
* as an array of field names.
|
|
15
|
+
*
|
|
16
|
+
* @param model The model object
|
|
17
|
+
* @returns Array of field names
|
|
18
|
+
*/
|
|
19
|
+
export function resolvePKFields(model: SchemaModel) {
|
|
20
|
+
const { primaryKeyFieldName, sortKeyFieldNames } = model.primaryKeyInfo;
|
|
21
|
+
return [primaryKeyFieldName, ...sortKeyFieldNames];
|
|
22
|
+
}
|