@aws-amplify/data-schema 1.1.6 → 1.2.1

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.
Files changed (41) hide show
  1. package/dist/cjs/ModelSchema.js +19 -0
  2. package/dist/cjs/ModelSchema.js.map +1 -1
  3. package/dist/cjs/SchemaProcessor.js +1 -4
  4. package/dist/cjs/SchemaProcessor.js.map +1 -1
  5. package/dist/cjs/runtime/internals/APIClient.js +132 -22
  6. package/dist/cjs/runtime/internals/APIClient.js.map +1 -1
  7. package/dist/cjs/runtime/internals/operations/list.js +4 -1
  8. package/dist/cjs/runtime/internals/operations/list.js.map +1 -1
  9. package/dist/cjs/runtime/utils/index.js +5 -1
  10. package/dist/cjs/runtime/utils/index.js.map +1 -1
  11. package/dist/cjs/runtime/utils/stringTransformation.js +13 -0
  12. package/dist/cjs/runtime/utils/stringTransformation.js.map +1 -0
  13. package/dist/esm/MappedTypes/MapIndexes.d.ts +13 -3
  14. package/dist/esm/ModelSchema.d.ts +13 -1
  15. package/dist/esm/ModelSchema.mjs +20 -1
  16. package/dist/esm/ModelSchema.mjs.map +1 -1
  17. package/dist/esm/ModelType.d.ts +1 -0
  18. package/dist/esm/SchemaProcessor.mjs +1 -4
  19. package/dist/esm/SchemaProcessor.mjs.map +1 -1
  20. package/dist/esm/runtime/client/index.d.ts +56 -7
  21. package/dist/esm/runtime/internals/APIClient.mjs +129 -18
  22. package/dist/esm/runtime/internals/APIClient.mjs.map +1 -1
  23. package/dist/esm/runtime/internals/operations/list.mjs +4 -1
  24. package/dist/esm/runtime/internals/operations/list.mjs.map +1 -1
  25. package/dist/esm/runtime/utils/index.d.ts +2 -0
  26. package/dist/esm/runtime/utils/index.mjs +2 -0
  27. package/dist/esm/runtime/utils/index.mjs.map +1 -1
  28. package/dist/esm/runtime/utils/stringTransformation.d.ts +5 -0
  29. package/dist/esm/runtime/utils/stringTransformation.mjs +10 -0
  30. package/dist/esm/runtime/utils/stringTransformation.mjs.map +1 -0
  31. package/dist/meta/cjs.tsbuildinfo +1 -1
  32. package/package.json +2 -1
  33. package/src/MappedTypes/MapIndexes.ts +26 -3
  34. package/src/ModelSchema.ts +43 -1
  35. package/src/ModelType.ts +1 -1
  36. package/src/SchemaProcessor.ts +2 -7
  37. package/src/runtime/client/index.ts +68 -13
  38. package/src/runtime/internals/APIClient.ts +147 -27
  39. package/src/runtime/internals/operations/list.ts +6 -1
  40. package/src/runtime/utils/index.ts +2 -0
  41. package/src/runtime/utils/stringTransformation.ts +7 -0
@@ -25,7 +25,7 @@ import type {
25
25
  } from './CustomOperation';
26
26
  import { processSchema } from './SchemaProcessor';
27
27
  import { AllowModifier, SchemaAuthorization, allow } from './Authorization';
28
- import { Brand, brand } from './util';
28
+ import { Brand, brand, getBrand } from './util';
29
29
  import {
30
30
  ModelRelationalField,
31
31
  ModelRelationalFieldParamShape,
@@ -45,6 +45,10 @@ type SchemaContent =
45
45
  | EnumType
46
46
  | CustomOperation<CustomOperationParamShape, any>;
47
47
 
48
+ // The SQL-only `addToSchema` accepts all top-level entities, excepts models
49
+ type AddToSchemaContent = Exclude<SchemaContent, BaseModelType>;
50
+ type AddToSchemaContents = Record<string, AddToSchemaContent>;
51
+
48
52
  type NonEmpty<T> = keyof T extends never ? never : T;
49
53
 
50
54
  type ModelSchemaContents = Record<string, SchemaContent>;
@@ -103,6 +107,7 @@ export type ModelSchema<
103
107
  DDBSchemaBrand;
104
108
 
105
109
  type RDSModelSchemaFunctions =
110
+ | 'addToSchema'
106
111
  | 'addQueries'
107
112
  | 'addMutations'
108
113
  | 'addSubscriptions'
@@ -124,18 +129,33 @@ export type RDSModelSchema<
124
129
  >,
125
130
  > = Omit<
126
131
  {
132
+ addToSchema: <AddedTypes extends AddToSchemaContents>(
133
+ types: AddedTypes,
134
+ ) => RDSModelSchema<
135
+ SetTypeSubArg<T, 'types', T['types'] & AddedTypes>,
136
+ UsedMethods | 'addToSchema'
137
+ >;
138
+ /**
139
+ * @deprecated use `addToSchema()` to add operations to a SQL schema
140
+ */
127
141
  addQueries: <Queries extends Record<string, QueryCustomOperation>>(
128
142
  types: Queries,
129
143
  ) => RDSModelSchema<
130
144
  SetTypeSubArg<T, 'types', T['types'] & Queries>,
131
145
  UsedMethods | 'addQueries'
132
146
  >;
147
+ /**
148
+ * @deprecated use `addToSchema()` to add operations to a SQL schema
149
+ */
133
150
  addMutations: <Mutations extends Record<string, MutationCustomOperation>>(
134
151
  types: Mutations,
135
152
  ) => RDSModelSchema<
136
153
  SetTypeSubArg<T, 'types', T['types'] & Mutations>,
137
154
  UsedMethods | 'addMutations'
138
155
  >;
156
+ /**
157
+ * @deprecated use `addToSchema()` to add operations to a SQL schema
158
+ */
139
159
  addSubscriptions: <
140
160
  Subscriptions extends Record<string, SubscriptionCustomOperation>,
141
161
  >(
@@ -254,6 +274,22 @@ export const isModelSchema = (
254
274
  return typeof schema === 'object' && schema.data !== undefined;
255
275
  };
256
276
 
277
+ /**
278
+ * Ensures that only supported entities are being added to the SQL schema through `addToSchema`
279
+ * Models are not supported for brownfield SQL
280
+ *
281
+ * @param types - purposely widened to ModelSchemaContents, because we need to validate at runtime that a model is not being passed in here
282
+ */
283
+ function validateAddToSchema(types: ModelSchemaContents): void {
284
+ for (const [name, type] of Object.entries(types)) {
285
+ if (getBrand(type) === 'modelType') {
286
+ throw new Error(
287
+ `Invalid value specified for ${name} in addToSchema(). Models cannot be manually added to a SQL schema.`,
288
+ );
289
+ }
290
+ }
291
+ }
292
+
257
293
  function _rdsSchema<
258
294
  T extends RDSModelSchemaParamShape,
259
295
  DSC extends SchemaConfiguration<any, any>,
@@ -278,6 +314,12 @@ function _rdsSchema<
278
314
  const { authorization: _, ...rest } = this;
279
315
  return rest;
280
316
  },
317
+ addToSchema(types: AddToSchemaContents): any {
318
+ validateAddToSchema(types);
319
+ this.data.types = { ...this.data.types, ...types };
320
+ const { addToSchema: _, ...rest } = this;
321
+ return rest;
322
+ },
281
323
  addQueries(types: Record<string, QueryCustomOperation>): any {
282
324
  this.data.types = { ...this.data.types, ...types };
283
325
  const { addQueries: _, ...rest } = this;
package/src/ModelType.ts CHANGED
@@ -363,7 +363,7 @@ export function model<T extends ModelFields>(
363
363
  fields: T,
364
364
  ): ModelType<{
365
365
  fields: T;
366
- identifier: { pk: { id: string }; sk: never };
366
+ identifier: { pk: { id: string }; sk: never; compositeSk: never };
367
367
  secondaryIndexes: [];
368
368
  authorization: [];
369
369
  }> {
@@ -1159,8 +1159,7 @@ const schemaPreprocessor = (
1159
1159
  ? 'dynamodb'
1160
1160
  : 'sql';
1161
1161
 
1162
- const staticSchema =
1163
- schema.data.configuration.database.engine === 'dynamodb' ? false : true;
1162
+ const staticSchema = databaseType === 'sql';
1164
1163
 
1165
1164
  const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
1166
1165
 
@@ -1288,11 +1287,7 @@ const schemaPreprocessor = (
1288
1287
  const [partitionKey] = identifier;
1289
1288
 
1290
1289
  const { authString, authFields } = calculateAuth(mostRelevantAuthRules);
1291
- if (authString == '') {
1292
- throw new Error(
1293
- `Model \`${typeName}\` is missing authorization rules. Add global rules to the schema or ensure every model has its own rules.`,
1294
- );
1295
- }
1290
+
1296
1291
  const fieldLevelAuthRules = processFieldLevelAuthRules(
1297
1292
  fields,
1298
1293
  authFields,
@@ -449,7 +449,7 @@ type SizeFilter = {
449
449
  /**
450
450
  * Filters options that can be used on string-like fields.
451
451
  */
452
- type StringFilter<T extends string = string> = {
452
+ export type StringFilter<T extends string = string> = {
453
453
  attributeExists?: boolean;
454
454
  beginsWith?: string;
455
455
  between?: [string, string];
@@ -464,7 +464,7 @@ type StringFilter<T extends string = string> = {
464
464
  size?: SizeFilter;
465
465
  };
466
466
 
467
- type NumericFilter = {
467
+ export type NumericFilter = {
468
468
  attributeExists?: boolean;
469
469
  between?: [number, number];
470
470
  eq?: number;
@@ -481,6 +481,53 @@ type BooleanFilters = {
481
481
  ne?: boolean;
482
482
  };
483
483
 
484
+ /**
485
+ * A composite SK (in an identifier or secondaryIndex) resolves to this type for
486
+ * list queries and index queries
487
+ *
488
+ * @example
489
+ * Given
490
+ * ```ts
491
+ * MyModel: a
492
+ .model({
493
+ pk: a.string().required(),
494
+ sk1: a.string().required(),
495
+ sk2: a.integer().required(),
496
+ })
497
+ .identifier(['pk', 'sk1', 'sk2']),
498
+ * ```
499
+ * Expected list options:
500
+ * ```ts
501
+ * {
502
+ * pk?: string
503
+ * sk1Sk2?: ModelPrimaryCompositeKeyConditionInput
504
+ * }
505
+ * ```
506
+ * Where ModelPrimaryCompositeKeyConditionInput resolves to:
507
+ * ```ts
508
+ * {
509
+ * eq: {sk1: string; sk2: number};
510
+ * le: {sk1: string; sk2: number};
511
+ * lt: {sk1: string; sk2: number};
512
+ * ge: {sk1: string; sk2: number};
513
+ * gt: {sk1: string; sk2: number};
514
+ * between: [ {sk1: string; sk2: number} ];
515
+ * beginsWith: {sk1: string; sk2: number};
516
+ * }
517
+ * ```
518
+ * */
519
+ export type ModelPrimaryCompositeKeyInput<
520
+ SkIr extends Record<string, string | number>,
521
+ > = {
522
+ eq?: SkIr;
523
+ le?: SkIr;
524
+ lt?: SkIr;
525
+ ge?: SkIr;
526
+ gt?: SkIr;
527
+ between?: [SkIr];
528
+ beginsWith?: SkIr;
529
+ };
530
+
484
531
  type ModelFilter<Model extends Record<any, any>> = LogicalFilters<Model> & {
485
532
  [K in keyof Model as Model[K] extends LazyLoader<any, any>
486
533
  ? never
@@ -498,7 +545,6 @@ type ModelMetaShape = {
498
545
  identifier: PrimaryIndexIrShape;
499
546
  };
500
547
 
501
- // TODO: remove export. added for debugging.
502
548
  export type ModelTypesClient<
503
549
  ModelName extends string,
504
550
  Model extends Record<string, unknown>,
@@ -890,6 +936,7 @@ export type CustomHeaders =
890
936
  export interface PrimaryIndexIrShape {
891
937
  pk: { [key: string]: string | number };
892
938
  sk: { [key: string]: string | number } | never;
939
+ compositeSk: never | string;
893
940
  }
894
941
 
895
942
  /**
@@ -900,7 +947,7 @@ export interface SecondaryIndexIrShape extends PrimaryIndexIrShape {
900
947
  queryField: string;
901
948
  }
902
949
 
903
- type IndexQueryMethodsFromIR<
950
+ export type IndexQueryMethodsFromIR<
904
951
  SecondaryIdxTuple extends SecondaryIndexIrShape[],
905
952
  ModelName extends string,
906
953
  Model extends Record<string, unknown>,
@@ -919,7 +966,7 @@ type IndexQueryMethodsFromIR<
919
966
  >
920
967
  : Res;
921
968
 
922
- type ListPkOptions<
969
+ export type ListPkOptions<
923
970
  ModelMeta extends ModelMetaShape,
924
971
  Enums extends Record<string, string>,
925
972
  > = ModelMeta['identifier']['sk'] extends never
@@ -933,20 +980,28 @@ type ListPkOptions<
933
980
  /**
934
981
  * Accepts a PrimaryIndexIr or SecondaryIndexIr and returns resolved parameters
935
982
  */
936
- type IndexQueryInput<
983
+ export type IndexQueryInput<
937
984
  Idx extends PrimaryIndexIrShape,
938
985
  Enums extends Record<string, string>,
939
986
  > = {
940
987
  [PKField in keyof Idx['pk']]: Idx['pk'][PKField] extends `${deferredRefResolvingPrefix}${infer R}`
941
988
  ? Enums[R]
942
989
  : Idx['pk'][PKField];
943
- } & {
944
- [SKField in keyof Idx['sk']]+?: number extends Idx['sk'][SKField]
945
- ? NumericFilter
946
- : Idx['sk'][SKField] extends `${deferredRefResolvingPrefix}${infer R}`
947
- ? StringFilter<Enums[R]>
948
- : StringFilter<Idx['sk'][SKField] & string>;
949
- };
990
+ } & (Idx['compositeSk'] extends never
991
+ ? {
992
+ [SKField in keyof Idx['sk']]+?: number extends Idx['sk'][SKField]
993
+ ? NumericFilter
994
+ : Idx['sk'][SKField] extends `${deferredRefResolvingPrefix}${infer R}`
995
+ ? StringFilter<Enums[R]>
996
+ : StringFilter<Idx['sk'][SKField] & string>;
997
+ }
998
+ : {
999
+ [CompositeSk in Idx['compositeSk']]+?: ModelPrimaryCompositeKeyInput<{
1000
+ [SKField in keyof Idx['sk']]: Idx['sk'][SKField] extends `${deferredRefResolvingPrefix}${infer _R}`
1001
+ ? string
1002
+ : Idx['sk'][SKField];
1003
+ }>;
1004
+ });
950
1005
 
951
1006
  type IndexQueryMethodSignature<
952
1007
  Idx extends SecondaryIndexIrShape,
@@ -20,7 +20,7 @@ import {
20
20
  } from '../bridge-types';
21
21
 
22
22
  import { CustomHeaders } from '../client';
23
- import { resolveOwnerFields } from '../utils/resolveOwnerFields';
23
+ import { resolveOwnerFields, capitalize } from '../utils';
24
24
 
25
25
  import type { IndexMeta } from './operations/indexQuery';
26
26
 
@@ -38,6 +38,39 @@ const connectionType = {
38
38
  BELONGS_TO: 'BELONGS_TO',
39
39
  };
40
40
 
41
+ // When generating an SK's KeyConditionInput name, string-like types map to String
42
+ const skGraphQlFieldTypeMap = {
43
+ ID: 'ID',
44
+ String: 'String',
45
+ AWSDate: 'String',
46
+ AWSTime: 'String',
47
+ AWSDateTime: 'String',
48
+ AWSTimestamp: 'String',
49
+ AWSEmail: 'String',
50
+ AWSPhone: 'String',
51
+ AWSURL: 'String',
52
+ AWSIPAddress: 'String',
53
+ AWSJSON: 'String',
54
+ Boolean: 'Boolean',
55
+ Int: 'Int',
56
+ Float: 'Float',
57
+ };
58
+
59
+ // move to util
60
+ const resolvedSkName = (sk: string[]): string => {
61
+ if (sk.length === 1) {
62
+ return sk[0];
63
+ } else {
64
+ return sk.reduce((acc, curr, idx) => {
65
+ if (idx === 0) {
66
+ return curr;
67
+ } else {
68
+ return acc + capitalize(curr);
69
+ }
70
+ }, '');
71
+ }
72
+ };
73
+
41
74
  /**
42
75
  *
43
76
  * @param GraphQL response object
@@ -676,12 +709,14 @@ export function generateGraphQLDocument(
676
709
  primaryKeyFieldName,
677
710
  sortKeyFieldNames,
678
711
  },
712
+ attributes,
679
713
  } = modelDefinition;
680
714
 
681
715
  // Use pascal case of the model name to generate the operations and the arguments.
682
716
  // This is required to be in sync with the resources generated by the GraphQL transformers.
683
717
  const namePascalCase = name.charAt(0).toUpperCase() + name.slice(1);
684
- const pluralNamePascalCase = pluralName.charAt(0).toUpperCase() + pluralName.slice(1);
718
+ const pluralNamePascalCase =
719
+ pluralName.charAt(0).toUpperCase() + pluralName.slice(1);
685
720
 
686
721
  const { operationPrefix, usePlural } = graphQLOperationsInfo[modelOperation];
687
722
 
@@ -696,17 +731,45 @@ export function generateGraphQLDocument(
696
731
  const { queryField, pk, sk = [] } = indexMeta;
697
732
  graphQLFieldName = queryField;
698
733
 
699
- const skQueryArgs = sk.reduce((acc: Record<string, any>, fieldName) => {
700
- const fieldType = Object.prototype.hasOwnProperty.call(
701
- fields[fieldName].type,
702
- 'enum',
703
- )
704
- ? 'String' // AppSync schema sets `ModelStringKeyConditionInput` as the type of the enum field that's used as SK
705
- : fields[fieldName].type;
706
- acc[fieldName] = `Model${fieldType}KeyConditionInput`;
734
+ /**
735
+ * **a. Single field SK** -> single arg where name is the field name and the type is `Model${gqlFieldType}KeyConditionInput` (nullable)
736
+ * Note: string-like data types e.g., AWSDateTime, AWSEmail, AWSPhone, etc. should map to String. See `skGraphQlFieldTypeMap` above
737
+ * @example
738
+ * ```
739
+ * sk1: ModelStringKeyConditionInput
740
+ * ```
741
+ *
742
+ * **b. Composite SK** -> single arg where the name is camelCase concatenation of all the field names that comprise the SK
743
+ * and the type is `Model${modelName}${keyAttributeName}CompositeKeyConditionInput` (nullable)
744
+ * @example
745
+ * ```
746
+ * sk1Sk2: ModelMyModelMyModelByPkAndSk1AndSk2CompositeKeyConditionInput
747
+ */
748
+ let skQueryArgs = {};
749
+
750
+ if (sk.length === 1) {
751
+ const [skField] = sk;
752
+ const type = (
753
+ typeof fields[skField].type === 'string'
754
+ ? fields[skField].type
755
+ : 'String'
756
+ ) as keyof typeof skGraphQlFieldTypeMap;
757
+ const normalizedType = skGraphQlFieldTypeMap[type];
758
+
759
+ skQueryArgs = {
760
+ [skField]: `Model${normalizedType}KeyConditionInput`,
761
+ };
762
+ } else if (sk.length > 1) {
763
+ const compositeSkArgName = resolvedSkName(sk);
764
+
765
+ const keyName = attributes?.find(
766
+ (attr) => attr?.properties?.queryField === queryField,
767
+ )?.properties?.name;
707
768
 
708
- return acc;
709
- }, {});
769
+ skQueryArgs = {
770
+ [compositeSkArgName]: `Model${capitalize(modelName)}${capitalize(keyName)}CompositeKeyConditionInput`,
771
+ };
772
+ }
710
773
 
711
774
  indexQueryArgs = {
712
775
  [pk]: `${
@@ -739,21 +802,69 @@ export function generateGraphQLDocument(
739
802
  };
740
803
  const listPkArgs = {};
741
804
 
805
+ /**
806
+ * Generate query field args for the SK if it's defined
807
+ *
808
+ * **1. Get queries** require each SK field to be present as a separate arg where the type is the field's GraphQL scalar type (non-nullable)
809
+ * @example
810
+ * ```
811
+ * sk1: String!, sk2: Int!
812
+ * ```
813
+ *
814
+ * **2. List queries**
815
+ *
816
+ * **a. Single field SK** -> single arg where name is the field name and the type is `Model${gqlFieldType}KeyConditionInput` (nullable)
817
+ * Note: string-like data types e.g., AWSDateTime, AWSEmail, AWSPhone, etc. should map to String. See `skGraphQlFieldTypeMap` above
818
+ * @example
819
+ * ```
820
+ * sk1: ModelStringKeyConditionInput
821
+ * ```
822
+ *
823
+ * **b. Composite SK** -> single arg where the name is camelCase concatenation of all the field names that comprise the SK
824
+ * and the type is `Model${modelName}PrimaryCompositeKeyConditionInput` (nullable)
825
+ * @example
826
+ * ```
827
+ * sk1Sk2: ModelMyModelPrimaryCompositeKeyConditionInput
828
+ * ```
829
+ */
742
830
  const generateSkArgs = (op: 'get' | 'list') => {
743
- return sortKeyFieldNames.reduce(
744
- (acc: Record<string, any>, fieldName: string) => {
745
- const fieldType = fields[fieldName].type;
746
-
747
- if (op === 'get') {
748
- acc[fieldName] = `${fieldType}!`;
749
- } else if (op === 'list') {
750
- acc[fieldName] = `Model${fieldType}KeyConditionInput`;
751
- }
831
+ if (sortKeyFieldNames.length === 0) return {};
752
832
 
753
- return acc;
754
- },
755
- {},
756
- );
833
+ if (op === 'get') {
834
+ return sortKeyFieldNames.reduce(
835
+ (acc: Record<string, any>, fieldName: string) => {
836
+ const fieldType = fields[fieldName].type;
837
+
838
+ if (op === 'get') {
839
+ acc[fieldName] = `${fieldType}!`; // ! - SK args are non-nullable in Get queries
840
+ }
841
+
842
+ return acc;
843
+ },
844
+ {},
845
+ );
846
+ } else {
847
+ // list SK
848
+ if (sortKeyFieldNames.length === 1) {
849
+ // Single SK
850
+ const [sk] = sortKeyFieldNames;
851
+ const type = (
852
+ typeof fields[sk].type === 'string' ? fields[sk].type : 'String'
853
+ ) as keyof typeof skGraphQlFieldTypeMap;
854
+ const normalizedType = skGraphQlFieldTypeMap[type];
855
+
856
+ return {
857
+ [sk]: `Model${normalizedType}KeyConditionInput`,
858
+ };
859
+ } else {
860
+ // Composite SK
861
+ const compositeSkArgName = resolvedSkName(sortKeyFieldNames);
862
+
863
+ return {
864
+ [compositeSkArgName]: `Model${capitalize(modelName)}PrimaryCompositeKeyConditionInput`,
865
+ };
866
+ }
867
+ }
757
868
  };
758
869
 
759
870
  if (isCustomPrimaryKey) {
@@ -873,6 +984,8 @@ export function buildGraphQLVariables(
873
984
  },
874
985
  } = modelDefinition;
875
986
 
987
+ const skName = sortKeyFieldNames?.length && resolvedSkName(sortKeyFieldNames);
988
+
876
989
  let variables: Record<string, any> = {};
877
990
 
878
991
  // TODO: process input
@@ -930,8 +1043,13 @@ export function buildGraphQLVariables(
930
1043
  }
931
1044
  if (arg?.sortDirection) {
932
1045
  variables.sortDirection = arg.sortDirection;
1046
+ }
1047
+ if (arg && arg[primaryKeyFieldName]) {
933
1048
  variables[primaryKeyFieldName] = arg[primaryKeyFieldName];
934
1049
  }
1050
+ if (skName && arg && arg[skName]) {
1051
+ variables[skName] = arg[skName];
1052
+ }
935
1053
  if (arg?.nextToken) {
936
1054
  variables.nextToken = arg.nextToken;
937
1055
  }
@@ -942,10 +1060,12 @@ export function buildGraphQLVariables(
942
1060
  case 'INDEX_QUERY': {
943
1061
  const { pk, sk = [] } = indexMeta!;
944
1062
 
1063
+ const indexQuerySkName = sk?.length && resolvedSkName(sk);
1064
+
945
1065
  variables[pk] = arg![pk];
946
1066
 
947
- for (const skField of sk) {
948
- variables[skField] = arg![skField];
1067
+ if (indexQuerySkName && arg && arg[indexQuerySkName]) {
1068
+ variables[indexQuerySkName] = arg[indexQuerySkName];
949
1069
  }
950
1070
 
951
1071
  if (arg?.filter) {
@@ -144,7 +144,12 @@ async function _list(
144
144
  const { data, errors } = error;
145
145
 
146
146
  // `data` is not `null`, and is not an empty object:
147
- if (data !== undefined && Object.keys(data).length !== 0 && errors) {
147
+ if (
148
+ data !== undefined &&
149
+ data !== null &&
150
+ Object.keys(data).length !== 0 &&
151
+ errors
152
+ ) {
148
153
  const [key] = Object.keys(data);
149
154
 
150
155
  if (data[key]?.items) {
@@ -3,3 +3,5 @@
3
3
 
4
4
  export { resolvePKFields } from './resolvePKFields';
5
5
  export { findIndexByFields } from './findIndexByFields';
6
+ export { resolveOwnerFields } from './resolveOwnerFields';
7
+ export { capitalize } from './stringTransformation';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @param s string to capitalize
3
+ * @returns capitalized string
4
+ */
5
+ export function capitalize<T extends string>(s: T): Capitalize<T> {
6
+ return `${s[0].toUpperCase()}${s.slice(1)}` as Capitalize<T>;
7
+ }