@aws-amplify/data-schema 0.13.6 → 0.13.8
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-esm/src/CustomOperation.d.ts +4 -5
- package/lib-esm/src/CustomOperation.js +3 -1
- package/lib-esm/src/CustomType.d.ts +5 -3
- package/lib-esm/src/Handler.d.ts +43 -18
- package/lib-esm/src/Handler.js +22 -9
- package/lib-esm/src/MappedTypes/ExtractNonModelTypes.d.ts +51 -13
- package/lib-esm/src/MappedTypes/ResolveFieldProperties.d.ts +15 -10
- package/lib-esm/src/MappedTypes/ResolveSchema.d.ts +27 -2
- package/lib-esm/src/SchemaProcessor.d.ts +1 -1
- package/lib-esm/src/SchemaProcessor.js +214 -20
- package/lib-esm/src/util/Brand.d.ts +6 -0
- package/lib-esm/src/util/Brand.js +10 -1
- package/lib-esm/src/util/index.d.ts +1 -1
- package/lib-esm/src/util/index.js +2 -1
- package/lib-esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/lib-esm/src/types.d.ts +0 -18
- package/lib-esm/src/types.js +0 -8
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.processSchema = void 0;
|
|
4
27
|
const ModelField_1 = require("./ModelField");
|
|
5
28
|
const ModelRelationalField_1 = require("./ModelRelationalField");
|
|
6
29
|
const Authorization_1 = require("./Authorization");
|
|
7
30
|
const CustomOperation_1 = require("./CustomOperation");
|
|
31
|
+
const util_1 = require("./util");
|
|
32
|
+
const Handler_1 = require("./Handler");
|
|
33
|
+
const os = __importStar(require("os"));
|
|
34
|
+
const path = __importStar(require("path"));
|
|
8
35
|
function isInternalModel(model) {
|
|
9
36
|
if (model.data &&
|
|
10
37
|
!isCustomType(model) &&
|
|
@@ -41,13 +68,18 @@ function isRefFieldDef(data) {
|
|
|
41
68
|
return data?.type === 'ref';
|
|
42
69
|
}
|
|
43
70
|
function isModelField(field) {
|
|
44
|
-
return isModelFieldDef(field
|
|
71
|
+
return isModelFieldDef(field?.data);
|
|
72
|
+
}
|
|
73
|
+
function dataSourceIsRef(dataSource) {
|
|
74
|
+
return (typeof dataSource !== 'string' &&
|
|
75
|
+
dataSource?.data &&
|
|
76
|
+
dataSource.data.type === 'ref');
|
|
45
77
|
}
|
|
46
78
|
function isScalarField(field) {
|
|
47
|
-
return isScalarFieldDef(field
|
|
79
|
+
return isScalarFieldDef(field?.data);
|
|
48
80
|
}
|
|
49
81
|
function isRefField(field) {
|
|
50
|
-
return isRefFieldDef(field
|
|
82
|
+
return isRefFieldDef(field?.data);
|
|
51
83
|
}
|
|
52
84
|
function scalarFieldToGql(fieldDef, identifier, secondaryIndexes = []) {
|
|
53
85
|
const { fieldType, required, array, arrayRequired, default: _default, } = fieldDef;
|
|
@@ -123,21 +155,23 @@ function refFieldToGql(fieldDef) {
|
|
|
123
155
|
// }
|
|
124
156
|
return field;
|
|
125
157
|
}
|
|
126
|
-
function customOperationToGql(typeName, typeDef, authorization) {
|
|
158
|
+
function customOperationToGql(typeName, typeDef, authorization, isCustom = false) {
|
|
127
159
|
const { arguments: fieldArgs, returnType, functionRef } = typeDef.data;
|
|
128
160
|
let callSignature = typeName;
|
|
129
161
|
const implicitModels = [];
|
|
130
|
-
const { authString } =
|
|
162
|
+
const { authString } = isCustom
|
|
163
|
+
? calculateCustomAuth(authorization)
|
|
164
|
+
: calculateAuth(authorization);
|
|
131
165
|
let returnTypeName;
|
|
132
166
|
if (isRefField(returnType)) {
|
|
133
|
-
returnTypeName = refFieldToGql(returnType
|
|
167
|
+
returnTypeName = refFieldToGql(returnType?.data);
|
|
134
168
|
}
|
|
135
169
|
else if (isCustomType(returnType)) {
|
|
136
170
|
returnTypeName = `${capitalize(typeName)}ReturnType`;
|
|
137
171
|
implicitModels.push([returnTypeName, returnType]);
|
|
138
172
|
}
|
|
139
173
|
else if (isScalarField(returnType)) {
|
|
140
|
-
returnTypeName = scalarFieldToGql(returnType
|
|
174
|
+
returnTypeName = scalarFieldToGql(returnType?.data);
|
|
141
175
|
}
|
|
142
176
|
else {
|
|
143
177
|
throw new Error(`Unrecognized return type on ${typeName}`);
|
|
@@ -282,6 +316,77 @@ function calculateAuth(authorization) {
|
|
|
282
316
|
const authString = rules.length > 0 ? `@auth(rules: [${rules.join(',\n ')}])` : '';
|
|
283
317
|
return { authString, authFields };
|
|
284
318
|
}
|
|
319
|
+
function validateCustomAuthRule(rule) {
|
|
320
|
+
if (rule.operations) {
|
|
321
|
+
throw new Error('.to() modifier is not supported for custom queries/mutations');
|
|
322
|
+
}
|
|
323
|
+
if (rule.groupOrOwnerField) {
|
|
324
|
+
throw new Error('Dynamic auth (owner or dynamic groups) is not supported for custom queries/mutations');
|
|
325
|
+
}
|
|
326
|
+
// identityClaim
|
|
327
|
+
if (rule.identityClaim) {
|
|
328
|
+
throw new Error('identityClaim attr is not supported with a.handler.custom');
|
|
329
|
+
}
|
|
330
|
+
// groupClaim
|
|
331
|
+
if (rule.groupClaim) {
|
|
332
|
+
throw new Error('groupClaim attr is not supported with a.handler.custom');
|
|
333
|
+
}
|
|
334
|
+
if (rule.groups && rule.provider === 'oidc') {
|
|
335
|
+
throw new Error('OIDC group auth is not supported with a.handler.custom');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function getCustomAuthProvider(rule) {
|
|
339
|
+
const strategyDict = {
|
|
340
|
+
public: {
|
|
341
|
+
default: '@aws_api_key',
|
|
342
|
+
apiKey: '@aws_api_key',
|
|
343
|
+
iam: '@aws_iam',
|
|
344
|
+
},
|
|
345
|
+
private: {
|
|
346
|
+
default: '@aws_cognito_user_pools',
|
|
347
|
+
userPools: '@aws_cognito_user_pools',
|
|
348
|
+
oidc: '@aws_oidc',
|
|
349
|
+
iam: '@aws_iam',
|
|
350
|
+
},
|
|
351
|
+
groups: {
|
|
352
|
+
default: '@aws_cognito_user_pools',
|
|
353
|
+
userPools: '@aws_cognito_user_pools',
|
|
354
|
+
},
|
|
355
|
+
custom: {
|
|
356
|
+
default: '@aws_lambda',
|
|
357
|
+
function: '@aws_lambda',
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
const stratProviders = strategyDict[rule.strategy];
|
|
361
|
+
if (stratProviders === undefined) {
|
|
362
|
+
throw new Error(`Unsupported auth strategy for custom handlers: ${rule.strategy}`);
|
|
363
|
+
}
|
|
364
|
+
const provider = rule.provider || 'default';
|
|
365
|
+
const stratProvider = stratProviders[provider];
|
|
366
|
+
if (stratProvider === undefined) {
|
|
367
|
+
throw new Error(`Unsupported provider for custom handlers: ${rule.provider}`);
|
|
368
|
+
}
|
|
369
|
+
return stratProvider;
|
|
370
|
+
}
|
|
371
|
+
function calculateCustomAuth(authorization) {
|
|
372
|
+
const rules = [];
|
|
373
|
+
for (const entry of authorization) {
|
|
374
|
+
const rule = (0, Authorization_1.accessData)(entry);
|
|
375
|
+
validateCustomAuthRule(rule);
|
|
376
|
+
const provider = getCustomAuthProvider(rule);
|
|
377
|
+
if (rule.groups) {
|
|
378
|
+
// example: (cognito_groups: ["Bloggers", "Readers"])
|
|
379
|
+
rules.push(`${provider}(cognito_groups: [${rule.groups
|
|
380
|
+
.map((group) => `"${group}"`)
|
|
381
|
+
.join(', ')}])`);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
rules.push(provider);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const authString = rules.join(' ');
|
|
388
|
+
return { authString };
|
|
389
|
+
}
|
|
285
390
|
function capitalize(s) {
|
|
286
391
|
return `${s[0].toUpperCase()}${s.slice(1)}`;
|
|
287
392
|
}
|
|
@@ -521,6 +626,7 @@ const schemaPreprocessor = (schema) => {
|
|
|
521
626
|
const customQueries = [];
|
|
522
627
|
const customMutations = [];
|
|
523
628
|
const customSubscriptions = [];
|
|
629
|
+
const jsFunctions = [];
|
|
524
630
|
const fkFields = allImpliedFKs(schema);
|
|
525
631
|
const topLevelTypes = Object.entries(schema.data.types);
|
|
526
632
|
for (const [typeName, typeDef] of topLevelTypes) {
|
|
@@ -537,9 +643,10 @@ const schemaPreprocessor = (schema) => {
|
|
|
537
643
|
}
|
|
538
644
|
else if (isCustomType(typeDef)) {
|
|
539
645
|
const fields = typeDef.data.fields;
|
|
646
|
+
const fieldAuthApplicableFields = Object.fromEntries(Object.entries(fields).filter((pair) => isModelField(pair[1])));
|
|
540
647
|
const authString = '';
|
|
541
648
|
const authFields = {};
|
|
542
|
-
const fieldLevelAuthRules = processFieldLevelAuthRules(
|
|
649
|
+
const fieldLevelAuthRules = processFieldLevelAuthRules(fieldAuthApplicableFields, authFields);
|
|
543
650
|
const { gqlFields, models } = processFields(typeName, fields, fieldLevelAuthRules);
|
|
544
651
|
topLevelTypes.push(...models);
|
|
545
652
|
const joined = gqlFields.join('\n ');
|
|
@@ -548,16 +655,11 @@ const schemaPreprocessor = (schema) => {
|
|
|
548
655
|
}
|
|
549
656
|
else if (isCustomOperation(typeDef)) {
|
|
550
657
|
const { typeName: opType } = typeDef.data;
|
|
551
|
-
|
|
552
|
-
(typeDef.data.functionRef && mostRelevantAuthRules.length < 1)) {
|
|
553
|
-
// Deploying a custom operation with auth and no handler reference OR
|
|
554
|
-
// with a handler reference but not auth
|
|
555
|
-
// causes the CFN stack to reach an unrecoverable state. Ideally, this should be fixed
|
|
556
|
-
// in the CDK construct, but we're catching it early here as a stopgap
|
|
557
|
-
throw new Error(`Custom operation ${typeName} requires both an authorization rule and a handler reference`);
|
|
558
|
-
}
|
|
559
|
-
const { gqlField, models } = customOperationToGql(typeName, typeDef, mostRelevantAuthRules);
|
|
658
|
+
const { gqlField, models, jsFunctionForField } = transformCustomOperations(typeDef, typeName, mostRelevantAuthRules);
|
|
560
659
|
topLevelTypes.push(...models);
|
|
660
|
+
if (jsFunctionForField) {
|
|
661
|
+
jsFunctions.push(jsFunctionForField);
|
|
662
|
+
}
|
|
561
663
|
switch (opType) {
|
|
562
664
|
case 'Query':
|
|
563
665
|
customQueries.push(gqlField);
|
|
@@ -604,8 +706,100 @@ const schemaPreprocessor = (schema) => {
|
|
|
604
706
|
};
|
|
605
707
|
gqlModels.push(...generateCustomOperationTypes(customOperations));
|
|
606
708
|
const processedSchema = gqlModels.join('\n\n');
|
|
607
|
-
return processedSchema;
|
|
709
|
+
return { schema: processedSchema, jsFunctions };
|
|
710
|
+
};
|
|
711
|
+
function validateCustomOperations(typeDef, typeName, authRules) {
|
|
712
|
+
const { functionRef, handlers } = typeDef.data;
|
|
713
|
+
// TODO: remove `functionRef` after deprecating
|
|
714
|
+
const handlerConfigured = functionRef !== null || handlers?.length;
|
|
715
|
+
const authConfigured = authRules.length > 0;
|
|
716
|
+
if ((authConfigured && !handlerConfigured) ||
|
|
717
|
+
(handlerConfigured && !authConfigured)) {
|
|
718
|
+
// Deploying a custom operation with auth and no handler reference OR
|
|
719
|
+
// with a handler reference but no auth
|
|
720
|
+
// causes the CFN stack to reach an unrecoverable state. Ideally, this should be fixed
|
|
721
|
+
// in the CDK construct, but we're catching it early here as a stopgap
|
|
722
|
+
throw new Error(`Custom operation ${typeName} requires both an authorization rule and a handler reference`);
|
|
723
|
+
}
|
|
724
|
+
// Handlers must all be of the same type
|
|
725
|
+
if (handlers?.length) {
|
|
726
|
+
const configuredHandlers = new Set();
|
|
727
|
+
for (const handler of handlers) {
|
|
728
|
+
configuredHandlers.add((0, util_1.getBrand)(handler));
|
|
729
|
+
}
|
|
730
|
+
if (configuredHandlers.size > 1) {
|
|
731
|
+
const configuredHandlersStr = JSON.stringify(Array.from(configuredHandlers));
|
|
732
|
+
throw new Error(`Field handlers must be of the same type. ${typeName} has been configured with ${configuredHandlersStr}`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const isCustomHandler = (handler) => {
|
|
737
|
+
return Array.isArray(handler) && (0, util_1.getBrand)(handler[0]) === 'customHandler';
|
|
738
|
+
};
|
|
739
|
+
const normalizeDataSourceName = (dataSource) => {
|
|
740
|
+
// default data source
|
|
741
|
+
const noneDataSourceName = 'NONE_DS';
|
|
742
|
+
if (dataSource === undefined) {
|
|
743
|
+
return noneDataSourceName;
|
|
744
|
+
}
|
|
745
|
+
if (dataSourceIsRef(dataSource)) {
|
|
746
|
+
return `${dataSource.data.link}Table`;
|
|
747
|
+
}
|
|
748
|
+
return dataSource;
|
|
749
|
+
};
|
|
750
|
+
const sanitizeStackTrace = (stackTrace) => {
|
|
751
|
+
// normalize EOL to \n so that parsing is consistent across platforms
|
|
752
|
+
const normalizedStackTrace = stackTrace.replaceAll(os.EOL, '\n');
|
|
753
|
+
return (normalizedStackTrace
|
|
754
|
+
.split('\n')
|
|
755
|
+
.map((line) => line.trim())
|
|
756
|
+
// filters out noise not relevant to the stack trace. All stack trace lines begin with 'at'
|
|
757
|
+
.filter((line) => line.startsWith('at')) || []);
|
|
608
758
|
};
|
|
759
|
+
// copied from the defineFunction path resolution impl:
|
|
760
|
+
// https://github.com/aws-amplify/amplify-backend/blob/main/packages/backend-function/src/get_caller_directory.ts
|
|
761
|
+
const resolveCustomHandlerEntryPath = (data) => {
|
|
762
|
+
if (path.isAbsolute(data.entry)) {
|
|
763
|
+
return data.entry;
|
|
764
|
+
}
|
|
765
|
+
const unresolvedImportLocationError = new Error('Could not determine import path to construct absolute code path for custom handler. Consider using an absolute path instead.');
|
|
766
|
+
if (!data.stack) {
|
|
767
|
+
throw unresolvedImportLocationError;
|
|
768
|
+
}
|
|
769
|
+
const stackTraceLines = sanitizeStackTrace(data.stack);
|
|
770
|
+
if (stackTraceLines.length < 2) {
|
|
771
|
+
throw unresolvedImportLocationError;
|
|
772
|
+
}
|
|
773
|
+
const stackTraceImportLine = stackTraceLines[1]; // the first entry is the file where the error was initialized (our code). The second entry is where the customer called our code which is what we are interested in
|
|
774
|
+
// if entry is relative, compute with respect to the caller directory
|
|
775
|
+
return { relativePath: data.entry, importLine: stackTraceImportLine };
|
|
776
|
+
};
|
|
777
|
+
const handleCustom = (handlers, opType, typeName) => {
|
|
778
|
+
const transformedHandlers = handlers.map((handler) => {
|
|
779
|
+
const handlerData = (0, Handler_1.getHandlerData)(handler);
|
|
780
|
+
return {
|
|
781
|
+
dataSource: normalizeDataSourceName(handlerData.dataSource),
|
|
782
|
+
entry: resolveCustomHandlerEntryPath(handlerData),
|
|
783
|
+
};
|
|
784
|
+
});
|
|
785
|
+
const jsFn = {
|
|
786
|
+
typeName: opType,
|
|
787
|
+
fieldName: typeName,
|
|
788
|
+
handlers: transformedHandlers,
|
|
789
|
+
};
|
|
790
|
+
return jsFn;
|
|
791
|
+
};
|
|
792
|
+
function transformCustomOperations(typeDef, typeName, authRules) {
|
|
793
|
+
const { typeName: opType, handlers } = typeDef.data;
|
|
794
|
+
let jsFunctionForField = undefined;
|
|
795
|
+
validateCustomOperations(typeDef, typeName, authRules);
|
|
796
|
+
if (isCustomHandler(handlers)) {
|
|
797
|
+
jsFunctionForField = handleCustom(handlers, opType, typeName);
|
|
798
|
+
}
|
|
799
|
+
const isCustom = Boolean(jsFunctionForField);
|
|
800
|
+
const { gqlField, models } = customOperationToGql(typeName, typeDef, authRules, isCustom);
|
|
801
|
+
return { gqlField, models, jsFunctionForField };
|
|
802
|
+
}
|
|
609
803
|
function generateCustomOperationTypes({ queries, mutations, subscriptions, }) {
|
|
610
804
|
const types = [];
|
|
611
805
|
if (mutations.length > 0) {
|
|
@@ -625,7 +819,7 @@ function generateCustomOperationTypes({ queries, mutations, subscriptions, }) {
|
|
|
625
819
|
* @returns DerivedApiDefinition that conforms to IAmplifyGraphqlDefinition
|
|
626
820
|
*/
|
|
627
821
|
function processSchema(arg) {
|
|
628
|
-
const schema = schemaPreprocessor(arg.schema);
|
|
629
|
-
return { schema, functionSlots: [] };
|
|
822
|
+
const { schema, jsFunctions } = schemaPreprocessor(arg.schema);
|
|
823
|
+
return { schema, functionSlots: [], jsFunctions };
|
|
630
824
|
}
|
|
631
825
|
exports.processSchema = processSchema;
|
|
@@ -26,4 +26,10 @@ export type Brand<BrandStr extends string> = {
|
|
|
26
26
|
* const myType = {content: "default content", ...brand<'example'>}
|
|
27
27
|
*/
|
|
28
28
|
export declare function brand<BrandStr extends string>(brand: BrandStr): Brand<BrandStr>;
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param branded: Branded object
|
|
32
|
+
* @returns The string brand value
|
|
33
|
+
*/
|
|
34
|
+
export declare function getBrand(branded: Brand<string>): string;
|
|
29
35
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.brand = void 0;
|
|
3
|
+
exports.getBrand = exports.brand = void 0;
|
|
4
4
|
const brandSymbol = Symbol('brand');
|
|
5
5
|
/**
|
|
6
6
|
* Create an object of a specific type Brand
|
|
@@ -21,3 +21,12 @@ function brand(brand) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
exports.brand = brand;
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @param branded: Branded object
|
|
27
|
+
* @returns The string brand value
|
|
28
|
+
*/
|
|
29
|
+
function getBrand(branded) {
|
|
30
|
+
return branded[brandSymbol];
|
|
31
|
+
}
|
|
32
|
+
exports.getBrand = getBrand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Brand, brand } from './Brand';
|
|
1
|
+
export { Brand, brand, getBrand } from './Brand';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.brand = void 0;
|
|
3
|
+
exports.getBrand = exports.brand = void 0;
|
|
4
4
|
var Brand_1 = require("./Brand");
|
|
5
5
|
Object.defineProperty(exports, "brand", { enumerable: true, get: function () { return Brand_1.brand; } });
|
|
6
|
+
Object.defineProperty(exports, "getBrand", { enumerable: true, get: function () { return Brand_1.getBrand; } });
|