@contember/engine-content-api 2.0.0-beta.2 → 2.0.0-beta.4
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/dist/development/src/acl/Authorizator.cjs +3 -0
- package/dist/development/src/acl/Authorizator.cjs.map +1 -1
- package/dist/development/src/acl/Authorizator.js +3 -0
- package/dist/development/src/acl/Authorizator.js.map +1 -1
- package/dist/development/src/acl/PermissionFactory.cjs +11 -2
- package/dist/development/src/acl/PermissionFactory.cjs.map +1 -1
- package/dist/development/src/acl/PermissionFactory.js +11 -2
- package/dist/development/src/acl/PermissionFactory.js.map +1 -1
- package/dist/development/src/schema/MutationProvider.cjs +12 -0
- package/dist/development/src/schema/MutationProvider.cjs.map +1 -1
- package/dist/development/src/schema/MutationProvider.js +12 -0
- package/dist/development/src/schema/MutationProvider.js.map +1 -1
- package/dist/development/src/schema/QueryProvider.cjs +3 -0
- package/dist/development/src/schema/QueryProvider.cjs.map +1 -1
- package/dist/development/src/schema/QueryProvider.js +3 -0
- package/dist/development/src/schema/QueryProvider.js.map +1 -1
- package/dist/production/src/acl/Authorizator.cjs +3 -0
- package/dist/production/src/acl/Authorizator.cjs.map +1 -1
- package/dist/production/src/acl/Authorizator.js +3 -0
- package/dist/production/src/acl/Authorizator.js.map +1 -1
- package/dist/production/src/acl/PermissionFactory.cjs +11 -2
- package/dist/production/src/acl/PermissionFactory.cjs.map +1 -1
- package/dist/production/src/acl/PermissionFactory.js +11 -2
- package/dist/production/src/acl/PermissionFactory.js.map +1 -1
- package/dist/production/src/schema/MutationProvider.cjs +12 -0
- package/dist/production/src/schema/MutationProvider.cjs.map +1 -1
- package/dist/production/src/schema/MutationProvider.js +12 -0
- package/dist/production/src/schema/MutationProvider.js.map +1 -1
- package/dist/production/src/schema/QueryProvider.cjs +3 -0
- package/dist/production/src/schema/QueryProvider.cjs.map +1 -1
- package/dist/production/src/schema/QueryProvider.js +3 -0
- package/dist/production/src/schema/QueryProvider.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/acl/Authorizator.d.ts +1 -0
- package/dist/types/acl/Authorizator.d.ts.map +1 -1
- package/dist/types/acl/PermissionFactory.d.ts.map +1 -1
- package/dist/types/schema/MutationProvider.d.ts.map +1 -1
- package/dist/types/schema/QueryProvider.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/acl/Authorizator.ts +4 -0
- package/src/acl/PermissionFactory.ts +12 -2
- package/src/schema/MutationProvider.ts +13 -0
- package/src/schema/QueryProvider.ts +3 -0
- package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +43 -1
- package/tests/cases/integration/graphQlSchema/schema-no-root-ops.gql +308 -0
- package/tests/cases/unit/permissionMerger.test.ts +120 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-content-api",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.4",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/production/index.js",
|
|
6
6
|
"typings": "./dist/types/index.d.ts",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
"generate": "gql-gen --config graphql.codegen.yml"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@contember/database": "2.0.0-beta.
|
|
30
|
-
"@contember/dic": "2.0.0-beta.
|
|
31
|
-
"@contember/graphql-utils": "2.0.0-beta.
|
|
32
|
-
"@contember/logger": "2.0.0-beta.
|
|
33
|
-
"@contember/schema": "2.0.0-beta.
|
|
34
|
-
"@contember/schema-definition": "2.0.0-beta.
|
|
35
|
-
"@contember/schema-utils": "2.0.0-beta.
|
|
29
|
+
"@contember/database": "2.0.0-beta.4",
|
|
30
|
+
"@contember/dic": "2.0.0-beta.4",
|
|
31
|
+
"@contember/graphql-utils": "2.0.0-beta.4",
|
|
32
|
+
"@contember/logger": "2.0.0-beta.4",
|
|
33
|
+
"@contember/schema": "2.0.0-beta.4",
|
|
34
|
+
"@contember/schema-definition": "2.0.0-beta.4",
|
|
35
|
+
"@contember/schema-utils": "2.0.0-beta.4",
|
|
36
36
|
"fast-deep-equal": "^3.1.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@contember/database-tester": "2.0.0-beta.
|
|
40
|
-
"@contember/schema-definition": "2.0.0-beta.
|
|
39
|
+
"@contember/database-tester": "2.0.0-beta.4",
|
|
40
|
+
"@contember/schema-definition": "2.0.0-beta.4",
|
|
41
41
|
"@types/node": "^20.16.11",
|
|
42
42
|
"graphql": "^16.9.0",
|
|
43
43
|
"pg": "^8.12.0"
|
package/src/acl/Authorizator.ts
CHANGED
|
@@ -52,4 +52,8 @@ export class Authorizator {
|
|
|
52
52
|
isCustomPrimaryAllowed(entity: string): boolean {
|
|
53
53
|
return this.permissions?.[entity]?.operations?.customPrimary ?? this.defaultCustomPrimary
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
isRootOperationDisallowed(entity: string, operation: Acl.Operation): boolean {
|
|
57
|
+
return this.permissions?.[entity]?.operations?.noRoot?.includes(operation) ?? false
|
|
58
|
+
}
|
|
55
59
|
}
|
|
@@ -116,6 +116,7 @@ export class PermissionFactory {
|
|
|
116
116
|
if (left.operations.customPrimary || right.operations.customPrimary) {
|
|
117
117
|
operations.customPrimary = true
|
|
118
118
|
}
|
|
119
|
+
const noRoot: `${Acl.Operation}`[] = []
|
|
119
120
|
|
|
120
121
|
const operationNames: (keyof Pick<Acl.EntityOperations, 'create' | 'read' | 'update'>)[] = [
|
|
121
122
|
'create',
|
|
@@ -124,8 +125,14 @@ export class PermissionFactory {
|
|
|
124
125
|
]
|
|
125
126
|
|
|
126
127
|
for (let operation of operationNames) {
|
|
127
|
-
const
|
|
128
|
-
const
|
|
128
|
+
const leftNoRoot = left.operations.noRoot?.includes(operation) || false
|
|
129
|
+
const rightNoRoot = right.operations.noRoot?.includes(operation) || false
|
|
130
|
+
if (leftNoRoot && rightNoRoot) {
|
|
131
|
+
noRoot.push(operation)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const leftFieldPermissions: Acl.FieldPermissions = leftNoRoot && !rightNoRoot ? {} : (left.operations[operation] || {})
|
|
135
|
+
const rightFieldPermissions: Acl.FieldPermissions = rightNoRoot && !leftNoRoot ? {} : (right.operations[operation] || {})
|
|
129
136
|
const [operationPredicates, fieldPermissions] = this.mergeFieldPermissions(
|
|
130
137
|
left.predicates,
|
|
131
138
|
leftFieldPermissions,
|
|
@@ -150,6 +157,9 @@ export class PermissionFactory {
|
|
|
150
157
|
predicates[predicate] = predicateDefinition
|
|
151
158
|
operations.delete = predicate
|
|
152
159
|
}
|
|
160
|
+
if (noRoot.length > 0) {
|
|
161
|
+
operations.noRoot = noRoot
|
|
162
|
+
}
|
|
153
163
|
|
|
154
164
|
return {
|
|
155
165
|
predicates: predicates,
|
|
@@ -41,6 +41,9 @@ export class MutationProvider {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
protected getCreateMutation(entity: Model.Entity): FieldConfig<Input.CreateInput> | undefined {
|
|
44
|
+
if (this.authorizator.isRootOperationDisallowed(entity.name, Acl.Operation.create)) {
|
|
45
|
+
return undefined
|
|
46
|
+
}
|
|
44
47
|
const entityName = entity.name
|
|
45
48
|
const dataType = this.createEntityInputProvider.getInput(entityName)
|
|
46
49
|
if (dataType === undefined) {
|
|
@@ -66,6 +69,9 @@ export class MutationProvider {
|
|
|
66
69
|
|
|
67
70
|
protected getDeleteMutation(entity: Model.Entity): FieldConfig<Input.DeleteInput> | undefined {
|
|
68
71
|
const entityName = entity.name
|
|
72
|
+
if (this.authorizator.isRootOperationDisallowed(entityName, Acl.Operation.delete)) {
|
|
73
|
+
return undefined
|
|
74
|
+
}
|
|
69
75
|
if (entity.view) {
|
|
70
76
|
return undefined
|
|
71
77
|
}
|
|
@@ -99,6 +105,9 @@ export class MutationProvider {
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
protected getUpdateMutation(entity: Model.Entity): FieldConfig<Input.UpdateInput> | undefined {
|
|
108
|
+
if (this.authorizator.isRootOperationDisallowed(entity.name, Acl.Operation.update)) {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
102
111
|
const entityName = entity.name
|
|
103
112
|
const dataType = this.updateEntityInputProvider.getInput(entityName)
|
|
104
113
|
if (dataType === undefined) {
|
|
@@ -133,6 +142,10 @@ export class MutationProvider {
|
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
private getUpsertMutation(entity: Model.Entity): FieldConfig<Input.UpsertInput> | undefined {
|
|
145
|
+
if (this.authorizator.isRootOperationDisallowed(entity.name, Acl.Operation.update)
|
|
146
|
+
|| this.authorizator.isRootOperationDisallowed(entity.name, Acl.Operation.create)) {
|
|
147
|
+
return undefined
|
|
148
|
+
}
|
|
136
149
|
const entityName = entity.name
|
|
137
150
|
const createInput = this.createEntityInputProvider.getInput(entityName)
|
|
138
151
|
if (createInput === undefined) {
|
|
@@ -22,6 +22,9 @@ export class QueryProvider {
|
|
|
22
22
|
if (this.authorizator.getEntityPermission(Acl.Operation.read, entity.name) === 'no') {
|
|
23
23
|
return {}
|
|
24
24
|
}
|
|
25
|
+
if (this.authorizator.isRootOperationDisallowed(entity.name, Acl.Operation.read)) {
|
|
26
|
+
return {}
|
|
27
|
+
}
|
|
25
28
|
return {
|
|
26
29
|
['get' + entity.name]: this.getByUniqueQuery(entity),
|
|
27
30
|
['list' + entity.name]: this.getListQuery(entity),
|
|
@@ -6,9 +6,11 @@ import {
|
|
|
6
6
|
AllowAllPermissionFactory,
|
|
7
7
|
SchemaBuilder,
|
|
8
8
|
SchemaDefinition,
|
|
9
|
+
c,
|
|
10
|
+
createSchema,
|
|
9
11
|
SchemaDefinition as def,
|
|
10
12
|
} from '@contember/schema-definition'
|
|
11
|
-
import { Authorizator, GraphQlSchemaBuilderFactory } from '../../../../src'
|
|
13
|
+
import { Authorizator, GraphQlSchemaBuilderFactory, PermissionFactory } from '../../../../src'
|
|
12
14
|
import * as model from './model'
|
|
13
15
|
import { expect, describe, it } from 'bun:test'
|
|
14
16
|
import { dirname } from 'node:path'
|
|
@@ -387,6 +389,20 @@ describe('GraphQL schema builder', () => {
|
|
|
387
389
|
graphQlSchemaFile: 'schema-view-entity.gql',
|
|
388
390
|
})
|
|
389
391
|
})
|
|
392
|
+
|
|
393
|
+
it.only('no root ops', async () => {
|
|
394
|
+
const schema = createSchema(NoRootOperation)
|
|
395
|
+
console.log(schema.acl.roles.editor.entities.Image)
|
|
396
|
+
|
|
397
|
+
await testSchema({
|
|
398
|
+
schema: () => schema.model,
|
|
399
|
+
permissions: () => {
|
|
400
|
+
const factory = new PermissionFactory()
|
|
401
|
+
return factory.create(schema, ['editor'])
|
|
402
|
+
},
|
|
403
|
+
graphQlSchemaFile: 'schema-no-root-ops.gql',
|
|
404
|
+
})
|
|
405
|
+
})
|
|
390
406
|
})
|
|
391
407
|
|
|
392
408
|
namespace ViewEntity {
|
|
@@ -395,3 +411,29 @@ namespace ViewEntity {
|
|
|
395
411
|
name = def.stringColumn()
|
|
396
412
|
}
|
|
397
413
|
}
|
|
414
|
+
|
|
415
|
+
namespace NoRootOperation {
|
|
416
|
+
export const editorRole = c.createRole('editor')
|
|
417
|
+
|
|
418
|
+
@c.Allow(editorRole, {
|
|
419
|
+
read: true,
|
|
420
|
+
create: true,
|
|
421
|
+
delete: true,
|
|
422
|
+
update: true,
|
|
423
|
+
})
|
|
424
|
+
export class Article {
|
|
425
|
+
title = c.stringColumn()
|
|
426
|
+
coverImage = c.oneHasOne(Image)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
@c.Allow(editorRole, {
|
|
430
|
+
through: true,
|
|
431
|
+
read: true,
|
|
432
|
+
create: true,
|
|
433
|
+
delete: true,
|
|
434
|
+
update: true,
|
|
435
|
+
})
|
|
436
|
+
export class Image {
|
|
437
|
+
url = c.stringColumn()
|
|
438
|
+
}
|
|
439
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
type Query {
|
|
2
|
+
getArticle(by: ArticleUniqueWhere!, filter: ArticleWhere): Article
|
|
3
|
+
listArticle(filter: ArticleWhere, orderBy: [ArticleOrderBy!], offset: Int, limit: Int): [Article!]!
|
|
4
|
+
paginateArticle(filter: ArticleWhere, orderBy: [ArticleOrderBy!], skip: Int, first: Int): ArticleConnection!
|
|
5
|
+
validateCreateArticle(data: ArticleCreateInput!): _ValidationResult!
|
|
6
|
+
validateUpdateArticle(by: ArticleUniqueWhere!, data: ArticleUpdateInput!): _ValidationResult!
|
|
7
|
+
validateCreateImage(data: ImageCreateInput!): _ValidationResult!
|
|
8
|
+
validateUpdateImage(by: ImageUniqueWhere!, data: ImageUpdateInput!): _ValidationResult!
|
|
9
|
+
transaction: QueryTransaction
|
|
10
|
+
_info: Info
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Article {
|
|
14
|
+
_meta: ArticleMeta
|
|
15
|
+
id: UUID!
|
|
16
|
+
title: String
|
|
17
|
+
coverImage(filter: ImageWhere): Image
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ArticleMeta {
|
|
21
|
+
id: FieldMeta
|
|
22
|
+
title: FieldMeta
|
|
23
|
+
coverImage: FieldMeta
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type FieldMeta {
|
|
27
|
+
readable: Boolean
|
|
28
|
+
updatable: Boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
scalar UUID
|
|
32
|
+
|
|
33
|
+
type Image {
|
|
34
|
+
_meta: ImageMeta
|
|
35
|
+
id: UUID!
|
|
36
|
+
url: String
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type ImageMeta {
|
|
40
|
+
id: FieldMeta
|
|
41
|
+
url: FieldMeta
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
input ImageWhere {
|
|
45
|
+
id: UUIDCondition
|
|
46
|
+
url: StringCondition
|
|
47
|
+
and: [ImageWhere]
|
|
48
|
+
or: [ImageWhere]
|
|
49
|
+
not: ImageWhere
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
input UUIDCondition {
|
|
53
|
+
and: [UUIDCondition!]
|
|
54
|
+
or: [UUIDCondition!]
|
|
55
|
+
not: UUIDCondition
|
|
56
|
+
null: Boolean
|
|
57
|
+
isNull: Boolean
|
|
58
|
+
eq: UUID
|
|
59
|
+
notEq: UUID
|
|
60
|
+
in: [UUID!]
|
|
61
|
+
notIn: [UUID!]
|
|
62
|
+
lt: UUID
|
|
63
|
+
lte: UUID
|
|
64
|
+
gt: UUID
|
|
65
|
+
gte: UUID
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
input StringCondition {
|
|
69
|
+
and: [StringCondition!]
|
|
70
|
+
or: [StringCondition!]
|
|
71
|
+
not: StringCondition
|
|
72
|
+
null: Boolean
|
|
73
|
+
isNull: Boolean
|
|
74
|
+
eq: String
|
|
75
|
+
notEq: String
|
|
76
|
+
in: [String!]
|
|
77
|
+
notIn: [String!]
|
|
78
|
+
lt: String
|
|
79
|
+
lte: String
|
|
80
|
+
gt: String
|
|
81
|
+
gte: String
|
|
82
|
+
contains: String
|
|
83
|
+
startsWith: String
|
|
84
|
+
endsWith: String
|
|
85
|
+
containsCI: String
|
|
86
|
+
startsWithCI: String
|
|
87
|
+
endsWithCI: String
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
input ArticleUniqueWhere {
|
|
91
|
+
id: UUID
|
|
92
|
+
coverImage: ImageUniqueWhere
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
input ImageUniqueWhere {
|
|
96
|
+
id: UUID
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
input ArticleWhere {
|
|
100
|
+
id: UUIDCondition
|
|
101
|
+
title: StringCondition
|
|
102
|
+
coverImage: ImageWhere
|
|
103
|
+
and: [ArticleWhere]
|
|
104
|
+
or: [ArticleWhere]
|
|
105
|
+
not: ArticleWhere
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
input ArticleOrderBy {
|
|
109
|
+
_random: Boolean
|
|
110
|
+
_randomSeeded: Int
|
|
111
|
+
id: OrderDirection
|
|
112
|
+
title: OrderDirection
|
|
113
|
+
coverImage: ImageOrderBy
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
enum OrderDirection {
|
|
117
|
+
asc
|
|
118
|
+
desc
|
|
119
|
+
ascNullsFirst
|
|
120
|
+
descNullsLast
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
input ImageOrderBy {
|
|
124
|
+
_random: Boolean
|
|
125
|
+
_randomSeeded: Int
|
|
126
|
+
id: OrderDirection
|
|
127
|
+
url: OrderDirection
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
type ArticleConnection {
|
|
131
|
+
pageInfo: PageInfo!
|
|
132
|
+
edges: [ArticleEdge!]!
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
type PageInfo {
|
|
136
|
+
totalCount: Int!
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
type ArticleEdge {
|
|
140
|
+
node: Article!
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
type _ValidationResult {
|
|
144
|
+
valid: Boolean!
|
|
145
|
+
errors: [_ValidationError!]!
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
type _ValidationError {
|
|
149
|
+
path: [_PathFragment!]!
|
|
150
|
+
message: _ValidationMessage!
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
union _PathFragment = _FieldPathFragment | _IndexPathFragment
|
|
154
|
+
|
|
155
|
+
type _FieldPathFragment {
|
|
156
|
+
field: String!
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type _IndexPathFragment {
|
|
160
|
+
index: Int!
|
|
161
|
+
alias: String
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
type _ValidationMessage {
|
|
165
|
+
text: String!
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
input ArticleCreateInput {
|
|
169
|
+
title: String
|
|
170
|
+
coverImage: ArticleCreateCoverImageEntityRelationInput
|
|
171
|
+
_dummy_field_: Boolean
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
input ArticleCreateCoverImageEntityRelationInput {
|
|
175
|
+
connect: ImageUniqueWhere
|
|
176
|
+
create: ImageCreateInput
|
|
177
|
+
connectOrCreate: ArticleConnectOrCreateCoverImageRelationInput
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
input ImageCreateInput {
|
|
181
|
+
url: String
|
|
182
|
+
_dummy_field_: Boolean
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
input ArticleConnectOrCreateCoverImageRelationInput {
|
|
186
|
+
connect: ImageUniqueWhere!
|
|
187
|
+
create: ImageCreateInput!
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
input ArticleUpdateInput {
|
|
191
|
+
title: String
|
|
192
|
+
coverImage: ArticleUpdateCoverImageEntityRelationInput
|
|
193
|
+
_dummy_field_: Boolean
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
input ArticleUpdateCoverImageEntityRelationInput {
|
|
197
|
+
connect: ImageUniqueWhere
|
|
198
|
+
create: ImageCreateInput
|
|
199
|
+
connectOrCreate: ArticleConnectOrCreateCoverImageRelationInput
|
|
200
|
+
update: ImageUpdateInput
|
|
201
|
+
upsert: ArticleUpsertCoverImageRelationInput
|
|
202
|
+
disconnect: Boolean
|
|
203
|
+
delete: Boolean
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
input ImageUpdateInput {
|
|
207
|
+
url: String
|
|
208
|
+
_dummy_field_: Boolean
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
input ArticleUpsertCoverImageRelationInput {
|
|
212
|
+
update: ImageUpdateInput!
|
|
213
|
+
create: ImageCreateInput!
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
type QueryTransaction {
|
|
217
|
+
getArticle(by: ArticleUniqueWhere!, filter: ArticleWhere): Article
|
|
218
|
+
listArticle(filter: ArticleWhere, orderBy: [ArticleOrderBy!], offset: Int, limit: Int): [Article!]!
|
|
219
|
+
paginateArticle(filter: ArticleWhere, orderBy: [ArticleOrderBy!], skip: Int, first: Int): ArticleConnection!
|
|
220
|
+
validateCreateArticle(data: ArticleCreateInput!): _ValidationResult!
|
|
221
|
+
validateUpdateArticle(by: ArticleUniqueWhere!, data: ArticleUpdateInput!): _ValidationResult!
|
|
222
|
+
validateCreateImage(data: ImageCreateInput!): _ValidationResult!
|
|
223
|
+
validateUpdateImage(by: ImageUniqueWhere!, data: ImageUpdateInput!): _ValidationResult!
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
type Info {
|
|
227
|
+
description: String
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
type Mutation {
|
|
231
|
+
createArticle(data: ArticleCreateInput!): ArticleCreateResult!
|
|
232
|
+
deleteArticle(by: ArticleUniqueWhere!, filter: ArticleWhere): ArticleDeleteResult!
|
|
233
|
+
updateArticle(by: ArticleUniqueWhere!, filter: ArticleWhere, data: ArticleUpdateInput!): ArticleUpdateResult!
|
|
234
|
+
upsertArticle(by: ArticleUniqueWhere!, filter: ArticleWhere, update: ArticleUpdateInput!, create: ArticleCreateInput!): ArticleUpsertResult!
|
|
235
|
+
transaction(options: MutationTransactionOptions): MutationTransaction!
|
|
236
|
+
query: Query!
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
type ArticleCreateResult implements MutationResult {
|
|
240
|
+
ok: Boolean!
|
|
241
|
+
errorMessage: String
|
|
242
|
+
errors: [_MutationError!]!
|
|
243
|
+
node: Article
|
|
244
|
+
validation: _ValidationResult!
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
interface MutationResult {
|
|
248
|
+
ok: Boolean!
|
|
249
|
+
errorMessage: String
|
|
250
|
+
errors: [_MutationError!]!
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
type _MutationError {
|
|
254
|
+
path: [_PathFragment!]! @deprecated(reason: "Use `paths`.")
|
|
255
|
+
paths: [[_PathFragment!]!]!
|
|
256
|
+
type: _MutationErrorType!
|
|
257
|
+
message: String
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
enum _MutationErrorType {
|
|
261
|
+
NotNullConstraintViolation
|
|
262
|
+
UniqueConstraintViolation
|
|
263
|
+
ForeignKeyConstraintViolation
|
|
264
|
+
NotFoundOrDenied
|
|
265
|
+
NonUniqueWhereInput
|
|
266
|
+
InvalidDataInput
|
|
267
|
+
SqlError
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
type ArticleDeleteResult implements MutationResult {
|
|
271
|
+
ok: Boolean!
|
|
272
|
+
errorMessage: String
|
|
273
|
+
errors: [_MutationError!]!
|
|
274
|
+
node: Article
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
type ArticleUpdateResult implements MutationResult {
|
|
278
|
+
ok: Boolean!
|
|
279
|
+
errorMessage: String
|
|
280
|
+
errors: [_MutationError!]!
|
|
281
|
+
node: Article
|
|
282
|
+
validation: _ValidationResult!
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
type ArticleUpsertResult implements MutationResult {
|
|
286
|
+
ok: Boolean!
|
|
287
|
+
errorMessage: String
|
|
288
|
+
errors: [_MutationError!]!
|
|
289
|
+
node: Article
|
|
290
|
+
validation: _ValidationResult!
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
type MutationTransaction {
|
|
294
|
+
ok: Boolean!
|
|
295
|
+
errorMessage: String
|
|
296
|
+
errors: [_MutationError!]!
|
|
297
|
+
validation: _ValidationResult!
|
|
298
|
+
createArticle(data: ArticleCreateInput!): ArticleCreateResult!
|
|
299
|
+
deleteArticle(by: ArticleUniqueWhere!, filter: ArticleWhere): ArticleDeleteResult!
|
|
300
|
+
updateArticle(by: ArticleUniqueWhere!, filter: ArticleWhere, data: ArticleUpdateInput!): ArticleUpdateResult!
|
|
301
|
+
upsertArticle(by: ArticleUniqueWhere!, filter: ArticleWhere, update: ArticleUpdateInput!, create: ArticleCreateInput!): ArticleUpsertResult!
|
|
302
|
+
query: Query
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
input MutationTransactionOptions {
|
|
306
|
+
deferForeignKeyConstraints: Boolean
|
|
307
|
+
deferUniqueConstraints: Boolean
|
|
308
|
+
}
|
|
@@ -550,4 +550,124 @@ describe('Permission merger', () => {
|
|
|
550
550
|
})
|
|
551
551
|
|
|
552
552
|
|
|
553
|
+
|
|
554
|
+
it('prefer permissions with root allowed', () => {
|
|
555
|
+
execute({
|
|
556
|
+
acl: {
|
|
557
|
+
roles: {
|
|
558
|
+
role1: {
|
|
559
|
+
variables: {},
|
|
560
|
+
entities: {
|
|
561
|
+
Entity2: {
|
|
562
|
+
predicates: {
|
|
563
|
+
foo: { xyz: { lorem: { eq: 'foo' } } },
|
|
564
|
+
},
|
|
565
|
+
operations: {
|
|
566
|
+
read: {
|
|
567
|
+
title: 'foo',
|
|
568
|
+
},
|
|
569
|
+
noRoot: ['read'],
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
role2: {
|
|
575
|
+
variables: {},
|
|
576
|
+
entities: {
|
|
577
|
+
Entity2: {
|
|
578
|
+
predicates: {
|
|
579
|
+
foo: { xyz: { lorem: { eq: 'bar' } } },
|
|
580
|
+
},
|
|
581
|
+
operations: {
|
|
582
|
+
read: {
|
|
583
|
+
title: 'foo',
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
roles: ['role1', 'role2'],
|
|
592
|
+
result: {
|
|
593
|
+
Entity2: {
|
|
594
|
+
operations: {
|
|
595
|
+
read: {
|
|
596
|
+
title: 'foo_',
|
|
597
|
+
id: 'foo_',
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
predicates: {
|
|
601
|
+
foo_: {
|
|
602
|
+
xyz: {
|
|
603
|
+
lorem: { eq: 'bar' },
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
})
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
it('merge permissions with no root', () => {
|
|
614
|
+
execute({
|
|
615
|
+
acl: {
|
|
616
|
+
roles: {
|
|
617
|
+
role1: {
|
|
618
|
+
variables: {},
|
|
619
|
+
entities: {
|
|
620
|
+
Entity2: {
|
|
621
|
+
predicates: {
|
|
622
|
+
foo: { xyz: { lorem: { eq: 'foo' } } },
|
|
623
|
+
},
|
|
624
|
+
operations: {
|
|
625
|
+
read: {
|
|
626
|
+
title: 'foo',
|
|
627
|
+
},
|
|
628
|
+
noRoot: ['read'],
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
role2: {
|
|
634
|
+
variables: {},
|
|
635
|
+
entities: {
|
|
636
|
+
Entity2: {
|
|
637
|
+
predicates: {
|
|
638
|
+
foo: { xyz: { lorem: { eq: 'bar' } } },
|
|
639
|
+
},
|
|
640
|
+
operations: {
|
|
641
|
+
read: {
|
|
642
|
+
title: 'foo',
|
|
643
|
+
},
|
|
644
|
+
noRoot: ['read'],
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
roles: ['role1', 'role2'],
|
|
652
|
+
result: {
|
|
653
|
+
Entity2: {
|
|
654
|
+
operations: {
|
|
655
|
+
noRoot: ['read'],
|
|
656
|
+
read: {
|
|
657
|
+
title: '__merge__foo__foo',
|
|
658
|
+
id: '__merge__foo__foo',
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
predicates: {
|
|
662
|
+
__merge__foo__foo: {
|
|
663
|
+
or: [
|
|
664
|
+
{ xyz: { lorem: { eq: 'foo' } } },
|
|
665
|
+
{ xyz: { lorem: { eq: 'bar' } } },
|
|
666
|
+
],
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
})
|
|
672
|
+
})
|
|
553
673
|
})
|