@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.
@@ -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.572a7b5.0+572a7b5",
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.572a7b5.0+572a7b5",
56
- "@aws-amplify/auth": "6.0.1-api-v6-models.572a7b5.0+572a7b5",
57
- "@aws-amplify/core": "6.0.1-api-v6-models.572a7b5.0+572a7b5",
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": "572a7b56d9e87ca779038254de3d68b5c607af07"
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 SELECTION_SET_ALL_NESTED = '*';
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
- return Object.values<any>(fields)
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
- const intermediateSelectionSet: Record<string, string | object> = {};
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 [modelFieldName, selectedField] = f.split('.');
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(`${modelFieldName} is not a model field`);
221
+ throw Error(`${fieldName} is not a model field`);
213
222
  }
214
223
 
215
- if (selectedField === SELECTION_SET_ALL_NESTED) {
216
- const relatedModelDefinition = modelIntrospection[relatedModel];
224
+ const relatedModelDefinition = modelIntrospection[relatedModel];
217
225
 
218
- const defaultSelectionSet = defaultSelectionSetForModel(
219
- relatedModelDefinition
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
- const reduced = defaultSelectionSet.reduce((acc, curVal) => {
223
- acc[curVal] = FIELD_IR;
224
- return acc;
225
- }, {});
234
+ if (modelFields[fieldName]?.isArray) {
235
+ const existing = resultObj[fieldName] || {
236
+ items: {},
237
+ };
238
+ const merged = { ...existing.items, ...selectionSet };
226
239
 
227
- if (fields[modelFieldName]?.isArray) {
228
- intermediateSelectionSet[modelFieldName] = {
229
- items: reduced,
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
- intermediateSelectionSet[modelFieldName] = { items: merged };
248
- } else {
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
- intermediateSelectionSet[modelFieldName] = merged;
254
- }
255
- }
256
- } else {
257
- const exists = Boolean(fields[f]);
247
+ resultObj[fieldName] = merged;
248
+ return resultObj;
249
+ }
258
250
 
259
- if (!exists) {
260
- throw Error(`${f} is not a field of model ${modelName}`);
261
- }
251
+ const exists = Boolean(modelFields[fieldName]);
262
252
 
263
- intermediateSelectionSet[f] = FIELD_IR;
253
+ if (!exists) {
254
+ throw Error(`${fieldName} is not a field of model ${modelName}`);
264
255
  }
265
- }
266
256
 
267
- return intermediateSelectionSet;
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
+ }