@aws-amplify/api-graphql 4.0.1-api-v6-models.572a7b5.0 → 4.0.1-api-v6-models.9493d4d.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 +12 -0
- package/lib/internals/APIClient.js +66 -60
- package/lib/internals/generateModelsProperty.js +15 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/resolveOwnerFields.d.ts +12 -0
- package/lib/utils/resolveOwnerFields.js +63 -0
- package/lib-esm/internals/APIClient.d.ts +12 -0
- package/lib-esm/internals/APIClient.js +66 -60
- package/lib-esm/internals/generateModelsProperty.js +15 -0
- package/lib-esm/tsconfig.tsbuildinfo +1 -1
- package/lib-esm/utils/resolveOwnerFields.d.ts +12 -0
- package/lib-esm/utils/resolveOwnerFields.js +59 -0
- package/package.json +5 -5
- package/src/internals/APIClient.ts +73 -56
- package/src/internals/generateModelsProperty.ts +28 -1
- package/src/utils/resolveOwnerFields.ts +65 -0
|
@@ -0,0 +1,12 @@
|
|
|
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 Model = ModelIntrospectionSchema['models'][string];
|
|
5
|
+
/**
|
|
6
|
+
* Given an introspection schema model, returns all owner fields.
|
|
7
|
+
*
|
|
8
|
+
* @param model Model from an introspection schema
|
|
9
|
+
* @returns List of owner field names
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveOwnerFields(model: Model): string[];
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { __values } from "tslib";
|
|
2
|
+
/**
|
|
3
|
+
* Given an introspection schema model, returns all owner fields.
|
|
4
|
+
*
|
|
5
|
+
* @param model Model from an introspection schema
|
|
6
|
+
* @returns List of owner field names
|
|
7
|
+
*/
|
|
8
|
+
export function resolveOwnerFields(model) {
|
|
9
|
+
var e_1, _a, e_2, _b;
|
|
10
|
+
var ownerFields = new Set();
|
|
11
|
+
try {
|
|
12
|
+
for (var _c = __values(model.attributes || []), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
13
|
+
var attr = _d.value;
|
|
14
|
+
if (isAuthAttribute(attr)) {
|
|
15
|
+
try {
|
|
16
|
+
for (var _e = (e_2 = void 0, __values(attr.properties.rules)), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
17
|
+
var rule = _f.value;
|
|
18
|
+
if (rule.allow === 'owner') {
|
|
19
|
+
ownerFields.add(rule.ownerField || 'owner');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
24
|
+
finally {
|
|
25
|
+
try {
|
|
26
|
+
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
|
27
|
+
}
|
|
28
|
+
finally { if (e_2) throw e_2.error; }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
34
|
+
finally {
|
|
35
|
+
try {
|
|
36
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
37
|
+
}
|
|
38
|
+
finally { if (e_1) throw e_1.error; }
|
|
39
|
+
}
|
|
40
|
+
return Array.from(ownerFields);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Type guard that identifies an auth attribute with an attached rules list that
|
|
44
|
+
* specifies an `allow` attribute at a minimum.
|
|
45
|
+
*
|
|
46
|
+
* @param attribute Any object. Ideally a model introspection schema model attribute
|
|
47
|
+
* @returns True if given object is an auth attribute
|
|
48
|
+
*/
|
|
49
|
+
function isAuthAttribute(attribute) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
if ((attribute === null || attribute === void 0 ? void 0 : attribute.type) === 'auth') {
|
|
52
|
+
if (typeof (attribute === null || attribute === void 0 ? void 0 : attribute.properties) === 'object') {
|
|
53
|
+
if (Array.isArray((_a = attribute === null || attribute === void 0 ? void 0 : attribute.properties) === null || _a === void 0 ? void 0 : _a.rules)) {
|
|
54
|
+
return ((_b = attribute === null || attribute === void 0 ? void 0 : attribute.properties) === null || _b === void 0 ? void 0 : _b.rules).every(function (rule) { return !!rule.allow; });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
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.9493d4d.0+9493d4d",
|
|
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.9493d4d.0+9493d4d",
|
|
56
|
+
"@aws-amplify/auth": "6.0.1-api-v6-models.9493d4d.0+9493d4d",
|
|
57
|
+
"@aws-amplify/core": "6.0.1-api-v6-models.9493d4d.0+9493d4d",
|
|
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": "9493d4d9a9fb9bfa104c0cf56e5a1d3154daa088"
|
|
130
130
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { resolveOwnerFields } from '../utils/resolveOwnerFields';
|
|
4
|
+
|
|
3
5
|
type ListArgs = { selectionSet?: string[]; filter?: {} };
|
|
4
6
|
|
|
5
7
|
const connectionType = {
|
|
@@ -152,6 +154,9 @@ export const graphQLOperationsInfo = {
|
|
|
152
154
|
UPDATE: { operationPrefix: 'update' as const, usePlural: false },
|
|
153
155
|
DELETE: { operationPrefix: 'delete' as const, usePlural: false },
|
|
154
156
|
LIST: { operationPrefix: 'list' as const, usePlural: true },
|
|
157
|
+
ONCREATE: { operationPrefix: 'onCreate' as const, usePlural: false },
|
|
158
|
+
ONUPDATE: { operationPrefix: 'onUpdate' as const, usePlural: false },
|
|
159
|
+
ONDELETE: { operationPrefix: 'onDelete' as const, usePlural: false },
|
|
155
160
|
};
|
|
156
161
|
export type ModelOperation = keyof typeof graphQLOperationsInfo;
|
|
157
162
|
|
|
@@ -159,13 +164,21 @@ type OperationPrefix =
|
|
|
159
164
|
(typeof graphQLOperationsInfo)[ModelOperation]['operationPrefix'];
|
|
160
165
|
|
|
161
166
|
const graphQLDocumentsCache = new Map<string, Map<ModelOperation, string>>();
|
|
162
|
-
const
|
|
167
|
+
const SELECTION_SET_WILDCARD = '*';
|
|
163
168
|
|
|
164
169
|
function defaultSelectionSetForModel(modelDefinition: any): string[] {
|
|
170
|
+
// fields that are explicitly part of the graphql schema; not
|
|
171
|
+
// inferred from owner auth rules.
|
|
165
172
|
const { fields } = modelDefinition;
|
|
166
|
-
|
|
173
|
+
const explicitFields = Object.values<any>(fields)
|
|
167
174
|
.map(({ type, name }) => typeof type === 'string' && name) // Default selection set omits model fields
|
|
168
175
|
.filter(Boolean);
|
|
176
|
+
|
|
177
|
+
// fields used for owner auth rules that may or may not also
|
|
178
|
+
// be explicit on the model.
|
|
179
|
+
const ownerFields = resolveOwnerFields(modelDefinition);
|
|
180
|
+
|
|
181
|
+
return Array.from(new Set(explicitFields.concat(ownerFields)));
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
const FIELD_IR = '';
|
|
@@ -195,78 +208,70 @@ export function customSelectionSetToIR(
|
|
|
195
208
|
selectionSet: string[]
|
|
196
209
|
): Record<string, string | object> {
|
|
197
210
|
const modelDefinition = modelIntrospection[modelName];
|
|
198
|
-
const { fields } = modelDefinition;
|
|
211
|
+
const { fields: modelFields } = modelDefinition;
|
|
199
212
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
for (const f of selectionSet) {
|
|
203
|
-
const nested = f.includes('.');
|
|
213
|
+
return selectionSet.reduce((resultObj, path) => {
|
|
214
|
+
const [fieldName, nested, ...rest] = path.split('.');
|
|
204
215
|
|
|
205
216
|
if (nested) {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
const relatedModel = fields[modelFieldName]?.type?.model;
|
|
217
|
+
const relatedModel = modelFields[fieldName]?.type?.model;
|
|
209
218
|
|
|
210
219
|
if (!relatedModel) {
|
|
211
220
|
// TODO: may need to change this to support custom types
|
|
212
|
-
throw Error(`${
|
|
221
|
+
throw Error(`${fieldName} is not a model field`);
|
|
213
222
|
}
|
|
214
223
|
|
|
215
|
-
|
|
216
|
-
const relatedModelDefinition = modelIntrospection[relatedModel];
|
|
224
|
+
const relatedModelDefinition = modelIntrospection[relatedModel];
|
|
217
225
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
const selectionSet =
|
|
227
|
+
nested === SELECTION_SET_WILDCARD
|
|
228
|
+
? defaultSelectionSetIR(relatedModelDefinition)
|
|
229
|
+
: // if we have a path like 'field.anotherField' recursively build up selection set IR
|
|
230
|
+
customSelectionSetToIR(modelIntrospection, relatedModel, [
|
|
231
|
+
[nested, ...rest].join('.'),
|
|
232
|
+
]);
|
|
221
233
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
234
|
+
if (modelFields[fieldName]?.isArray) {
|
|
235
|
+
const existing = resultObj[fieldName] || {
|
|
236
|
+
items: {},
|
|
237
|
+
};
|
|
238
|
+
const merged = { ...existing.items, ...selectionSet };
|
|
226
239
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
};
|
|
231
|
-
} else {
|
|
232
|
-
intermediateSelectionSet[modelFieldName] = reduced;
|
|
233
|
-
}
|
|
234
|
-
} else {
|
|
235
|
-
const getNestedSelSet = customSelectionSetToIR(
|
|
236
|
-
modelIntrospection,
|
|
237
|
-
relatedModel,
|
|
238
|
-
[selectedField]
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
if (fields[modelFieldName]?.isArray) {
|
|
242
|
-
const existing = (intermediateSelectionSet as any)[
|
|
243
|
-
modelFieldName
|
|
244
|
-
] || { items: {} };
|
|
245
|
-
const merged = { ...existing.items, ...getNestedSelSet };
|
|
240
|
+
resultObj[fieldName] = { items: merged };
|
|
241
|
+
return resultObj;
|
|
242
|
+
}
|
|
246
243
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const existingItems =
|
|
250
|
-
(intermediateSelectionSet as any)[modelFieldName] || {};
|
|
251
|
-
const merged = { ...existingItems, ...getNestedSelSet };
|
|
244
|
+
const existingItems = resultObj[fieldName] || {};
|
|
245
|
+
const merged = { ...existingItems, ...selectionSet };
|
|
252
246
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
} else {
|
|
257
|
-
const exists = Boolean(fields[f]);
|
|
247
|
+
resultObj[fieldName] = merged;
|
|
248
|
+
return resultObj;
|
|
249
|
+
}
|
|
258
250
|
|
|
259
|
-
|
|
260
|
-
throw Error(`${f} is not a field of model ${modelName}`);
|
|
261
|
-
}
|
|
251
|
+
const exists = Boolean(modelFields[fieldName]);
|
|
262
252
|
|
|
263
|
-
|
|
253
|
+
if (!exists) {
|
|
254
|
+
throw Error(`${fieldName} is not a field of model ${modelName}`);
|
|
264
255
|
}
|
|
265
|
-
}
|
|
266
256
|
|
|
267
|
-
|
|
257
|
+
resultObj[fieldName] = FIELD_IR;
|
|
258
|
+
return resultObj;
|
|
259
|
+
}, {});
|
|
268
260
|
}
|
|
269
261
|
|
|
262
|
+
const defaultSelectionSetIR = relatedModelDefinition => {
|
|
263
|
+
const defaultSelectionSet = defaultSelectionSetForModel(
|
|
264
|
+
relatedModelDefinition
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const reduced = defaultSelectionSet.reduce((acc, curVal) => {
|
|
268
|
+
acc[curVal] = FIELD_IR;
|
|
269
|
+
return acc;
|
|
270
|
+
}, {});
|
|
271
|
+
|
|
272
|
+
return reduced;
|
|
273
|
+
};
|
|
274
|
+
|
|
270
275
|
/**
|
|
271
276
|
* Stringifies selection set IR
|
|
272
277
|
* * @example
|
|
@@ -365,7 +370,7 @@ export function generateGraphQLDocument(
|
|
|
365
370
|
}
|
|
366
371
|
|
|
367
372
|
const graphQLFieldName = `${operationPrefix}${usePlural ? pluralName : name}`;
|
|
368
|
-
let graphQLOperationType: 'mutation' | 'query' | undefined;
|
|
373
|
+
let graphQLOperationType: 'mutation' | 'query' | 'subscription' | undefined;
|
|
369
374
|
let graphQLSelectionSet: string | undefined;
|
|
370
375
|
let graphQLArguments: Record<string, any> | undefined;
|
|
371
376
|
|
|
@@ -410,6 +415,15 @@ export function generateGraphQLDocument(
|
|
|
410
415
|
graphQLOperationType ?? (graphQLOperationType = 'query');
|
|
411
416
|
graphQLSelectionSet ??
|
|
412
417
|
(graphQLSelectionSet = `items { ${selectionSetFields} }`);
|
|
418
|
+
case 'ONCREATE':
|
|
419
|
+
case 'ONUPDATE':
|
|
420
|
+
case 'ONDELETE':
|
|
421
|
+
graphQLArguments ??
|
|
422
|
+
(graphQLArguments = {
|
|
423
|
+
filter: `ModelSubscription${name}FilterInput`,
|
|
424
|
+
});
|
|
425
|
+
graphQLOperationType ?? (graphQLOperationType = 'subscription');
|
|
426
|
+
graphQLSelectionSet ?? (graphQLSelectionSet = selectionSetFields);
|
|
413
427
|
}
|
|
414
428
|
|
|
415
429
|
const graphQLDocument = `${graphQLOperationType}${
|
|
@@ -488,6 +502,9 @@ export function buildGraphQLVariables(
|
|
|
488
502
|
}
|
|
489
503
|
break;
|
|
490
504
|
case 'LIST':
|
|
505
|
+
case 'ONCREATE':
|
|
506
|
+
case 'ONUPDATE':
|
|
507
|
+
case 'ONDELETE':
|
|
491
508
|
if (arg?.filter) {
|
|
492
509
|
variables = { filter: arg.filter };
|
|
493
510
|
}
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
flattenItems,
|
|
11
11
|
} from './APIClient';
|
|
12
12
|
import { ClientGenerationParams } from './types';
|
|
13
|
-
import { V6Client } from '../types';
|
|
13
|
+
import { V6Client, GraphqlSubscriptionResult } from '../types';
|
|
14
|
+
import { Observable, map } from 'rxjs';
|
|
14
15
|
|
|
15
16
|
export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
16
17
|
client: V6Client,
|
|
@@ -102,6 +103,32 @@ export function generateModelsProperty<T extends Record<any, any> = never>(
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
};
|
|
106
|
+
} else if (['ONCREATE', 'ONUPDATE', 'ONDELETE'].includes(operation)) {
|
|
107
|
+
models[name][operationPrefix] = (arg?: any, options?: any) => {
|
|
108
|
+
const query = generateGraphQLDocument(
|
|
109
|
+
modelIntrospection.models,
|
|
110
|
+
name,
|
|
111
|
+
operation
|
|
112
|
+
);
|
|
113
|
+
const variables = buildGraphQLVariables(
|
|
114
|
+
model,
|
|
115
|
+
operation,
|
|
116
|
+
arg,
|
|
117
|
+
modelIntrospection
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const observable = client.graphql({
|
|
121
|
+
query,
|
|
122
|
+
variables,
|
|
123
|
+
}) as GraphqlSubscriptionResult<object>;
|
|
124
|
+
|
|
125
|
+
return observable.pipe(
|
|
126
|
+
map(value => {
|
|
127
|
+
const [key] = Object.keys(value.data);
|
|
128
|
+
return value.data[key];
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
};
|
|
105
132
|
} else {
|
|
106
133
|
models[name][operationPrefix] = async (arg?: any, options?: any) => {
|
|
107
134
|
const query = generateGraphQLDocument(
|
|
@@ -0,0 +1,65 @@
|
|
|
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 Model = ModelIntrospectionSchema['models'][string];
|
|
11
|
+
|
|
12
|
+
type AuthAttribute = {
|
|
13
|
+
type: 'auth';
|
|
14
|
+
properties: {
|
|
15
|
+
rules: AuthRule[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Only the portions of an Auth rule we care about.
|
|
21
|
+
*/
|
|
22
|
+
type AuthRule = {
|
|
23
|
+
allow: string;
|
|
24
|
+
ownerField?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Given an introspection schema model, returns all owner fields.
|
|
29
|
+
*
|
|
30
|
+
* @param model Model from an introspection schema
|
|
31
|
+
* @returns List of owner field names
|
|
32
|
+
*/
|
|
33
|
+
export function resolveOwnerFields(model: Model): string[] {
|
|
34
|
+
const ownerFields = new Set<string>();
|
|
35
|
+
for (const attr of model.attributes || []) {
|
|
36
|
+
if (isAuthAttribute(attr)) {
|
|
37
|
+
for (const rule of attr.properties.rules) {
|
|
38
|
+
if (rule.allow === 'owner') {
|
|
39
|
+
ownerFields.add(rule.ownerField || 'owner');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return Array.from(ownerFields);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Type guard that identifies an auth attribute with an attached rules list that
|
|
49
|
+
* specifies an `allow` attribute at a minimum.
|
|
50
|
+
*
|
|
51
|
+
* @param attribute Any object. Ideally a model introspection schema model attribute
|
|
52
|
+
* @returns True if given object is an auth attribute
|
|
53
|
+
*/
|
|
54
|
+
function isAuthAttribute(attribute: any): attribute is AuthAttribute {
|
|
55
|
+
if (attribute?.type === 'auth') {
|
|
56
|
+
if (typeof attribute?.properties === 'object') {
|
|
57
|
+
if (Array.isArray(attribute?.properties?.rules)) {
|
|
58
|
+
return (attribute?.properties?.rules as Array<any>).every(
|
|
59
|
+
rule => !!rule.allow
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|