@aws-amplify/data-schema
Advanced tools
Comparing version 0.13.17 to 0.13.18
@@ -14,2 +14,4 @@ import { SetTypeSubArg } from '@aws-amplify/data-schema-types'; | ||
type CustomArguments = Record<string, ModelField<any, any> | EnumType<EnumTypeParamShape>>; | ||
type SubscriptionSource = RefType<any, any>; | ||
type InternalSubscriptionSource = InternalRef; | ||
type CustomReturnType = RefType<any> | CustomType<any>; | ||
@@ -29,2 +31,3 @@ type CustomFunctionRefType = string; | ||
handlers: Handler[] | null; | ||
subscriptionSource: SubscriptionSource[]; | ||
}; | ||
@@ -35,2 +38,3 @@ type InternalCustomData = CustomData & { | ||
functionRef: string | null; | ||
subscriptionSource: InternalSubscriptionSource[]; | ||
authorization: Authorization<any, any, any>[]; | ||
@@ -60,2 +64,3 @@ }; | ||
handler<H extends HandlerInputType>(handlers: H): CustomOperation<T, K | 'handler', B>; | ||
for(source: SubscriptionSource | SubscriptionSource[]): CustomOperation<T, K | 'for', B>; | ||
}, K> & Brand<B>; | ||
@@ -62,0 +67,0 @@ /** |
@@ -24,2 +24,3 @@ "use strict"; | ||
handlers: null, | ||
subscriptionSource: [], | ||
}; | ||
@@ -49,2 +50,6 @@ const builder = brandedBuilder({ | ||
}, | ||
for(source) { | ||
data.subscriptionSource = Array.isArray(source) ? source : [source]; | ||
return this; | ||
}, | ||
}, brand); | ||
@@ -51,0 +56,0 @@ return { ...builder, data }; |
@@ -26,3 +26,3 @@ import type { DefineFunction } from '@aws-amplify/data-schema-types'; | ||
*/ | ||
dataSource?: string | RefType<any, any, any>; | ||
dataSource?: string | RefType<any>; | ||
/** | ||
@@ -29,0 +29,0 @@ * The path to the file that contains the function entry point. |
@@ -12,2 +12,3 @@ import { SetTypeSubArg } from '@aws-amplify/data-schema-types'; | ||
arrayRequired: boolean; | ||
mutationOperations: MutationOperations[]; | ||
authorization: Authorization<any, any, any>[]; | ||
@@ -23,2 +24,3 @@ }; | ||
}; | ||
type MutationOperations = 'create' | 'update' | 'delete'; | ||
export type RefType<T extends RefTypeParamShape, K extends keyof RefType<T> = never, Auth = undefined> = Omit<{ | ||
@@ -38,2 +40,3 @@ /** | ||
authorization<AuthRuleType extends Authorization<any, any, any>>(rules: AuthRuleType[]): RefType<T, K | 'authorization', AuthRuleType>; | ||
mutations(operations: MutationOperations[]): RefType<T, K | 'mutations'>; | ||
}, K> & { | ||
@@ -40,0 +43,0 @@ [__auth]?: Auth; |
@@ -16,2 +16,3 @@ "use strict"; | ||
arrayRequired: false, | ||
mutationOperations: [], | ||
authorization: [], | ||
@@ -37,2 +38,6 @@ }; | ||
}, | ||
mutations(operations) { | ||
data.mutationOperations = operations; | ||
return this; | ||
}, | ||
}); | ||
@@ -39,0 +44,0 @@ return { ...builder, data }; |
@@ -179,4 +179,4 @@ "use strict"; | ||
} | ||
function customOperationToGql(typeName, typeDef, authorization, isCustom = false, databaseType) { | ||
const { arguments: fieldArgs, returnType, functionRef, handlers, } = typeDef.data; | ||
function customOperationToGql(typeName, typeDef, authorization, isCustom = false, databaseType, getRefType) { | ||
const { arguments: fieldArgs, typeName: opType, returnType, functionRef, handlers, subscriptionSource, } = typeDef.data; | ||
let callSignature = typeName; | ||
@@ -222,2 +222,17 @@ const implicitModels = []; | ||
} | ||
if (opType === 'Subscription') { | ||
const subscriptionSources = subscriptionSource | ||
.flatMap((source) => { | ||
const refTarget = source.data.link; | ||
const { type } = getRefType(refTarget, typeName); | ||
if (type === 'CustomOperation') { | ||
return refTarget; | ||
} | ||
if (type === 'Model') { | ||
return source.data.mutationOperations.map((op) => `${op}${refTarget}`); | ||
} | ||
}) | ||
.join('", "'); | ||
gqlHandlerContent += `@aws_subscribe(mutations: ["${subscriptionSources}"]) `; | ||
} | ||
const gqlField = `${callSignature}: ${returnTypeName} ${gqlHandlerContent}${authString}`; | ||
@@ -737,2 +752,27 @@ return { gqlField, models: implicitModels, lambdaFunctionDefinition }; | ||
}; | ||
/** | ||
* Returns a closure for retrieving reference type and definition from schema | ||
*/ | ||
const getRefTypeForSchema = (schema) => { | ||
const getRefType = (source, target) => { | ||
const typeDef = schema.data.types[source]; | ||
if (typeDef === undefined) { | ||
throw new Error(`Invalid ref. ${target} is referencing ${source} which is not defined in the schema`); | ||
} | ||
if (isInternalModel(typeDef)) { | ||
return { type: 'Model', def: typeDef }; | ||
} | ||
if (isCustomOperation(typeDef)) { | ||
return { type: 'CustomOperation', def: typeDef }; | ||
} | ||
if (isCustomType(typeDef)) { | ||
return { type: 'CustomType', def: typeDef }; | ||
} | ||
if (isEnumType(typeDef)) { | ||
return { type: 'Enum', def: typeDef }; | ||
} | ||
throw new Error(`Invalid ref. ${target} is referencing ${source} which is neither a Model, Custom Operation, Custom Type, or Enum`); | ||
}; | ||
return getRefType; | ||
}; | ||
const schemaPreprocessor = (schema) => { | ||
@@ -752,2 +792,3 @@ const gqlModels = []; | ||
const { schemaAuth, functionSchemaAccess } = extractFunctionSchemaAccess(schema.data.authorization); | ||
const getRefType = getRefTypeForSchema(schema); | ||
for (const [typeName, typeDef] of topLevelTypes) { | ||
@@ -780,3 +821,3 @@ validateAuth(typeDef.data?.authorization); | ||
const { typeName: opType } = typeDef.data; | ||
const { gqlField, models, jsFunctionForField, lambdaFunctionDefinition, } = transformCustomOperations(typeDef, typeName, mostRelevantAuthRules, databaseType); | ||
const { gqlField, models, jsFunctionForField, lambdaFunctionDefinition, } = transformCustomOperations(typeDef, typeName, mostRelevantAuthRules, databaseType, getRefType); | ||
lambdaFunctions = lambdaFunctionDefinition; | ||
@@ -857,4 +898,4 @@ topLevelTypes.push(...models); | ||
}; | ||
function validateCustomOperations(typeDef, typeName, authRules) { | ||
const { functionRef, handlers } = typeDef.data; | ||
function validateCustomOperations(typeDef, typeName, authRules, getRefType) { | ||
const { functionRef, handlers, typeName: opType, subscriptionSource, returnType, } = typeDef.data; | ||
// TODO: remove `functionRef` after deprecating | ||
@@ -882,2 +923,53 @@ const handlerConfigured = functionRef !== null || handlers?.length; | ||
} | ||
if (opType === 'Subscription') { | ||
if (subscriptionSource.length < 1) { | ||
throw new Error(`${typeName} is missing a mutation source. Custom subscriptions must reference a mutation source via subscription().for(a.ref('ModelOrOperationName')) `); | ||
} | ||
subscriptionSource.forEach((source) => { | ||
const sourceName = source.data.link; | ||
const { type, def } = getRefType(sourceName, typeName); | ||
if (type !== 'Model' && source.data.mutationOperations.length > 0) { | ||
throw new Error(`Invalid subscription definition. .mutations() modifier can only be used with a Model ref. ${typeName} is referencing ${type}`); | ||
} | ||
if (type === 'Model' && source.data.mutationOperations.length === 0) { | ||
throw new Error(`Invalid subscription definition. .mutations() modifier must be used with a Model ref subscription source. ${typeName} is referencing ${sourceName} without specifying a mutation`); | ||
} | ||
if (type === 'CustomOperation' && def.data.typeName !== 'Mutation') { | ||
throw new Error(`Invalid subscription definition. .for() can only reference a mutation. ${typeName} is referencing ${sourceName} which is a ${def.data.typeName}`); | ||
} | ||
// Ensure subscription return type matches the return type of triggering mutation(s) | ||
// TODO: when we remove .returns() for custom subscriptions, minor changes will be needed here. Instead of comparing subscriptionSource return val | ||
// to a root returnType, we'll need to ensure that each subscriptionSource has the same return type | ||
if (returnType.data.type === 'ref') { | ||
const returnTypeName = returnType.data.link; | ||
if (type === 'Model') { | ||
if (returnTypeName !== sourceName || | ||
returnType.data.array !== source.data.array) { | ||
throw new Error(`Invalid subscription definition. Subscription return type must match the return type of the mutation triggering it. ${typeName} is referencing ${sourceName} which has a different return type`); | ||
} | ||
} | ||
if (type === 'CustomOperation') { | ||
const customOperationReturnType = def.data.returnType.data.link; | ||
const customOperationReturnTypeArray = def.data.returnType.data.array; | ||
if (returnTypeName !== customOperationReturnType || | ||
returnType.data.array !== customOperationReturnTypeArray) { | ||
throw new Error(`Invalid subscription definition. Subscription return type must match the return type of the mutation triggering it. ${typeName} is referencing ${sourceName} which has a different return type`); | ||
} | ||
} | ||
} | ||
else if (returnType.data.fieldType !== undefined) { | ||
if (type === 'Model') { | ||
throw new Error(`Invalid subscription definition. Subscription return type must match the return type of the mutation triggering it. ${typeName} is referencing ${sourceName} which has a different return type`); | ||
} | ||
if (type === 'CustomOperation') { | ||
const customOperationReturnType = def.data.returnType.data.fieldType; | ||
const customOperationReturnTypeArray = def.data.returnType.data.array; | ||
if (returnType.data.fieldType !== customOperationReturnType || | ||
returnType.data.array !== customOperationReturnTypeArray) { | ||
throw new Error(`Invalid subscription definition. Subscription return type must match the return type of the mutation triggering it. ${typeName} is referencing ${sourceName} which has a different return type`); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
@@ -943,6 +1035,6 @@ const isCustomHandler = (handler) => { | ||
}; | ||
function transformCustomOperations(typeDef, typeName, authRules, databaseType) { | ||
function transformCustomOperations(typeDef, typeName, authRules, databaseType, getRefType) { | ||
const { typeName: opType, handlers } = typeDef.data; | ||
let jsFunctionForField = undefined; | ||
validateCustomOperations(typeDef, typeName, authRules); | ||
validateCustomOperations(typeDef, typeName, authRules, getRefType); | ||
if (isCustomHandler(handlers)) { | ||
@@ -952,3 +1044,3 @@ jsFunctionForField = handleCustom(handlers, opType, typeName); | ||
const isCustom = Boolean(jsFunctionForField); | ||
const { gqlField, models, lambdaFunctionDefinition } = customOperationToGql(typeName, typeDef, authRules, isCustom, databaseType); | ||
const { gqlField, models, lambdaFunctionDefinition } = customOperationToGql(typeName, typeDef, authRules, isCustom, databaseType, getRefType); | ||
return { gqlField, models, jsFunctionForField, lambdaFunctionDefinition }; | ||
@@ -955,0 +1047,0 @@ } |
{ | ||
"name": "@aws-amplify/data-schema", | ||
"version": "0.13.17", | ||
"version": "0.13.18", | ||
"license": "Apache-2.0", | ||
@@ -5,0 +5,0 @@ "repository": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
647778
3963