@declaro/data 2.0.0-beta.126 → 2.0.0-beta.128

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 (39) hide show
  1. package/dist/browser/index.js +11 -11
  2. package/dist/browser/index.js.map +6 -6
  3. package/dist/node/index.cjs +217 -92
  4. package/dist/node/index.cjs.map +6 -6
  5. package/dist/node/index.js +209 -84
  6. package/dist/node/index.js.map +6 -6
  7. package/dist/ts/application/model-controller.d.ts +15 -5
  8. package/dist/ts/application/model-controller.d.ts.map +1 -1
  9. package/dist/ts/application/read-only-model-controller.d.ts +5 -1
  10. package/dist/ts/application/read-only-model-controller.d.ts.map +1 -1
  11. package/dist/ts/domain/services/model-service.d.ts +27 -0
  12. package/dist/ts/domain/services/model-service.d.ts.map +1 -1
  13. package/dist/ts/domain/services/read-only-model-service.d.ts +8 -0
  14. package/dist/ts/domain/services/read-only-model-service.d.ts.map +1 -1
  15. package/dist/ts/shared/utils/schema-inheritance.test.d.ts +2 -0
  16. package/dist/ts/shared/utils/schema-inheritance.test.d.ts.map +1 -0
  17. package/dist/ts/shared/utils/test/animal-schema.d.ts +57 -0
  18. package/dist/ts/shared/utils/test/animal-schema.d.ts.map +1 -0
  19. package/dist/ts/shared/utils/test/animal-trait-schema.d.ts +55 -0
  20. package/dist/ts/shared/utils/test/animal-trait-schema.d.ts.map +1 -0
  21. package/dist/ts/shared/utils/test/elephant-schema.d.ts +30 -0
  22. package/dist/ts/shared/utils/test/elephant-schema.d.ts.map +1 -0
  23. package/dist/ts/shared/utils/test/elephant-trait-schema.d.ts +26 -0
  24. package/dist/ts/shared/utils/test/elephant-trait-schema.d.ts.map +1 -0
  25. package/package.json +5 -5
  26. package/src/application/model-controller.ts +110 -59
  27. package/src/application/read-only-model-controller.ts +43 -25
  28. package/src/domain/services/model-service.test.ts +460 -0
  29. package/src/domain/services/model-service.ts +165 -67
  30. package/src/domain/services/read-only-model-service.test.ts +230 -0
  31. package/src/domain/services/read-only-model-service.ts +65 -40
  32. package/src/shared/utils/schema-inheritance.test.ts +295 -0
  33. package/src/shared/utils/test/animal-schema.ts +46 -0
  34. package/src/shared/utils/test/animal-trait-schema.ts +45 -0
  35. package/src/shared/utils/test/elephant-schema.ts +58 -0
  36. package/src/shared/utils/test/elephant-trait-schema.ts +53 -0
  37. package/dist/ts/test/mock/repositories/mock-memory-repository.custom-lookup.test.d.ts +0 -1
  38. package/dist/ts/test/mock/repositories/mock-memory-repository.custom-lookup.test.d.ts.map +0 -1
  39. package/src/test/mock/repositories/mock-memory-repository.custom-lookup.test.ts +0 -0
@@ -1,52 +1,92 @@
1
1
  import type { AuthValidator } from '@declaro/auth'
2
2
  import { PermissionValidator, type AnyModelSchema } from '@declaro/core'
3
3
  import type { ModelService, ICreateOptions, IUpdateOptions } from '../domain/services/model-service'
4
+ import type { ILoadOptions } from '../domain/services/read-only-model-service'
4
5
  import type { InferDetail, InferFilters, InferInput, InferLookup, InferSummary } from '../shared/utils/schema-inference'
5
6
  import { ReadOnlyModelController } from './read-only-model-controller'
6
7
 
7
8
  export class ModelController<TSchema extends AnyModelSchema> extends ReadOnlyModelController<TSchema> {
8
- constructor(protected readonly service: ModelService<TSchema>, protected readonly authValidator: AuthValidator) {
9
+ constructor(
10
+ protected readonly service: ModelService<TSchema>,
11
+ protected readonly authValidator: AuthValidator,
12
+ ) {
9
13
  super(service, authValidator)
10
14
  }
11
15
 
12
- async create(input: InferInput<TSchema>): Promise<InferDetail<TSchema>> {
13
- this.authValidator.validatePermissions((v) =>
14
- v.someOf([
15
- this.service.getDescriptor('create', '*').toString(),
16
- this.service.getDescriptor('write', '*').toString(),
17
- ]),
18
- )
19
- return this.service.create(input)
16
+ async createPermissions(input: InferInput<TSchema>, options?: ICreateOptions): Promise<PermissionValidator> {
17
+ return PermissionValidator.create().someOf([
18
+ this.service.getDescriptor('create', '*').toString(),
19
+ this.service.getDescriptor('write', '*').toString(),
20
+ ])
21
+ }
22
+
23
+ async create(input: InferInput<TSchema>, options?: ICreateOptions): Promise<InferDetail<TSchema>> {
24
+ const permissions = await this.createPermissions(input, options)
25
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
26
+ return this.service.create(input, options)
27
+ }
28
+
29
+ async updatePermissions(
30
+ lookup: InferLookup<TSchema>,
31
+ input: InferInput<TSchema>,
32
+ options?: IUpdateOptions,
33
+ ): Promise<PermissionValidator> {
34
+ return PermissionValidator.create().someOf([
35
+ this.service.getDescriptor('update', '*').toString(),
36
+ this.service.getDescriptor('write', '*').toString(),
37
+ ])
38
+ }
39
+
40
+ async update(
41
+ lookup: InferLookup<TSchema>,
42
+ input: InferInput<TSchema>,
43
+ options?: IUpdateOptions,
44
+ ): Promise<InferDetail<TSchema>> {
45
+ const permissions = await this.updatePermissions(lookup, input, options)
46
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
47
+ return this.service.update(lookup, input, options)
48
+ }
49
+
50
+ async removePermissions(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<PermissionValidator> {
51
+ return PermissionValidator.create().someOf([
52
+ this.service.getDescriptor('remove', '*').toString(),
53
+ this.service.getDescriptor('write', '*').toString(),
54
+ ])
55
+ }
56
+
57
+ async remove(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<InferSummary<TSchema>> {
58
+ const permissions = await this.removePermissions(lookup, options)
59
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
60
+ return this.service.remove(lookup, options)
20
61
  }
21
62
 
22
- async update(lookup: InferLookup<TSchema>, input: InferInput<TSchema>): Promise<InferDetail<TSchema>> {
23
- this.authValidator.validatePermissions((v) =>
24
- v.someOf([
25
- this.service.getDescriptor('update', '*').toString(),
26
- this.service.getDescriptor('write', '*').toString(),
27
- ]),
28
- )
29
- return this.service.update(lookup, input)
63
+ async restorePermissions(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<PermissionValidator> {
64
+ return PermissionValidator.create().someOf([
65
+ this.service.getDescriptor('restore', '*').toString(),
66
+ this.service.getDescriptor('write', '*').toString(),
67
+ ])
30
68
  }
31
69
 
32
- async remove(lookup: InferLookup<TSchema>): Promise<InferSummary<TSchema>> {
33
- this.authValidator.validatePermissions((v) =>
34
- v.someOf([
35
- this.service.getDescriptor('remove', '*').toString(),
36
- this.service.getDescriptor('write', '*').toString(),
37
- ]),
38
- )
39
- return this.service.remove(lookup)
70
+ async restore(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<InferSummary<TSchema>> {
71
+ const permissions = await this.restorePermissions(lookup, options)
72
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
73
+ return this.service.restore(lookup, options)
40
74
  }
41
75
 
42
- async restore(lookup: InferLookup<TSchema>): Promise<InferSummary<TSchema>> {
43
- this.authValidator.validatePermissions((v) =>
44
- v.someOf([
45
- this.service.getDescriptor('restore', '*').toString(),
46
- this.service.getDescriptor('write', '*').toString(),
47
- ]),
48
- )
49
- return this.service.restore(lookup)
76
+ async upsertPermissions(
77
+ input: InferInput<TSchema>,
78
+ options?: ICreateOptions | IUpdateOptions,
79
+ ): Promise<PermissionValidator> {
80
+ // Create nested validator for (create AND update) permissions
81
+ const createAndUpdateValidator = PermissionValidator.create().allOf([
82
+ this.service.getDescriptor('create', '*').toString(),
83
+ this.service.getDescriptor('update', '*').toString(),
84
+ ])
85
+
86
+ return PermissionValidator.create().someOf([
87
+ createAndUpdateValidator,
88
+ this.service.getDescriptor('write', '*').toString(),
89
+ ])
50
90
  }
51
91
 
52
92
  /**
@@ -56,16 +96,25 @@ export class ModelController<TSchema extends AnyModelSchema> extends ReadOnlyMod
56
96
  * @returns The upserted record.
57
97
  */
58
98
  async upsert(input: InferInput<TSchema>, options?: ICreateOptions | IUpdateOptions): Promise<InferDetail<TSchema>> {
99
+ const permissions = await this.upsertPermissions(input, options)
100
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
101
+ return this.service.upsert(input, options)
102
+ }
103
+
104
+ async bulkUpsertPermissions(
105
+ inputs: InferInput<TSchema>[],
106
+ options?: ICreateOptions | IUpdateOptions,
107
+ ): Promise<PermissionValidator> {
59
108
  // Create nested validator for (create AND update) permissions
60
109
  const createAndUpdateValidator = PermissionValidator.create().allOf([
61
110
  this.service.getDescriptor('create', '*').toString(),
62
111
  this.service.getDescriptor('update', '*').toString(),
63
112
  ])
64
113
 
65
- this.authValidator.validatePermissions((v) =>
66
- v.someOf([createAndUpdateValidator, this.service.getDescriptor('write', '*').toString()]),
67
- )
68
- return this.service.upsert(input, options)
114
+ return PermissionValidator.create().someOf([
115
+ createAndUpdateValidator,
116
+ this.service.getDescriptor('write', '*').toString(),
117
+ ])
69
118
  }
70
119
 
71
120
  /**
@@ -78,18 +127,19 @@ export class ModelController<TSchema extends AnyModelSchema> extends ReadOnlyMod
78
127
  inputs: InferInput<TSchema>[],
79
128
  options?: ICreateOptions | IUpdateOptions,
80
129
  ): Promise<InferDetail<TSchema>[]> {
81
- // Create nested validator for (create AND update) permissions
82
- const createAndUpdateValidator = PermissionValidator.create().allOf([
83
- this.service.getDescriptor('create', '*').toString(),
84
- this.service.getDescriptor('update', '*').toString(),
85
- ])
86
-
87
- this.authValidator.validatePermissions((v) =>
88
- v.someOf([createAndUpdateValidator, this.service.getDescriptor('write', '*').toString()]),
89
- )
130
+ const permissions = await this.bulkUpsertPermissions(inputs, options)
131
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
90
132
  return this.service.bulkUpsert(inputs, options)
91
133
  }
92
134
 
135
+ async permanentlyDeleteFromTrashPermissions(lookup: InferLookup<TSchema>): Promise<PermissionValidator> {
136
+ return PermissionValidator.create().someOf([
137
+ this.service.getDescriptor('permanently-delete-from-trash', '*').toString(),
138
+ this.service.getDescriptor('permanently-delete', '*').toString(),
139
+ this.service.getDescriptor('empty-trash', '*').toString(),
140
+ ])
141
+ }
142
+
93
143
  /**
94
144
  * Permanently deletes a specific entity from the trash.
95
145
  * Requires 'permanently-delete-from-trash', 'permanently-delete', or 'empty-trash' permission.
@@ -97,16 +147,15 @@ export class ModelController<TSchema extends AnyModelSchema> extends ReadOnlyMod
97
147
  * @returns The permanently deleted entity summary
98
148
  */
99
149
  async permanentlyDeleteFromTrash(lookup: InferLookup<TSchema>): Promise<InferSummary<TSchema>> {
100
- this.authValidator.validatePermissions((v) =>
101
- v.someOf([
102
- this.service.getDescriptor('permanently-delete-from-trash', '*').toString(),
103
- this.service.getDescriptor('permanently-delete', '*').toString(),
104
- this.service.getDescriptor('empty-trash', '*').toString(),
105
- ]),
106
- )
150
+ const permissions = await this.permanentlyDeleteFromTrashPermissions(lookup)
151
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
107
152
  return this.service.permanentlyDeleteFromTrash(lookup)
108
153
  }
109
154
 
155
+ async permanentlyDeletePermissions(lookup: InferLookup<TSchema>): Promise<PermissionValidator> {
156
+ return PermissionValidator.create().someOf([this.service.getDescriptor('permanently-delete', '*').toString()])
157
+ }
158
+
110
159
  /**
111
160
  * Permanently deletes an entity without moving it to trash first.
112
161
  * Requires 'permanently-delete' permission.
@@ -114,12 +163,15 @@ export class ModelController<TSchema extends AnyModelSchema> extends ReadOnlyMod
114
163
  * @returns The permanently deleted entity summary
115
164
  */
116
165
  async permanentlyDelete(lookup: InferLookup<TSchema>): Promise<InferSummary<TSchema>> {
117
- this.authValidator.validatePermissions((v) =>
118
- v.someOf([this.service.getDescriptor('permanently-delete', '*').toString()]),
119
- )
166
+ const permissions = await this.permanentlyDeletePermissions(lookup)
167
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
120
168
  return this.service.permanentlyDelete(lookup)
121
169
  }
122
170
 
171
+ async emptyTrashPermissions(filters?: InferFilters<TSchema>): Promise<PermissionValidator> {
172
+ return PermissionValidator.create().someOf([this.service.getDescriptor('empty-trash', '*').toString()])
173
+ }
174
+
123
175
  /**
124
176
  * Empties the trash by permanently deleting entities that have been marked as removed.
125
177
  * Requires 'empty-trash' permission.
@@ -127,9 +179,8 @@ export class ModelController<TSchema extends AnyModelSchema> extends ReadOnlyMod
127
179
  * @returns The count of entities permanently deleted
128
180
  */
129
181
  async emptyTrash(filters?: InferFilters<TSchema>): Promise<number> {
130
- this.authValidator.validatePermissions((v) =>
131
- v.someOf([this.service.getDescriptor('empty-trash', '*').toString()]),
132
- )
182
+ const permissions = await this.emptyTrashPermissions(filters)
183
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
133
184
  return this.service.emptyTrash(filters)
134
185
  }
135
186
  }
@@ -1,6 +1,6 @@
1
1
  import type { AuthValidator } from '@declaro/auth'
2
2
  import {} from '@declaro/auth'
3
- import type { AnyModelSchema } from '@declaro/core'
3
+ import { PermissionValidator, type AnyModelSchema } from '@declaro/core'
4
4
  import type { ILoadOptions, ISearchOptions, ReadOnlyModelService } from '../domain/services/read-only-model-service'
5
5
  import type { InferDetail, InferFilters, InferLookup, InferSearchResults } from '../shared/utils/schema-inference'
6
6
 
@@ -10,39 +10,61 @@ export class ReadOnlyModelController<TSchema extends AnyModelSchema> {
10
10
  protected readonly authValidator: AuthValidator,
11
11
  ) {}
12
12
 
13
+ async loadPermissions(lookup: InferLookup<TSchema>): Promise<PermissionValidator> {
14
+ return PermissionValidator.create().someOf([
15
+ this.service.getDescriptor('load', '*').toString(),
16
+ this.service.getDescriptor('read', '*').toString(),
17
+ ])
18
+ }
19
+
13
20
  async load(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<InferDetail<TSchema>> {
14
- this.authValidator.validatePermissions((v) =>
15
- v.someOf([
16
- this.service.getDescriptor('load', '*').toString(),
17
- this.service.getDescriptor('read', '*').toString(),
18
- ]),
19
- )
21
+ const permissions = await this.loadPermissions(lookup)
22
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
20
23
  return this.service.load(lookup, options)
21
24
  }
22
25
 
26
+ async loadManyPermissions(lookups: InferLookup<TSchema>[]): Promise<PermissionValidator> {
27
+ return PermissionValidator.create().someOf([
28
+ this.service.getDescriptor('loadMany', '*').toString(),
29
+ this.service.getDescriptor('read', '*').toString(),
30
+ ])
31
+ }
32
+
23
33
  async loadMany(lookups: InferLookup<TSchema>[], options?: ILoadOptions): Promise<InferDetail<TSchema>[]> {
24
- this.authValidator.validatePermissions((v) =>
25
- v.someOf([
26
- this.service.getDescriptor('loadMany', '*').toString(),
27
- this.service.getDescriptor('read', '*').toString(),
28
- ]),
29
- )
34
+ const permissions = await this.loadManyPermissions(lookups)
35
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
30
36
  return this.service.loadMany(lookups, options)
31
37
  }
32
38
 
39
+ async searchPermissions(
40
+ input: InferFilters<TSchema>,
41
+ options?: ISearchOptions<TSchema>,
42
+ ): Promise<PermissionValidator> {
43
+ return PermissionValidator.create().someOf([
44
+ this.service.getDescriptor('search', '*').toString(),
45
+ this.service.getDescriptor('read', '*').toString(),
46
+ ])
47
+ }
48
+
33
49
  async search(
34
50
  input: InferFilters<TSchema>,
35
51
  options?: ISearchOptions<TSchema>,
36
52
  ): Promise<InferSearchResults<TSchema>> {
37
- this.authValidator.validatePermissions((v) =>
38
- v.someOf([
39
- this.service.getDescriptor('search', '*').toString(),
40
- this.service.getDescriptor('read', '*').toString(),
41
- ]),
42
- )
53
+ const permissions = await this.searchPermissions(input, options)
54
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
43
55
  return this.service.search(input, options)
44
56
  }
45
57
 
58
+ async countPermissions(
59
+ input: InferFilters<TSchema>,
60
+ options?: ISearchOptions<TSchema>,
61
+ ): Promise<PermissionValidator> {
62
+ return PermissionValidator.create().someOf([
63
+ this.service.getDescriptor('count', '*').toString(),
64
+ this.service.getDescriptor('read', '*').toString(),
65
+ ])
66
+ }
67
+
46
68
  /**
47
69
  * Count the number of records matching the given filters.
48
70
  * @param input The filters to apply to the count operation.
@@ -50,12 +72,8 @@ export class ReadOnlyModelController<TSchema extends AnyModelSchema> {
50
72
  * @returns The count of matching records.
51
73
  */
52
74
  async count(input: InferFilters<TSchema>, options?: ISearchOptions<TSchema>): Promise<number> {
53
- this.authValidator.validatePermissions((v) =>
54
- v.someOf([
55
- this.service.getDescriptor('count', '*').toString(),
56
- this.service.getDescriptor('read', '*').toString(),
57
- ]),
58
- )
75
+ const permissions = await this.countPermissions(input, options)
76
+ this.authValidator.validatePermissions((v) => v.extend(permissions))
59
77
  return this.service.count(input, options)
60
78
  }
61
79
  }