@contember/engine-content-api 1.1.1 → 1.2.0-alpha.10

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 (198) hide show
  1. package/dist/src/acl/EvaluatedPredicateReplacer.d.ts +9 -0
  2. package/dist/src/acl/EvaluatedPredicateReplacer.d.ts.map +1 -0
  3. package/dist/src/acl/EvaluatedPredicateReplacer.js +31 -0
  4. package/dist/src/acl/EvaluatedPredicateReplacer.js.map +1 -0
  5. package/dist/src/acl/PermissionFactory.d.ts.map +1 -1
  6. package/dist/src/acl/PermissionFactory.js +15 -11
  7. package/dist/src/acl/PermissionFactory.js.map +1 -1
  8. package/dist/src/acl/PredicateFactory.d.ts +4 -2
  9. package/dist/src/acl/PredicateFactory.d.ts.map +1 -1
  10. package/dist/src/acl/PredicateFactory.js +24 -6
  11. package/dist/src/acl/PredicateFactory.js.map +1 -1
  12. package/dist/src/acl/PredicatesInjector.d.ts +1 -1
  13. package/dist/src/acl/PredicatesInjector.d.ts.map +1 -1
  14. package/dist/src/acl/PredicatesInjector.js +18 -14
  15. package/dist/src/acl/PredicatesInjector.js.map +1 -1
  16. package/dist/src/acl/VariableInjector.d.ts +0 -1
  17. package/dist/src/acl/VariableInjector.d.ts.map +1 -1
  18. package/dist/src/acl/VariableInjector.js +4 -14
  19. package/dist/src/acl/VariableInjector.js.map +1 -1
  20. package/dist/src/acl/VariableUtils.d.ts +2 -13
  21. package/dist/src/acl/VariableUtils.d.ts.map +1 -1
  22. package/dist/src/acl/VariableUtils.js +5 -16
  23. package/dist/src/acl/VariableUtils.js.map +1 -1
  24. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducerExecutionHandler.js +1 -1
  25. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducerExecutionHandler.js.map +1 -1
  26. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.d.ts.map +1 -1
  27. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.js +25 -13
  28. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.js.map +1 -1
  29. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.d.ts.map +1 -1
  30. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.js +26 -13
  31. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.js.map +1 -1
  32. package/dist/src/input-validation/ValidationContext.d.ts +2 -2
  33. package/dist/src/input-validation/ValidationContext.d.ts.map +1 -1
  34. package/dist/src/input-validation/ValidationContext.js.map +1 -1
  35. package/dist/src/input-validation/ValidationDataSelector.d.ts.map +1 -1
  36. package/dist/src/input-validation/ValidationDataSelector.js +1 -1
  37. package/dist/src/input-validation/ValidationDataSelector.js.map +1 -1
  38. package/dist/src/introspection/content-schema.types.d.ts +19 -223
  39. package/dist/src/introspection/content-schema.types.d.ts.map +1 -1
  40. package/dist/src/introspection/content-schema.types.js +2 -2
  41. package/dist/src/introspection/content-schema.types.js.map +1 -1
  42. package/dist/src/mapper/Mapper.d.ts +4 -4
  43. package/dist/src/mapper/Mapper.d.ts.map +1 -1
  44. package/dist/src/mapper/Mapper.js +7 -7
  45. package/dist/src/mapper/Mapper.js.map +1 -1
  46. package/dist/src/mapper/MapperContainer.d.ts.map +1 -1
  47. package/dist/src/mapper/MapperContainer.js +5 -2
  48. package/dist/src/mapper/MapperContainer.js.map +1 -1
  49. package/dist/src/mapper/select/RelationFetcher.d.ts +40 -5
  50. package/dist/src/mapper/select/RelationFetcher.d.ts.map +1 -1
  51. package/dist/src/mapper/select/RelationFetcher.js +19 -6
  52. package/dist/src/mapper/select/RelationFetcher.js.map +1 -1
  53. package/dist/src/mapper/select/WhereBuilder.d.ts +3 -1
  54. package/dist/src/mapper/select/WhereBuilder.d.ts.map +1 -1
  55. package/dist/src/mapper/select/WhereBuilder.js +4 -2
  56. package/dist/src/mapper/select/WhereBuilder.js.map +1 -1
  57. package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts +2 -2
  58. package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
  59. package/dist/src/mapper/select/handlers/FieldsVisitor.js +31 -18
  60. package/dist/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
  61. package/dist/src/mapper/select/optimizer/ConditionOptimizer.d.ts +5 -0
  62. package/dist/src/mapper/select/optimizer/ConditionOptimizer.d.ts.map +1 -0
  63. package/dist/src/mapper/select/optimizer/ConditionOptimizer.js +33 -0
  64. package/dist/src/mapper/select/optimizer/ConditionOptimizer.js.map +1 -0
  65. package/dist/src/mapper/select/optimizer/WhereOptimizer.d.ts +14 -0
  66. package/dist/src/mapper/select/optimizer/WhereOptimizer.d.ts.map +1 -0
  67. package/dist/src/mapper/select/optimizer/WhereOptimizer.js +86 -0
  68. package/dist/src/mapper/select/optimizer/WhereOptimizer.js.map +1 -0
  69. package/dist/src/mapper/select/optimizer/WhereReplacer.d.ts +3 -0
  70. package/dist/src/mapper/select/optimizer/WhereReplacer.d.ts.map +1 -0
  71. package/dist/src/mapper/select/optimizer/WhereReplacer.js +27 -0
  72. package/dist/src/mapper/select/optimizer/WhereReplacer.js.map +1 -0
  73. package/dist/src/mapper/select/optimizer/helpers.d.ts +10 -0
  74. package/dist/src/mapper/select/optimizer/helpers.d.ts.map +1 -0
  75. package/dist/src/mapper/select/optimizer/helpers.js +72 -0
  76. package/dist/src/mapper/select/optimizer/helpers.js.map +1 -0
  77. package/dist/src/resolvers/GraphQlQueryAstFactory.js +2 -2
  78. package/dist/src/resolvers/GraphQlQueryAstFactory.js.map +1 -1
  79. package/dist/src/resolvers/MutationResolver.js +15 -15
  80. package/dist/src/resolvers/MutationResolver.js.map +1 -1
  81. package/dist/src/resolvers/ReadHelpers.js +10 -10
  82. package/dist/src/resolvers/ReadHelpers.js.map +1 -1
  83. package/dist/src/resolvers/ReadResolver.js +2 -2
  84. package/dist/src/resolvers/ReadResolver.js.map +1 -1
  85. package/dist/src/schema/MutationProvider.js +4 -4
  86. package/dist/src/schema/MutationProvider.js.map +1 -1
  87. package/dist/src/schema/QueryProvider.js +3 -3
  88. package/dist/src/schema/QueryProvider.js.map +1 -1
  89. package/dist/src/schema/ResultSchemaTypeProvider.d.ts +2 -2
  90. package/dist/src/schema/ResultSchemaTypeProvider.d.ts.map +1 -1
  91. package/dist/src/schema/entities/FieldArgsVisitor.d.ts +4 -4
  92. package/dist/src/schema/entities/FieldArgsVisitor.d.ts.map +1 -1
  93. package/dist/src/tsconfig.tsbuildinfo +1 -1
  94. package/dist/src/utils/object.d.ts.map +1 -1
  95. package/dist/src/utils/object.js +3 -4
  96. package/dist/src/utils/object.js.map +1 -1
  97. package/dist/tests/cases/integration/graphQlRequests/delete/aclDelete.test.js +1 -1
  98. package/dist/tests/cases/integration/graphQlRequests/delete/aclDelete.test.js.map +1 -1
  99. package/dist/tests/cases/integration/graphQlRequests/insert/acl/setField.test.js +2 -2
  100. package/dist/tests/cases/integration/graphQlRequests/insert/acl/setField.test.js.map +1 -1
  101. package/dist/tests/cases/integration/graphQlRequests/insert/aclInsert.test.js +1 -1
  102. package/dist/tests/cases/integration/graphQlRequests/insert/aclInsert.test.js.map +1 -1
  103. package/dist/tests/cases/integration/graphQlRequests/query/acl/fieldRestrictedByLocale.test.js +7 -6
  104. package/dist/tests/cases/integration/graphQlRequests/query/acl/fieldRestrictedByLocale.test.js.map +1 -1
  105. package/dist/tests/cases/integration/graphQlRequests/query/acl/nestedPredicate.test.js +1 -1
  106. package/dist/tests/cases/integration/graphQlRequests/query/acl/nestedPredicate.test.js.map +1 -1
  107. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.d.ts +2 -0
  108. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.d.ts.map +1 -0
  109. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.js +137 -0
  110. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.js.map +1 -0
  111. package/dist/tests/cases/integration/graphQlRequests/query/filter/complexCondition.test.js +1 -1
  112. package/dist/tests/cases/integration/graphQlRequests/query/filter/composedFilter.test.js +1 -1
  113. package/dist/tests/cases/integration/graphQlRequests/query/filter/composedFilter.test.js.map +1 -1
  114. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyInverse.test.js +73 -0
  115. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyInverse.test.js.map +1 -1
  116. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js +73 -0
  117. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js.map +1 -1
  118. package/dist/tests/cases/integration/graphQlRequests/update/acl/aclUpdate.test.js +6 -6
  119. package/dist/tests/cases/integration/graphQlRequests/update/acl/aclUpdate.test.js.map +1 -1
  120. package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js +6 -3
  121. package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js.map +1 -1
  122. package/dist/tests/cases/integration/graphQlSchema/model.js.map +1 -1
  123. package/dist/tests/cases/unit/aclVariablesFactory.test.js +7 -33
  124. package/dist/tests/cases/unit/aclVariablesFactory.test.js.map +1 -1
  125. package/dist/tests/cases/unit/conditionOptimizer.test.d.ts +2 -0
  126. package/dist/tests/cases/unit/conditionOptimizer.test.d.ts.map +1 -0
  127. package/dist/tests/cases/unit/conditionOptimizer.test.js +118 -0
  128. package/dist/tests/cases/unit/conditionOptimizer.test.js.map +1 -0
  129. package/dist/tests/cases/unit/orderbyHelper.test.js +1 -0
  130. package/dist/tests/cases/unit/orderbyHelper.test.js.map +1 -1
  131. package/dist/tests/cases/unit/permissionMerger.test.js +2 -0
  132. package/dist/tests/cases/unit/permissionMerger.test.js.map +1 -1
  133. package/dist/tests/cases/unit/predicatesFactory.test.d.ts +2 -0
  134. package/dist/tests/cases/unit/predicatesFactory.test.d.ts.map +1 -0
  135. package/dist/tests/cases/unit/predicatesFactory.test.js +103 -0
  136. package/dist/tests/cases/unit/predicatesFactory.test.js.map +1 -0
  137. package/dist/tests/cases/unit/predicatesInjector.test.js +88 -14
  138. package/dist/tests/cases/unit/predicatesInjector.test.js.map +1 -1
  139. package/dist/tests/cases/unit/variableInjector.test.js +4 -3
  140. package/dist/tests/cases/unit/variableInjector.test.js.map +1 -1
  141. package/dist/tests/cases/unit/whereOptimizer.test.d.ts +2 -0
  142. package/dist/tests/cases/unit/whereOptimizer.test.d.ts.map +1 -0
  143. package/dist/tests/cases/unit/whereOptimizer.test.js +183 -0
  144. package/dist/tests/cases/unit/whereOptimizer.test.js.map +1 -0
  145. package/dist/tests/src/testGraphql.d.ts.map +1 -1
  146. package/dist/tests/src/testGraphql.js +10 -2
  147. package/dist/tests/src/testGraphql.js.map +1 -1
  148. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  149. package/graphql.codegen.yml +0 -3
  150. package/package.json +16 -16
  151. package/src/acl/EvaluatedPredicateReplacer.ts +32 -0
  152. package/src/acl/PermissionFactory.ts +18 -13
  153. package/src/acl/PredicateFactory.ts +39 -8
  154. package/src/acl/PredicatesInjector.ts +26 -13
  155. package/src/acl/VariableInjector.ts +2 -15
  156. package/src/acl/VariableUtils.ts +7 -23
  157. package/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducerExecutionHandler.ts +3 -3
  158. package/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.ts +26 -28
  159. package/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.ts +27 -31
  160. package/src/input-validation/ValidationContext.ts +2 -2
  161. package/src/input-validation/ValidationDataSelector.ts +2 -2
  162. package/src/introspection/content-schema.types.ts +19 -286
  163. package/src/mapper/Mapper.ts +10 -6
  164. package/src/mapper/MapperContainer.ts +7 -3
  165. package/src/mapper/select/RelationFetcher.ts +95 -34
  166. package/src/mapper/select/WhereBuilder.ts +5 -2
  167. package/src/mapper/select/handlers/FieldsVisitor.ts +31 -24
  168. package/src/mapper/select/optimizer/ConditionOptimizer.ts +35 -0
  169. package/src/mapper/select/optimizer/WhereOptimizer.ts +100 -0
  170. package/src/mapper/select/optimizer/WhereReplacer.ts +22 -0
  171. package/src/mapper/select/optimizer/helpers.ts +58 -0
  172. package/src/resolvers/GraphQlQueryAstFactory.ts +3 -3
  173. package/src/resolvers/MutationResolver.ts +1 -1
  174. package/src/resolvers/ReadHelpers.ts +3 -3
  175. package/src/resolvers/ReadResolver.ts +2 -2
  176. package/src/utils/object.ts +6 -6
  177. package/tests/cases/integration/graphQlRequests/delete/aclDelete.test.ts +2 -2
  178. package/tests/cases/integration/graphQlRequests/insert/acl/setField.test.ts +3 -3
  179. package/tests/cases/integration/graphQlRequests/insert/aclInsert.test.ts +2 -2
  180. package/tests/cases/integration/graphQlRequests/query/acl/fieldRestrictedByLocale.test.ts +7 -6
  181. package/tests/cases/integration/graphQlRequests/query/acl/nestedPredicate.test.ts +1 -1
  182. package/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.ts +130 -0
  183. package/tests/cases/integration/graphQlRequests/query/filter/complexCondition.test.ts +1 -1
  184. package/tests/cases/integration/graphQlRequests/query/filter/composedFilter.test.ts +1 -1
  185. package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyInverse.test.ts +74 -0
  186. package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.ts +77 -0
  187. package/tests/cases/integration/graphQlRequests/update/acl/aclUpdate.test.ts +7 -7
  188. package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +5 -5
  189. package/tests/cases/unit/aclVariablesFactory.test.ts +8 -35
  190. package/tests/cases/unit/conditionOptimizer.test.ts +135 -0
  191. package/tests/cases/unit/orderbyHelper.test.ts +1 -0
  192. package/tests/cases/unit/permissionMerger.test.ts +2 -0
  193. package/tests/cases/unit/predicatesFactory.test.ts +97 -0
  194. package/tests/cases/unit/predicatesInjector.test.ts +78 -16
  195. package/tests/cases/unit/variableInjector.test.ts +6 -5
  196. package/tests/cases/unit/whereOptimizer.test.ts +195 -0
  197. package/tests/src/testGraphql.ts +9 -2
  198. package/jasmine.json +0 -7
@@ -10,9 +10,6 @@ generates:
10
10
  plugins:
11
11
  - "typescript"
12
12
  - "typescript-operations"
13
- - "typescript-resolvers"
14
- require:
15
- - "ts-node/register"
16
13
  hooks:
17
14
  afterAllFileWrite:
18
15
  - eslint --fix
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/engine-content-api",
3
- "version": "1.1.1",
3
+ "version": "1.2.0-alpha.10",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -10,25 +10,25 @@
10
10
  "test": "vitest --no-threads"
11
11
  },
12
12
  "dependencies": {
13
- "@contember/database": "^1.1.1",
14
- "@contember/dic": "^1.1.1",
15
- "@contember/engine-common": "^1.1.1",
16
- "@contember/graphql-utils": "^1.1.1",
17
- "@contember/schema": "^1.1.1",
18
- "@contember/schema-utils": "^1.1.1",
13
+ "@contember/database": "^1.2.0-alpha.10",
14
+ "@contember/dic": "^1.2.0-alpha.10",
15
+ "@contember/engine-common": "^1.2.0-alpha.10",
16
+ "@contember/graphql-utils": "^1.2.0-alpha.10",
17
+ "@contember/schema": "^1.2.0-alpha.10",
18
+ "@contember/schema-utils": "^1.2.0-alpha.10",
19
19
  "@graphql-tools/schema": "^8.3.5",
20
20
  "graphql-tag": "^2.12.5"
21
21
  },
22
22
  "devDependencies": {
23
- "@contember/database-tester": "^1.1.1",
24
- "@contember/engine-api-tester": "^1.1.1",
25
- "@contember/schema-definition": "^1.1.1",
26
- "@graphql-codegen/cli": "^1.15.2",
27
- "@graphql-codegen/typescript": "^1.15.2",
28
- "@graphql-codegen/typescript-operations": "^1.15.2",
29
- "@graphql-codegen/typescript-resolvers": "^1.15.2",
30
- "@types/node": "^17.0.5",
31
- "graphql": "^15.4.0",
23
+ "@contember/database-tester": "^1.2.0-alpha.10",
24
+ "@contember/engine-api-tester": "^1.2.0-alpha.10",
25
+ "@contember/schema-definition": "^1.2.0-alpha.10",
26
+ "@graphql-codegen/cli": "^2.6.2",
27
+ "@graphql-codegen/typescript": "^2.5.1",
28
+ "@graphql-codegen/typescript-operations": "^2.4.2",
29
+ "@graphql-codegen/typescript-resolvers": "^2.6.6",
30
+ "@types/node": "^18",
31
+ "graphql": "^16.5.0",
32
32
  "pg": "^8.5.0"
33
33
  },
34
34
  "peerDependencies": {
@@ -0,0 +1,32 @@
1
+ import { Input, Model, Writable } from '@contember/schema'
2
+ import { replaceWhere } from '../mapper/select/optimizer/WhereReplacer'
3
+
4
+ export class EvaluatedPredicateReplacer {
5
+ constructor(
6
+ private readonly evaluatedPredicate: Input.Where,
7
+ private readonly sourceEntity: Model.Entity,
8
+ private readonly relation: Model.AnyRelation,
9
+ ) {
10
+ }
11
+
12
+ public replace(where: Input.Where): Input.Where {
13
+ const result: Writable<Input.Where> = {}
14
+ for (const [key, value] of Object.entries(where)) {
15
+ if (key === 'not') {
16
+ result['not'] = this.replace(value as Input.Where)
17
+
18
+ } else if (key === 'and' || key === 'or') {
19
+ result[key] = (value as Input.Where[]).map(it => this.replace(it))
20
+
21
+ } else if (key === this.relation.name) {
22
+ result[key] = replaceWhere(value as Input.Where, this.evaluatedPredicate, { [this.sourceEntity.primary]: { always: true } })
23
+
24
+ } else {
25
+ result[key] = value
26
+ }
27
+
28
+ }
29
+ return result
30
+ }
31
+
32
+ }
@@ -17,7 +17,7 @@ export class PermissionFactory {
17
17
  }
18
18
  result = this.mergePermissions(result, rolePermissions)
19
19
  }
20
- this.makePrimaryPredicatesUnionOfAllFields(result)
20
+ result = this.makePrimaryPredicatesUnionOfAllFields(result)
21
21
 
22
22
  return result
23
23
  }
@@ -45,18 +45,20 @@ export class PermissionFactory {
45
45
  }))
46
46
  }
47
47
 
48
- private makePrimaryPredicatesUnionOfAllFields(permissions: Acl.Permissions): void {
49
- for (let entityName in permissions) {
48
+ private makePrimaryPredicatesUnionOfAllFields(permissions: Acl.Permissions): Acl.Permissions {
49
+ return mapObject(permissions, (permission, entityName): Acl.EntityPermissions => {
50
50
  const entity = getEntity(this.schema, entityName)
51
- const entityPermissions: Acl.EntityPermissions = permissions[entityName]
52
- const entityPredicates: Writable<Acl.PredicateMap> = entityPermissions.predicates
51
+ const entityPredicates: Writable<Acl.PredicateMap> = { ...permission.predicates }
52
+ const entityOperations: Writable<Acl.EntityOperations> = { ...permission.operations }
53
53
 
54
54
  const operationNames = ['read', 'create', 'update'] as const
55
55
  for (let operation of operationNames) {
56
- const fieldPermissions: Writable<Acl.FieldPermissions> | undefined = entityPermissions.operations[operation]
57
- if (fieldPermissions === undefined) {
56
+ if (!entityOperations[operation]) {
58
57
  continue
59
58
  }
59
+ const fieldPermissions: Writable<Acl.FieldPermissions> = { ...entityOperations[operation] }
60
+ entityOperations[operation] = fieldPermissions
61
+
60
62
  if (Object.values(fieldPermissions).some(it => it === true)) {
61
63
  fieldPermissions[entity.primary] = true
62
64
  }
@@ -70,25 +72,28 @@ export class PermissionFactory {
70
72
  .filter((value, index, array): value is string => array.indexOf(value) === index)
71
73
 
72
74
  let idPermissions: Acl.Predicate = fieldPermissions[entity.primary] || false
73
- const predicates = { ...entityPermissions.predicates }
74
75
 
75
76
  for (let predicateReference of predicateReferences) {
76
77
  const [predicateDefinition, predicate] = this.mergePredicates(
77
- predicates,
78
+ entityPredicates,
78
79
  idPermissions,
79
- predicates,
80
+ entityPredicates,
80
81
  predicateReference,
81
82
  )
82
83
  if (typeof predicate !== 'string' || predicateDefinition === undefined) {
83
84
  throw new Error('should not happen')
84
85
  }
85
86
  idPermissions = predicate
86
- predicates[predicate] = predicateDefinition
87
+ entityPredicates[predicate] = predicateDefinition
87
88
  }
88
89
  fieldPermissions[entity.primary] = idPermissions
89
- entityPredicates[idPermissions as Acl.PredicateReference] = predicates[idPermissions as Acl.PredicateReference]
90
+ entityPredicates[idPermissions as Acl.PredicateReference] = entityPredicates[idPermissions as Acl.PredicateReference]
90
91
  }
91
- }
92
+ return {
93
+ operations: entityOperations,
94
+ predicates: entityPredicates,
95
+ }
96
+ })
92
97
  }
93
98
 
94
99
  private mergePermissions(left: Acl.Permissions, right: Acl.Permissions): Acl.Permissions {
@@ -1,10 +1,17 @@
1
1
  import { Acl, Input, Model } from '@contember/schema'
2
2
  import { VariableInjector } from './VariableInjector'
3
+ import { isOwningRelation } from '@contember/schema-utils'
4
+ import { ImplementationException } from '../exception'
5
+ import { EvaluatedPredicateReplacer } from './EvaluatedPredicateReplacer'
3
6
 
4
7
  const getRowLevelPredicatePseudoField = (entity: Model.Entity) => entity.primary
5
8
 
6
9
  export class PredicateFactory {
7
- constructor(private readonly permissions: Acl.Permissions, private readonly variableInjector: VariableInjector) {}
10
+ constructor(
11
+ private readonly permissions: Acl.Permissions,
12
+ private readonly model: Model.Schema,
13
+ private readonly variableInjector: VariableInjector,
14
+ ) {}
8
15
 
9
16
  public shouldApplyCellLevelPredicate(
10
17
  entity: Model.Entity,
@@ -16,13 +23,22 @@ export class PredicateFactory {
16
23
  return permissions?.[fieldName] !== permissions?.[rowLevelField]
17
24
  }
18
25
 
19
- public create(entity: Model.Entity, operation: Acl.Operation.delete): Input.Where
26
+ public create(
27
+ entity: Model.Entity,
28
+ operation: Acl.Operation.delete,
29
+ ): Input.Where
20
30
  public create(
21
31
  entity: Model.Entity,
22
32
  operation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,
23
33
  fieldNames?: string[],
34
+ overRelation?: Model.AnyRelation,
24
35
  ): Input.Where
25
- public create(entity: Model.Entity, operation: Acl.Operation, fieldNames?: string[]): Input.Where {
36
+ public create(
37
+ entity: Model.Entity,
38
+ operation: Acl.Operation,
39
+ fieldNames?: string[],
40
+ overRelation?: Model.AnyRelation,
41
+ ): Input.Where {
26
42
  const entityPermissions: Acl.EntityPermissions = this.permissions[entity.name]
27
43
  const neverCondition: Input.Where = { [entity.primary]: { never: true } }
28
44
 
@@ -68,11 +84,8 @@ export class PredicateFactory {
68
84
  if (predicatesWhere.length === 0) {
69
85
  return {}
70
86
  }
71
- if (predicatesWhere.length === 1) {
72
- return predicatesWhere[0]
73
- }
74
-
75
- return { and: predicatesWhere }
87
+ const where: Input.Where = predicatesWhere.length === 1 ? predicatesWhere[0] : { and: predicatesWhere }
88
+ return this.optimizePredicates(where, overRelation)
76
89
  }
77
90
 
78
91
  private getRequiredPredicates(
@@ -94,4 +107,22 @@ export class PredicateFactory {
94
107
  }
95
108
  return predicates
96
109
  }
110
+
111
+ private optimizePredicates(where: Input.Where, overRelation?: Model.AnyRelation) {
112
+ if (!overRelation) {
113
+ return where
114
+ }
115
+ const otherSide = isOwningRelation(overRelation) ? overRelation.inversedBy : overRelation.ownedBy
116
+ if (!otherSide) {
117
+ throw new ImplementationException()
118
+ }
119
+ const sourceEntity = this.model.entities[overRelation.target]
120
+ const sourcePredicate = this.create(sourceEntity, Acl.Operation.read, [otherSide])
121
+ if (Object.keys(sourcePredicate).length === 0) {
122
+ return where
123
+ }
124
+
125
+ const replacer = new EvaluatedPredicateReplacer(sourcePredicate, sourceEntity, overRelation)
126
+ return replacer.replace(where)
127
+ }
97
128
  }
@@ -5,17 +5,18 @@ import { PredicateFactory } from './PredicateFactory'
5
5
  export class PredicatesInjector {
6
6
  constructor(private readonly schema: Model.Schema, private readonly predicateFactory: PredicateFactory) {}
7
7
 
8
- public inject(entity: Model.Entity, where: Input.OptionalWhere): Input.OptionalWhere {
9
- const restrictedWhere = this.injectToWhere(where, entity)
10
- return this.createWhere(entity, undefined, restrictedWhere)
8
+ public inject(entity: Model.Entity, where: Input.OptionalWhere, overRelation?: Model.AnyRelation): Input.OptionalWhere {
9
+ const restrictedWhere = this.injectToWhere(where, entity, true)
10
+ return this.createWhere(entity, undefined, restrictedWhere, overRelation)
11
11
  }
12
12
 
13
13
  private createWhere(
14
14
  entity: Model.Entity,
15
15
  fieldNames: string[] | undefined,
16
16
  where: Input.OptionalWhere,
17
+ overRelation?: Model.AnyRelation,
17
18
  ): Input.OptionalWhere {
18
- const predicatesWhere: Input.Where = this.predicateFactory.create(entity, Acl.Operation.read, fieldNames)
19
+ const predicatesWhere: Input.Where = this.predicateFactory.create(entity, Acl.Operation.read, fieldNames, overRelation)
19
20
 
20
21
  const and = [where, predicatesWhere].filter(it => Object.keys(it).length > 0)
21
22
  if (and.length === 0) {
@@ -27,32 +28,44 @@ export class PredicatesInjector {
27
28
  return { and: and }
28
29
  }
29
30
 
30
- private injectToWhere(where: Input.OptionalWhere, entity: Model.Entity): Input.OptionalWhere {
31
+ private injectToWhere(
32
+ where: Input.OptionalWhere,
33
+ entity: Model.Entity,
34
+ isRoot: boolean,
35
+ overRelation?: Model.AnyRelation,
36
+ ): Input.OptionalWhere {
31
37
  const resultWhere: Writable<Input.OptionalWhere> = {}
32
38
  if (where.and) {
33
- resultWhere.and = where.and.filter((it): it is Input.Where => !!it).map(it => this.injectToWhere(it, entity))
39
+ resultWhere.and = where.and.filter((it): it is Input.Where => !!it).map(it => this.injectToWhere(it, entity, isRoot, overRelation))
34
40
  }
35
41
  if (where.or) {
36
- resultWhere.or = where.or.filter((it): it is Input.Where => !!it).map(it => this.injectToWhere(it, entity))
42
+ resultWhere.or = where.or.filter((it): it is Input.Where => !!it).map(it => this.injectToWhere(it, entity, isRoot, overRelation))
37
43
  }
38
44
  if (where.not) {
39
- resultWhere.not = this.injectToWhere(where.not, entity)
45
+ resultWhere.not = this.injectToWhere(where.not, entity, isRoot, overRelation)
40
46
  }
47
+
41
48
  const fields = Object.keys(where).filter(it => !['and', 'or', 'not'].includes(it))
49
+
42
50
  if (fields.length === 0) {
43
51
  return resultWhere
44
52
  }
45
53
  for (let field of fields) {
46
- const targetEntity = acceptFieldVisitor(this.schema, entity, field, {
54
+ const [targetEntity, targetRelation] = acceptFieldVisitor(this.schema, entity, field, {
47
55
  visitColumn: () => null,
48
- visitRelation: (_a, _b, targetEntity) => targetEntity,
49
- })
56
+ visitRelation: (_a, _b, targetEntity, targetRelation) => [targetEntity, targetRelation ?? undefined],
57
+ }) ?? []
50
58
  if (targetEntity) {
51
- resultWhere[field] = this.injectToWhere(where[field] as Input.Where, targetEntity)
59
+ resultWhere[field] = this.injectToWhere(where[field] as Input.Where, targetEntity, false, targetRelation)
52
60
  } else {
53
61
  resultWhere[field] = where[field]
54
62
  }
55
63
  }
56
- return this.createWhere(entity, fields, resultWhere)
64
+ const fieldsForPredicate = !isRoot
65
+ ? fields
66
+ : fields.filter(it => this.predicateFactory.shouldApplyCellLevelPredicate(entity, Acl.Operation.read, it))
67
+
68
+ return this.createWhere(entity, fieldsForPredicate, resultWhere, overRelation)
57
69
  }
70
+
58
71
  }
@@ -10,29 +10,16 @@ export class VariableInjector {
10
10
  return predicateDefinitionProcessor.process(entity, where, {
11
11
  handleColumn: ({ value }) => {
12
12
  if (typeof value === 'string') {
13
- return this.createCondition(this.variables[value] || undefined)
13
+ return this.variables[value] ?? { never: true }
14
14
  }
15
15
  return value as Input.Condition
16
16
  },
17
17
  handleRelation: ({ value, targetEntity }) => {
18
18
  if (typeof value === 'string') {
19
- return { [targetEntity.primary]: this.createCondition(this.variables[value] || undefined) }
19
+ return { [targetEntity.primary]: this.variables[value] ?? { never: true } }
20
20
  }
21
21
  return value
22
22
  },
23
23
  })
24
24
  }
25
-
26
- private createCondition(variable: Acl.VariableValue | undefined): Input.Condition {
27
- if (variable === undefined) {
28
- return { never: true }
29
- }
30
- if (Array.isArray(variable)) {
31
- return { in: variable }
32
- }
33
- if (typeof variable === 'string' || typeof variable === 'number') {
34
- return { eq: variable }
35
- }
36
- throw new Error('not implemented')
37
- }
38
25
  }
@@ -1,36 +1,20 @@
1
- import { Acl } from '@contember/schema'
2
- import { getRoleVariables } from '@contember/schema-utils'
1
+ import { Acl, Input } from '@contember/schema'
2
+ import { getRoleVariables, ParsedMembership } from '@contember/schema-utils'
3
3
 
4
- type Memberships = readonly { role: string; variables: readonly { name: string; values: readonly string[] }[] }[]
5
4
 
6
5
  export const createAclVariables = (
7
6
  aclSchema: Acl.Schema,
8
- identity: {
9
- identityId: string
10
- personId: string | null
11
- memberships: Memberships
12
- },
7
+ memberships: readonly ParsedMembership[],
13
8
  ): Acl.VariablesMap => {
14
9
 
15
- const variablesEntries = identity.memberships.flatMap(
10
+ const variablesEntries = memberships.flatMap(
16
11
  membership => {
17
12
  const roleVariables = getRoleVariables(membership.role, aclSchema)
18
- const membershipVariablesMap = Object.fromEntries(membership.variables.map(it => [it.name, it.values]))
13
+ const membershipVariablesMap = Object.fromEntries(membership.variables.map(it => [it.name, it.condition]))
19
14
 
20
- return Object.entries(roleVariables).map(([name, def]) => {
15
+ return Object.keys(roleVariables).map((name): [string, Input.Condition] => {
21
16
  const prefixed = prefixVariable(membership.role, name)
22
-
23
- switch (def.type) {
24
- case Acl.VariableType.entity:
25
- return [prefixed, membershipVariablesMap[name] ?? []]
26
- case Acl.VariableType.predefined:
27
- switch (def.value) {
28
- case 'identityID':
29
- return [prefixed, [identity.identityId]]
30
- case 'personID':
31
- return [prefixed, identity.personId ? [identity.personId] : []]
32
- }
33
- }
17
+ return [prefixed, membershipVariablesMap[name] ?? { never: true }]
34
18
  })
35
19
  },
36
20
  )
@@ -31,7 +31,7 @@ export class HasManyToHasOneReducerExecutionHandler implements SelectExecutionHa
31
31
  }
32
32
  const newObjectNode = objectNode.withArgs<Input.ListQueryInput>({ filter: whereWithParentId })
33
33
 
34
- return context.mapper.select(targetEntity, newObjectNode, targetRelation.name)
34
+ return context.mapper.select(targetEntity, newObjectNode, targetRelation, targetRelation.name)
35
35
  },
36
36
  null,
37
37
  )
@@ -40,7 +40,7 @@ export class HasManyToHasOneReducerExecutionHandler implements SelectExecutionHa
40
40
  private getRelationTarget(
41
41
  entity: Model.Entity,
42
42
  relationName: string,
43
- ): [Model.Entity, Model.Relation & Model.JoiningColumnRelation] {
43
+ ): [Model.Entity, Model.AnyRelation & Model.JoiningColumnRelation] {
44
44
  return acceptFieldVisitor(this.schema, entity, relationName, {
45
45
  visitColumn: (): never => {
46
46
  throw new ImplementationException('HasManyToHasOneReducerExecutionHandler: Not applicable for a column')
@@ -50,7 +50,7 @@ export class HasManyToHasOneReducerExecutionHandler implements SelectExecutionHa
50
50
  relation,
51
51
  targetEntity,
52
52
  targetRelation,
53
- ): [Model.Entity, Model.Relation & Model.JoiningColumnRelation] => {
53
+ ): [Model.Entity, Model.AnyRelation & Model.JoiningColumnRelation] => {
54
54
  if (!targetRelation || !isIt<Model.JoiningColumnRelation>(targetRelation, 'joiningColumn')) {
55
55
  throw new Error('HasManyToHasOneReducerExecutionHandler: only applicable for relations with joiningColumn')
56
56
  }
@@ -1,5 +1,5 @@
1
1
  import { Input, Model } from '@contember/schema'
2
- import { GroupedCounts, JoiningColumns, Mapper, RelationFetcher } from '../../mapper'
2
+ import { GroupedCounts, Mapper, RelationFetcher } from '../../mapper'
3
3
  import { ObjectNode } from '../../inputProcessing'
4
4
 
5
5
  export class PaginatedHasManyCountVisitor implements
@@ -20,11 +20,13 @@ export class PaginatedHasManyCountVisitor implements
20
20
  targetRelation: Model.ManyHasOneRelation,
21
21
  ): Promise<GroupedCounts> {
22
22
  return await this.relationFetcher.countOneHasManyGroups(
23
- this.mapper,
24
- this.objectNode.args.filter,
25
- targetEntity,
26
- targetRelation,
27
- this.ids,
23
+ {
24
+ mapper: this.mapper,
25
+ filter: this.objectNode.args.filter,
26
+ targetEntity: targetEntity,
27
+ targetRelation: targetRelation,
28
+ ids: this.ids,
29
+ },
28
30
  )
29
31
  }
30
32
 
@@ -34,18 +36,16 @@ export class PaginatedHasManyCountVisitor implements
34
36
  targetEntity: Model.Entity,
35
37
  targetRelation: Model.ManyHasManyInverseRelation | null,
36
38
  ): Promise<GroupedCounts> {
37
- const joiningTable = relation.joiningTable
38
- const columns: JoiningColumns = {
39
- sourceColumn: joiningTable.joiningColumn,
40
- targetColumn: joiningTable.inverseJoiningColumn,
41
- }
42
39
  return await this.relationFetcher.countManyHasManyGroups(
43
- this.mapper,
44
- this.objectNode.args.filter,
45
- targetEntity,
46
- relation,
47
- this.ids,
48
- columns,
40
+ {
41
+ mapper: this.mapper,
42
+ filter: this.objectNode.args.filter,
43
+ targetEntity: targetEntity,
44
+ sourceRelation: relation,
45
+ targetRelation,
46
+ directionFrom: 'owning',
47
+ ids: this.ids,
48
+ },
49
49
  )
50
50
  }
51
51
 
@@ -55,18 +55,16 @@ export class PaginatedHasManyCountVisitor implements
55
55
  targetEntity: Model.Entity,
56
56
  targetRelation: Model.ManyHasManyOwningRelation,
57
57
  ): Promise<GroupedCounts> {
58
- const joiningTable = targetRelation.joiningTable
59
- const columns: JoiningColumns = {
60
- targetColumn: joiningTable.joiningColumn,
61
- sourceColumn: joiningTable.inverseJoiningColumn,
62
- }
63
58
  return await this.relationFetcher.countManyHasManyGroups(
64
- this.mapper,
65
- this.objectNode.args.filter,
66
- targetEntity,
67
- targetRelation,
68
- this.ids,
69
- columns,
59
+ {
60
+ mapper: this.mapper,
61
+ filter: this.objectNode.args.filter,
62
+ targetEntity: targetEntity,
63
+ sourceRelation: relation,
64
+ targetRelation,
65
+ directionFrom: 'inverse',
66
+ ids: this.ids,
67
+ },
70
68
  )
71
69
  }
72
70
 
@@ -1,5 +1,5 @@
1
1
  import { Input, Model } from '@contember/schema'
2
- import { GroupedCounts, JoiningColumns, Mapper, RelationFetcher, SelectGroupedObjects } from '../../mapper'
2
+ import { Mapper, RelationFetcher, SelectGroupedObjects } from '../../mapper'
3
3
  import { ObjectNode } from '../../inputProcessing'
4
4
 
5
5
  export class PaginatedHasManyNodesVisitor implements
@@ -20,12 +20,14 @@ export class PaginatedHasManyNodesVisitor implements
20
20
  targetRelation: Model.ManyHasOneRelation,
21
21
  ): Promise<SelectGroupedObjects> {
22
22
  return await this.relationFetcher.fetchOneHasManyGroups(
23
- this.mapper,
24
- this.objectNode,
25
- targetEntity,
26
- relation,
27
- targetRelation,
28
- this.ids,
23
+ {
24
+ mapper: this.mapper,
25
+ objectNode: this.objectNode,
26
+ targetEntity: targetEntity,
27
+ relation: relation,
28
+ targetRelation: targetRelation,
29
+ ids: this.ids,
30
+ },
29
31
  )
30
32
  }
31
33
 
@@ -35,19 +37,16 @@ export class PaginatedHasManyNodesVisitor implements
35
37
  targetEntity: Model.Entity,
36
38
  targetRelation: Model.ManyHasManyInverseRelation | null,
37
39
  ): Promise<SelectGroupedObjects> {
38
- const joiningTable = relation.joiningTable
39
- const columns: JoiningColumns = {
40
- sourceColumn: joiningTable.joiningColumn,
41
- targetColumn: joiningTable.inverseJoiningColumn,
42
- }
43
40
  return await this.relationFetcher.fetchManyHasManyGroups(
44
- this.mapper,
45
- this.objectNode,
46
- targetEntity,
47
- relation,
48
- relation,
49
- columns,
50
- this.ids,
41
+ {
42
+ mapper: this.mapper,
43
+ field: this.objectNode,
44
+ targetEntity: targetEntity,
45
+ sourceRelation: relation,
46
+ targetRelation,
47
+ directionFrom: 'owning',
48
+ ids: this.ids,
49
+ },
51
50
  )
52
51
  }
53
52
 
@@ -57,19 +56,16 @@ export class PaginatedHasManyNodesVisitor implements
57
56
  targetEntity: Model.Entity,
58
57
  targetRelation: Model.ManyHasManyOwningRelation,
59
58
  ): Promise<SelectGroupedObjects> {
60
- const joiningTable = targetRelation.joiningTable
61
- const columns: JoiningColumns = {
62
- targetColumn: joiningTable.joiningColumn,
63
- sourceColumn: joiningTable.inverseJoiningColumn,
64
- }
65
59
  return await this.relationFetcher.fetchManyHasManyGroups(
66
- this.mapper,
67
- this.objectNode,
68
- targetEntity,
69
- relation,
70
- targetRelation,
71
- columns,
72
- this.ids,
60
+ {
61
+ mapper: this.mapper,
62
+ field: this.objectNode,
63
+ targetEntity: targetEntity,
64
+ sourceRelation: relation,
65
+ targetRelation,
66
+ directionFrom: 'inverse',
67
+ ids: this.ids,
68
+ },
73
69
  )
74
70
  }
75
71
 
@@ -10,7 +10,7 @@ namespace ValidationContext {
10
10
  }
11
11
 
12
12
  export type NodeType = Value.Object
13
- export type ValueType = Value.FieldValue
13
+ export type ValueType = Value.FieldValue | undefined
14
14
 
15
15
  export enum NodeContextType {
16
16
  value = 'value',
@@ -69,7 +69,7 @@ namespace ValidationContext {
69
69
 
70
70
  export const createSingleNodeContext = (
71
71
  parent: NodeContext | ValueContext,
72
- value: object | null | string | number | boolean,
72
+ value: ValueType,
73
73
  ): NodeContext | ValueContext => {
74
74
  if (Array.isArray(value)) {
75
75
  throw new Error('Nested arrays are not allowed')
@@ -1,6 +1,6 @@
1
1
  import { QueryAstFactory } from './QueryAstFactory'
2
2
  import { Mapper } from '../mapper'
3
- import { Dependencies, DependencyCollector } from './dependencies'
3
+ import { Dependencies } from './dependencies'
4
4
  import { Input, Model, Value } from '@contember/schema'
5
5
 
6
6
  export class ValidationDataSelector {
@@ -20,7 +20,7 @@ export class ValidationDataSelector {
20
20
  return {}
21
21
  }
22
22
  const queryAst = this.queryAstFactory.create(entity.name, dependencies).withArg('by', where)
23
- const node = await mapper.selectUnique(entity, queryAst)
23
+ const node = await mapper.selectUnique(entity, queryAst, null)
24
24
  if (!node) {
25
25
  return null
26
26
  }