@contember/engine-content-api 1.3.7 → 1.3.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/engine-content-api",
3
- "version": "1.3.7",
3
+ "version": "1.3.9",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -13,20 +13,20 @@
13
13
  "test": "vitest --dir ./tests/cases --no-threads"
14
14
  },
15
15
  "dependencies": {
16
- "@contember/database": "1.3.7",
17
- "@contember/dic": "1.3.7",
18
- "@contember/graphql-utils": "1.3.7",
19
- "@contember/logger": "1.3.7",
20
- "@contember/schema": "1.3.7",
21
- "@contember/schema-definition": "1.3.7",
22
- "@contember/schema-utils": "1.3.7",
16
+ "@contember/database": "1.3.9",
17
+ "@contember/dic": "1.3.9",
18
+ "@contember/graphql-utils": "1.3.9",
19
+ "@contember/logger": "1.3.9",
20
+ "@contember/schema": "1.3.9",
21
+ "@contember/schema-definition": "1.3.9",
22
+ "@contember/schema-utils": "1.3.9",
23
23
  "fast-deep-equal": "^3.1.3",
24
24
  "graphql-tag": "^2.12.5"
25
25
  },
26
26
  "devDependencies": {
27
- "@contember/database-tester": "1.3.7",
28
- "@contember/engine-api-tester": "1.3.7",
29
- "@contember/schema-definition": "1.3.7",
27
+ "@contember/database-tester": "1.3.9",
28
+ "@contember/engine-api-tester": "1.3.9",
29
+ "@contember/schema-definition": "1.3.9",
30
30
  "@graphql-codegen/cli": "^2.6.2",
31
31
  "@graphql-codegen/typescript": "^2.5.1",
32
32
  "@graphql-codegen/typescript-operations": "^2.4.2",
@@ -1,4 +1,4 @@
1
- import { Acl, Input, Model } from '@contember/schema'
1
+ import { Acl, Input, Model, Value } from '@contember/schema'
2
2
  import { acceptFieldVisitor, getColumnName } from '@contember/schema-utils'
3
3
  import { ColumnValueGetter, SelectHydrator, SelectRow } from './SelectHydrator'
4
4
  import { Path, PathFactory } from './Path'
@@ -138,15 +138,21 @@ export class SelectBuilder {
138
138
  relationPath: this.relationPath,
139
139
  addData: async ({ field, dataProvider, defaultValue, predicate }) => {
140
140
  if (predicate === false) {
141
- this.hydrator.addPromise(fieldPath, path.for(field), Promise.resolve({}), defaultValue ?? null)
141
+ this.hydrator.addPromise(fieldPath, () => null, Promise.resolve({}), defaultValue ?? null)
142
142
  return
143
143
  }
144
144
  const predicateGetter = predicate !== undefined && predicate !== true ? addPredicate(predicate) : null
145
145
  const columnName = getColumnName(this.schema, entity, field)
146
- let ids = await this.getColumnValues(path.for(field), columnName, predicateGetter)
146
+ const parentValuePath = path.for(field)
147
+ let ids = await this.getColumnValues(parentValuePath, columnName, predicateGetter)
147
148
 
148
149
  const data = ids.length > 0 ? dataProvider(ids) : Promise.resolve({})
149
- this.hydrator.addPromise(fieldPath, path.for(field), data, defaultValue ?? null)
150
+ this.hydrator.addPromise(
151
+ fieldPath,
152
+ row => predicateGetter === null || predicateGetter(row) ? (row[parentValuePath.alias] as Value.PrimaryValue) : null,
153
+ data,
154
+ defaultValue ?? null,
155
+ )
150
156
  },
151
157
  addColumn: ({ path = fieldPath, predicate, query, valueGetter }) => {
152
158
  if (predicate === false) {
@@ -5,14 +5,14 @@ import { logger } from '@contember/logger'
5
5
 
6
6
  type DataPromises = {
7
7
  path: Path
8
- parentKeyPath: Path
8
+ getParentValue: ColumnValueGetter<Value.PrimaryValue | null>
9
9
  data: Promise<SelectNestedData>
10
10
  defaultValue: SelectNestedDefaultValue
11
11
  }
12
12
 
13
13
  type ResolvedData = {
14
14
  path: Path
15
- parentKeyPath: Path
15
+ getParentValue: ColumnValueGetter<Value.PrimaryValue | null>
16
16
  data: SelectNestedData
17
17
  defaultValue: SelectNestedDefaultValue
18
18
  }
@@ -34,11 +34,11 @@ export class SelectHydrator {
34
34
 
35
35
  public addPromise(
36
36
  path: Path,
37
- parentKeyPath: Path,
37
+ getParentValue: ColumnValueGetter<Value.PrimaryValue | null>,
38
38
  data: Promise<SelectNestedData>,
39
39
  defaultValue: SelectNestedDefaultValue,
40
40
  ) {
41
- this.promises.push({ path, parentKeyPath, data, defaultValue })
41
+ this.promises.push({ path, getParentValue, data, defaultValue })
42
42
  }
43
43
 
44
44
  public async hydrateGroups(rows: SelectRow[], groupBy: string): Promise<SelectGroupedObjects> {
@@ -87,11 +87,11 @@ export class SelectHydrator {
87
87
  currentObject[last] = this.formatValue(columnPath.getValue(row))
88
88
  }
89
89
 
90
- for (let { path, parentKeyPath, data, defaultValue } of resolvedData) {
90
+ for (let { path, getParentValue, data, defaultValue } of resolvedData) {
91
91
  const pathTmp = [...path.path]
92
92
  const last = pathTmp.pop() as string
93
93
  const currentObject = pathTmp.reduce<any>((obj, part) => (obj && obj[part]) || undefined, result)
94
- const parentValue = row[parentKeyPath.alias] as Value.PrimaryValue
94
+ const parentValue = getParentValue(row)
95
95
  if (currentObject) {
96
96
  currentObject[last] = (parentValue ? data[parentValue] : undefined) || defaultValue
97
97
  }
@@ -103,7 +103,7 @@ export class SelectHydrator {
103
103
  private async resolveDataPromises(): Promise<ResolvedData[]> {
104
104
  const results = await Promise.allSettled(this.promises.map(async (it): Promise<ResolvedData> => ({
105
105
  defaultValue: it.defaultValue,
106
- parentKeyPath: it.parentKeyPath,
106
+ getParentValue: it.getParentValue,
107
107
  path: it.path,
108
108
  data: await it.data,
109
109
  })))
@@ -0,0 +1,226 @@
1
+ import { c, createSchema } from '@contember/schema-definition'
2
+ import { test } from 'vitest'
3
+ import { execute } from '../../../../../src/test'
4
+ import { GQL, SQL } from '../../../../../src/tags'
5
+ import { PermissionFactory } from '../../../../../../src'
6
+ import { testUuid } from '@contember/engine-api-tester'
7
+
8
+
9
+ namespace RelationAclManyHasOne {
10
+ export const readerRole = c.createRole('reader')
11
+
12
+ @c.Allow(readerRole, {
13
+ read: ['title'],
14
+ })
15
+ @c.Allow(readerRole, {
16
+ when: { discloseAuthor: { eq: true } },
17
+ read: ['author'],
18
+ })
19
+ export class Article {
20
+ title = c.stringColumn().notNull()
21
+ author = c.manyHasOne(Author).notNull()
22
+ discloseAuthor = c.boolColumn().notNull()
23
+ }
24
+
25
+ @c.Allow(readerRole, {
26
+ read: true,
27
+ })
28
+ export class Author {
29
+ name = c.stringColumn().notNull()
30
+ }
31
+ }
32
+
33
+
34
+ test('can read same entity only over relation of some rows - many has one', async () => {
35
+ const schema = createSchema(RelationAclManyHasOne)
36
+
37
+ const permissions = new PermissionFactory().create(schema, ['reader'])
38
+
39
+ await execute({
40
+ schema: schema.model,
41
+ permissions: permissions,
42
+ variables: {},
43
+ query: GQL`
44
+ query {
45
+ listArticle {
46
+ title
47
+ author {
48
+ name
49
+ }
50
+ }
51
+ }`,
52
+ executes: [
53
+ {
54
+ sql: SQL`select "root_"."title" as "root_title", "root_"."disclose_author" = ? as "root___predicate_discloseAuthor_eq_true", "root_"."author_id" as "root_author", "root_"."id" as "root_id"
55
+ from "public"."article" as "root_"`,
56
+ response: {
57
+ rows: [
58
+ {
59
+ root_id: testUuid(1),
60
+ root_title: 'Article A',
61
+ root___predicate_discloseAuthor_eq_true: true,
62
+ root_author: testUuid(10),
63
+ },
64
+ {
65
+ root_id: testUuid(2),
66
+ root_title: 'Article B',
67
+ root___predicate_discloseAuthor_eq_true: false,
68
+ root_author: testUuid(10),
69
+ },
70
+ ],
71
+ },
72
+ },
73
+ {
74
+ sql: SQL`select "root_"."id" as "root_id", "root_"."name" as "root_name", "root_"."id" as "root_id" from "public"."author" as "root_" where "root_"."id" in (?)`,
75
+ parameters: [testUuid(10)],
76
+ response: {
77
+ rows: [
78
+ {
79
+ root_id: testUuid(10),
80
+ root_name: 'John doe',
81
+ },
82
+ ],
83
+ },
84
+ },
85
+ ],
86
+ return: {
87
+ data: {
88
+ listArticle: [
89
+ {
90
+ title: 'Article A',
91
+ author: {
92
+ name: 'John doe',
93
+ },
94
+ },
95
+ {
96
+ title: 'Article B',
97
+ author: null,
98
+ },
99
+ ],
100
+ },
101
+ },
102
+ })
103
+ })
104
+
105
+
106
+
107
+ namespace RelationAclManyHasMany {
108
+ export const readerRole = c.createRole('reader')
109
+
110
+ @c.Allow(readerRole, {
111
+ read: ['title'],
112
+ })
113
+ @c.Allow(readerRole, {
114
+ when: { discloseAuthor: { eq: true } },
115
+ read: ['authors'],
116
+ })
117
+ export class Article {
118
+ title = c.stringColumn().notNull()
119
+ authors = c.manyHasMany(Author)
120
+ discloseAuthor = c.boolColumn().notNull()
121
+ }
122
+
123
+ @c.Allow(readerRole, {
124
+ read: true,
125
+ })
126
+ export class Author {
127
+ name = c.stringColumn().notNull()
128
+ }
129
+ }
130
+
131
+
132
+ test('can read same entity only over relation of some rows - many has many', async () => {
133
+ const schema = createSchema(RelationAclManyHasMany)
134
+
135
+ const permissions = new PermissionFactory().create(schema, ['reader'])
136
+
137
+ await execute({
138
+ schema: schema.model,
139
+ permissions: permissions,
140
+ variables: {},
141
+ query: GQL`
142
+ query {
143
+ listArticle {
144
+ title
145
+ authors {
146
+ name
147
+ }
148
+ }
149
+ }`,
150
+ executes: [
151
+ {
152
+ sql: SQL`select "root_"."title" as "root_title", "root_"."disclose_author" = ? as "root___predicate_discloseauthor_eq_true", "root_"."id" as "root_id", "root_"."id" as "root_id"
153
+ from "public"."article" as "root_"`,
154
+ response: {
155
+ rows: [
156
+ {
157
+ root_id: testUuid(1),
158
+ root_title: 'Article A',
159
+ root___predicate_discloseAuthor_eq_true: true,
160
+ },
161
+ {
162
+ root_id: testUuid(2),
163
+ root_title: 'Article B',
164
+ root___predicate_discloseAuthor_eq_true: false,
165
+ },
166
+ ],
167
+ },
168
+ },
169
+ {
170
+ sql: SQL`select "junction_"."author_id", "junction_"."article_id" from "public"."article_authors" as "junction_" where "junction_"."article_id" in (?)`,
171
+ parameters: [testUuid(1)],
172
+ response: {
173
+ rows: [
174
+ {
175
+ article_id: testUuid(1),
176
+ author_id: testUuid(10),
177
+ },
178
+ {
179
+ article_id: testUuid(1),
180
+ author_id: testUuid(11),
181
+ },
182
+ ],
183
+ },
184
+ },
185
+ {
186
+ sql: SQL`select "root_"."name" as "root_name", "root_"."id" as "root_id" from "public"."author" as "root_" where "root_"."id" in (?, ?)`,
187
+ parameters: [testUuid(10), testUuid(11)],
188
+ response: {
189
+ rows: [
190
+ {
191
+ root_id: testUuid(10),
192
+ root_name: 'John doe',
193
+ },
194
+ {
195
+ root_id: testUuid(11),
196
+ root_name: 'Jack doe',
197
+ },
198
+ ],
199
+ },
200
+ },
201
+ ],
202
+ return: {
203
+ data: {
204
+ listArticle: [
205
+ {
206
+ title: 'Article A',
207
+ authors: [
208
+ {
209
+ name: 'John doe',
210
+ },
211
+ {
212
+ name: 'Jack doe',
213
+ },
214
+ ],
215
+ },
216
+ {
217
+ title: 'Article B',
218
+ authors: [],
219
+ },
220
+ ],
221
+ },
222
+ },
223
+ })
224
+ })
225
+
226
+