@aws-amplify/data-schema 1.17.5 → 1.18.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.
@@ -257,10 +257,15 @@ function customOperationToGql(typeName, typeDef, authorization, isCustom = false
257
257
  refererTypeName: typeName,
258
258
  });
259
259
  }
260
- if (Object.keys(fieldArgs).length > 0) {
261
- const { gqlFields, implicitTypes: implied } = processFields(typeName, fieldArgs, {}, {});
262
- callSignature += `(${gqlFields.join(', ')})`;
263
- implicitTypes.push(...implied);
260
+ const { inputTypes, argDefinitions, collectedEnums } = generateInputTypes(typeName, fieldArgs, getRefType);
261
+ // Handle collected enums
262
+ for (const [enumName, enumDef] of collectedEnums) {
263
+ if (!implicitTypes.some(([name]) => name === enumName)) {
264
+ implicitTypes.push([enumName, enumDef]);
265
+ }
266
+ }
267
+ if (argDefinitions.length > 0) {
268
+ callSignature += `(${argDefinitions.join(', ')})`;
264
269
  }
265
270
  const handler = handlers && handlers[0];
266
271
  const brand = handler && (0, util_1.getBrand)(handler);
@@ -335,6 +340,7 @@ function customOperationToGql(typeName, typeDef, authorization, isCustom = false
335
340
  customTypeAuthRules,
336
341
  lambdaFunctionDefinition,
337
342
  customSqlDataSourceStrategy,
343
+ inputTypes,
338
344
  };
339
345
  }
340
346
  /**
@@ -858,6 +864,134 @@ const mergeCustomTypeAuthRules = (existing, added) => {
858
864
  existing[typeName] = authRules;
859
865
  }
860
866
  };
867
+ /**
868
+ * Generates input types for custom operations in the schema.
869
+ *
870
+ * Processes operation arguments to create corresponding input types,
871
+ * handling referenced and inline custom types, enums, and nested structures.
872
+ * Manages circular references and prevents duplicate processing.
873
+ *
874
+ **/
875
+ function generateInputTypes(operationName, args, getRefType) {
876
+ const inputTypes = [];
877
+ const argDefinitions = [];
878
+ const collectedEnums = new Map();
879
+ const processedTypes = new Set(); // Track processed types to avoid duplicates
880
+ const processNonScalarFields = (fields, originalTypeName, isParentRef = false, parentChain = []) => {
881
+ const processedFields = {};
882
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
883
+ if (isRefField(fieldDef)) {
884
+ const refType = getRefType(fieldDef.data.link, originalTypeName);
885
+ if (refType.type === 'CustomType') {
886
+ const nestedInputTypeName = `${fieldDef.data.link}Input`;
887
+ processedFields[fieldName] = {
888
+ data: { type: 'ref', link: nestedInputTypeName },
889
+ };
890
+ // Process the nested type if it hasn't been processed and isn't a circular reference
891
+ if (!parentChain.includes(nestedInputTypeName) &&
892
+ !processedTypes.has(nestedInputTypeName)) {
893
+ processedTypes.add(nestedInputTypeName);
894
+ const nestedFields = processNonScalarFields(refType.def.data.fields, fieldDef.data.link, true, [...parentChain, nestedInputTypeName]);
895
+ inputTypes.push({
896
+ name: nestedInputTypeName,
897
+ fields: nestedFields,
898
+ });
899
+ }
900
+ }
901
+ else if (refType.type === 'Enum') {
902
+ processedFields[fieldName] = {
903
+ data: { type: 'ref', link: fieldDef.data.link },
904
+ };
905
+ }
906
+ else {
907
+ throw new Error(`Unsupported reference type '${refType.type}' for field '${fieldName}'. ` +
908
+ `Only references to CustomType and Enum are supported.`);
909
+ }
910
+ }
911
+ else if (isCustomType(fieldDef)) {
912
+ // Handle inline custom types
913
+ const nestedInputTypeName = `${capitalize(originalTypeName)}${capitalize(fieldName)}Input`;
914
+ processedFields[fieldName] = {
915
+ data: { type: 'ref', link: nestedInputTypeName },
916
+ };
917
+ if (!processedTypes.has(nestedInputTypeName)) {
918
+ processedTypes.add(nestedInputTypeName);
919
+ const nestedFields = processNonScalarFields(fieldDef.data.fields, `${capitalize(originalTypeName)}${capitalize(fieldName)}`, isParentRef, [...parentChain, nestedInputTypeName]);
920
+ inputTypes.push({
921
+ name: nestedInputTypeName,
922
+ fields: nestedFields,
923
+ });
924
+ }
925
+ }
926
+ else if (isEnumType(fieldDef)) {
927
+ // Handle enum types
928
+ const enumName = `${capitalize(originalTypeName)}${capitalize(fieldName)}`;
929
+ if (!collectedEnums.has(enumName) && !isParentRef) {
930
+ collectedEnums.set(enumName, fieldDef);
931
+ }
932
+ processedFields[fieldName] = { data: { type: 'ref', link: enumName } };
933
+ }
934
+ else {
935
+ processedFields[fieldName] = fieldDef;
936
+ }
937
+ }
938
+ return processedFields;
939
+ };
940
+ // Process top-level arguments
941
+ for (const [argName, argDef] of Object.entries(args)) {
942
+ if (isRefField(argDef)) {
943
+ const refType = getRefType(argDef.data.link, operationName);
944
+ if (refType.type === 'CustomType') {
945
+ const inputTypeName = `${argDef.data.link}Input`;
946
+ argDefinitions.push(`${argName}: ${inputTypeName}`);
947
+ // Process the input type if it hasn't been processed yet
948
+ if (!processedTypes.has(inputTypeName)) {
949
+ processedTypes.add(inputTypeName);
950
+ const fields = processNonScalarFields(refType.def.data.fields, argDef.data.link, true, [inputTypeName]);
951
+ inputTypes.push({
952
+ name: inputTypeName,
953
+ fields,
954
+ });
955
+ }
956
+ }
957
+ else if (refType.type === 'Enum') {
958
+ argDefinitions.push(`${argName}: ${argDef.data.link}`);
959
+ }
960
+ else {
961
+ throw new Error(`Unsupported reference type '${refType.type}' for argument '${argName}' in '${operationName}'. ` +
962
+ `Only references to CustomType and Enum are supported.`);
963
+ }
964
+ }
965
+ else if (isEnumType(argDef)) {
966
+ // Handle top-level enum arguments
967
+ const enumName = `${capitalize(operationName)}${capitalize(argName)}`;
968
+ if (!collectedEnums.has(enumName)) {
969
+ collectedEnums.set(enumName, argDef);
970
+ }
971
+ argDefinitions.push(`${argName}: ${enumName}`);
972
+ }
973
+ else if (isCustomType(argDef)) {
974
+ // Handle top-level custom type arguments
975
+ const inputTypeName = `${capitalize(operationName)}${capitalize(argName)}Input`;
976
+ argDefinitions.push(`${argName}: ${inputTypeName}`);
977
+ if (!processedTypes.has(inputTypeName)) {
978
+ processedTypes.add(inputTypeName);
979
+ const fields = processNonScalarFields(argDef.data.fields, `${capitalize(operationName)}${capitalize(argName)}`, false, [inputTypeName]);
980
+ inputTypes.push({
981
+ name: inputTypeName,
982
+ fields,
983
+ });
984
+ }
985
+ }
986
+ else if (isScalarField(argDef)) {
987
+ argDefinitions.push(`${argName}: ${scalarFieldToGql(argDef.data)}`);
988
+ }
989
+ else {
990
+ throw new Error(`Unsupported argument type for ${argName}`);
991
+ }
992
+ }
993
+ return { inputTypes, argDefinitions, collectedEnums };
994
+ }
861
995
  const schemaPreprocessor = (schema) => {
862
996
  const gqlModels = [];
863
997
  const customQueries = [];
@@ -889,6 +1023,7 @@ const schemaPreprocessor = (schema) => {
889
1023
  const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
890
1024
  const { schemaAuth, functionSchemaAccess } = extractFunctionSchemaAccess(schema.data.authorization);
891
1025
  const getRefType = getRefTypeForSchema(schema);
1026
+ const uniqueInputTypes = new Map();
892
1027
  for (const [typeName, typeDef] of topLevelTypes) {
893
1028
  const mostRelevantAuthRules = typeDef.data?.authorization?.length > 0
894
1029
  ? typeDef.data.authorization
@@ -921,7 +1056,13 @@ const schemaPreprocessor = (schema) => {
921
1056
  else if (isCustomOperation(typeDef)) {
922
1057
  // TODO: add generation route logic.
923
1058
  const { typeName: opType } = typeDef.data;
924
- const { gqlField, implicitTypes, customTypeAuthRules, jsFunctionForField, lambdaFunctionDefinition, customSqlDataSourceStrategy, } = transformCustomOperations(typeDef, typeName, mostRelevantAuthRules, databaseType, getRefType);
1059
+ const { gqlField, implicitTypes, customTypeAuthRules, jsFunctionForField, lambdaFunctionDefinition, customSqlDataSourceStrategy, inputTypes, } = transformCustomOperations(typeDef, typeName, mostRelevantAuthRules, databaseType, getRefType);
1060
+ // Process input types without duplicates
1061
+ for (const { name, fields } of inputTypes) {
1062
+ if (!uniqueInputTypes.has(name)) {
1063
+ uniqueInputTypes.set(name, fields);
1064
+ }
1065
+ }
925
1066
  topLevelTypes.push(...implicitTypes);
926
1067
  mergeCustomTypeAuthRules(customTypeInheritedAuthRules, customTypeAuthRules);
927
1068
  if (customTypeAuthRules) {
@@ -1011,6 +1152,12 @@ const schemaPreprocessor = (schema) => {
1011
1152
  gqlModels.push(model);
1012
1153
  }
1013
1154
  }
1155
+ // Generate input types after processing all custom operations
1156
+ for (const [name, fields] of uniqueInputTypes) {
1157
+ const { gqlFields } = processFields(name, fields, {}, {}, undefined, undefined, undefined, databaseEngine);
1158
+ const inputTypeDefinition = `input ${name} {\n ${gqlFields.join('\n ')}\n}`;
1159
+ gqlModels.push(inputTypeDefinition);
1160
+ }
1014
1161
  const customOperations = {
1015
1162
  queries: customQueries,
1016
1163
  mutations: customMutations,
@@ -1205,7 +1352,7 @@ function transformCustomOperations(typeDef, typeName, authRules, databaseType, g
1205
1352
  opType, typeName);
1206
1353
  }
1207
1354
  const isCustom = Boolean(jsFunctionForField);
1208
- const { gqlField, implicitTypes, customTypeAuthRules, lambdaFunctionDefinition, customSqlDataSourceStrategy, } = customOperationToGql(typeName, typeDef, authRules, isCustom, databaseType, getRefType);
1355
+ const { gqlField, implicitTypes, customTypeAuthRules, lambdaFunctionDefinition, customSqlDataSourceStrategy, inputTypes, } = customOperationToGql(typeName, typeDef, authRules, isCustom, databaseType, getRefType);
1209
1356
  return {
1210
1357
  gqlField,
1211
1358
  implicitTypes,
@@ -1213,6 +1360,7 @@ function transformCustomOperations(typeDef, typeName, authRules, databaseType, g
1213
1360
  jsFunctionForField,
1214
1361
  lambdaFunctionDefinition,
1215
1362
  customSqlDataSourceStrategy,
1363
+ inputTypes,
1216
1364
  };
1217
1365
  }
1218
1366
  function generateCustomOperationTypes({ queries, mutations, subscriptions, }) {