@live-change/relations-plugin 0.9.80 → 0.9.82

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": "@live-change/relations-plugin",
3
- "version": "0.9.80",
3
+ "version": "0.9.82",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "type": "module",
24
24
  "dependencies": {
25
- "@live-change/framework": "^0.9.80",
25
+ "@live-change/framework": "^0.9.82",
26
26
  "pluralize": "^8.0.0"
27
27
  },
28
- "gitHead": "8c4e9d0d1415c8805f0a56dbef07d7b8333632a4"
28
+ "gitHead": "df19af1be46787b1278948f5be40476b45519ca7"
29
29
  }
package/src/entity.ts CHANGED
@@ -9,8 +9,6 @@ import {
9
9
  EntityConfig,
10
10
  EntityContext
11
11
  } from './entityUtils.js'
12
- import { AccessControlSettings, ModelDefinitionSpecificationExtended } from './types.js'
13
- import { AccessSpecification, ModelDefinitionSpecification } from '@live-change/framework'
14
12
 
15
13
  export type { EntityConfig, ModelDefinitionSpecificationWithEntity } from './entityUtils.js'
16
14
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineProperties, defineIndex,
3
- processModelsAnnotation, extractIdParts, extractIdentifiers, extractObjectData, prepareAccessControl,
3
+ extractIdParts, extractIdentifiers, extractObjectData, prepareAccessControl,
4
4
  includeAccessRoles, defineGlobalRangeView
5
5
  } from './utils.js'
6
6
  import { fireChangeTriggers } from "./changeTriggers.js"
@@ -38,7 +38,7 @@ export interface ModelDefinitionSpecificationWithEntity extends ModelDefinitionS
38
38
  }
39
39
 
40
40
  export interface EntityContext {
41
- service: ServiceDefinitionSpecification
41
+ service: ServiceDefinition<ServiceDefinitionSpecification>
42
42
  modelName: string
43
43
  modelPropertyName: string
44
44
  model: ModelDefinitionSpecificationWithEntity
@@ -2,7 +2,8 @@ import {
2
2
  defineProperties, defineIndexes,
3
3
  processModelsAnnotation, addAccessControlParents,
4
4
  defineDeleteByOwnerEvents, defineParentDeleteTriggers, defineParentCopyTriggers,
5
- defineGlobalRangeView
5
+ defineGlobalRangeView,
6
+ RelationConfig
6
7
  } from './utils.js'
7
8
 
8
9
  import {
@@ -17,15 +18,38 @@ import {
17
18
  defineSortIndex,
18
19
  defineCopyAction, defineCopyOnParentCopyTrigger
19
20
  } from './pluralRelationUtils.js'
21
+ import App, { AccessSpecification, ServiceDefinition, ServiceDefinitionSpecification } from '@live-change/framework'
22
+ import { AccessControlSettings } from './types.js'
23
+
24
+ export interface ItemOfConfig extends RelationConfig {
25
+ readAccess?: AccessSpecification
26
+ writeAccess?: AccessSpecification
27
+ createAccess?: AccessSpecification
28
+ updateAccess?: AccessSpecification
29
+ deleteAccess?: AccessSpecification
30
+ copyAccess?: AccessSpecification
31
+ readAllAccess?: AccessSpecification
32
+
33
+ readAccessControl?: AccessControlSettings
34
+ writeAccessControl?: AccessControlSettings
35
+ createAccessControl?: AccessControlSettings
36
+ updateAccessControl?: AccessControlSettings
37
+ deleteAccessControl?: AccessControlSettings
38
+ copyAccessControl?: AccessControlSettings
39
+ readAllAccessControl?: AccessControlSettings
40
+ }
20
41
 
21
- export default function(service, app) {
22
- processModelsAnnotation(service, app, 'itemOf', false, (config, context) => {
42
+ export default function(service: ServiceDefinition<ServiceDefinitionSpecification>, app: any) {
43
+ processModelsAnnotation<ItemOfConfig>(service, app, 'itemOf', false, (config, context) => {
23
44
 
24
45
  context.relationWord = 'Item'
25
46
  context.reverseRelationWord = 'Owned'
26
47
 
27
48
  context.identifiers = defineProperties(context.model, context.others, context.otherPropertyNames)
28
- context.model.identifiers = [...Object.keys(context.identifiers), { name: context.modelPropertyName, field: 'id' }]
49
+ context.model.identifiers = [
50
+ ...Object.keys(context.identifiers).map(name => ({ name, field: name })),
51
+ { name: context.modelPropertyName, field: 'id' }
52
+ ]
29
53
 
30
54
  addAccessControlParents(context)
31
55
  defineIndexes(context.model, context.otherPropertyNames.map(p => p[0].toLowerCase() + p.slice(1)), context.others)
@@ -42,7 +66,7 @@ export default function(service, app) {
42
66
  config.readAccess || config.readAccessControl || config.writeAccessControl)
43
67
  /// TODO: multiple views with limited fields
44
68
 
45
- defineGlobalRangeView(config, context, config.readAllAccess)
69
+ defineGlobalRangeView(config, context, !!config.readAllAccess)
46
70
 
47
71
  defineCreatedEvent(config, context)
48
72
  defineUpdatedEvent(config, context)
package/src/types.ts CHANGED
@@ -6,10 +6,13 @@ import {
6
6
  ModelDefinitionSpecification
7
7
  } from "@live-change/framework"
8
8
 
9
- export type AccessControlSettings = string | string[] | {
9
+ export type PreparedAccessControlSettings = {
10
10
  roles: string | string[]
11
11
  objects?: (properties: any) => any[]
12
+ objParams?: { names: string[], types: string[] }
12
13
  }
14
+
15
+ export type AccessControlSettings = string | string[] | PreparedAccessControlSettings
13
16
  export interface ViewDefinitionSpecificationObservableAC extends ViewDefinitionSpecificationObservable {
14
17
  accessControl?: AccessControlSettings
15
18
  }
@@ -41,6 +44,7 @@ export interface CrudSettings {
41
44
  create?: string
42
45
  update?: string
43
46
  delete?: string
47
+ range?: string
44
48
  }
45
49
 
46
50
  export interface Identifier {
@@ -53,4 +57,9 @@ export interface ModelDefinitionSpecificationExtended extends ModelDefinitionSpe
53
57
  identifiers: Identifier[]
54
58
  }
55
59
 
60
+ export interface ModelDefinitionSpecificationWithAccessControl extends ModelDefinitionSpecificationExtended {
61
+ accessControlParents?: (what: { object: string }) => Promise<{ objectType: string, object: any }[]>
62
+ accessControlParentsSource?: { property: string, type: string }[]
63
+ accessRoles?: string[]
64
+ }
56
65
 
@@ -1,11 +1,13 @@
1
- import App from "@live-change/framework"
1
+ import App, { AccessSpecification, ServiceDefinition } from "@live-change/framework"
2
2
  const app = App.app()
3
3
  import { allCombinations } from "./combinations.js"
4
4
  import {
5
5
  registerParentDeleteTriggers, registerParentCopyTriggers
6
6
  } from "./changeTriggers.js"
7
7
  import {
8
- PropertyDefinition, ViewDefinition, IndexDefinition, ActionDefinition, EventDefinition, TriggerDefinition
8
+ PropertyDefinition, ViewDefinition, IndexDefinition, ActionDefinition, EventDefinition, TriggerDefinition,
9
+ ServiceDefinitionSpecification,
10
+ PropertyDefinitionSpecification
9
11
  } from "@live-change/framework"
10
12
 
11
13
  export {
@@ -13,8 +15,10 @@ export {
13
15
  } from './dataUtils.js'
14
16
 
15
17
  import pluralize from 'pluralize'
18
+ import { ModelDefinitionSpecificationWithEntity } from "./entityUtils.js"
19
+ import { AccessControlSettings, ModelDefinitionSpecificationWithAccessControl, PreparedAccessControlSettings } from "./types.js"
16
20
 
17
- export function extractIdParts(otherPropertyNames, properties) {
21
+ export function extractIdParts(otherPropertyNames: string[], properties: Record<string, any>) {
18
22
  const idParts = []
19
23
  for (const propertyName of otherPropertyNames) {
20
24
  idParts.push(properties[propertyName])
@@ -23,13 +27,15 @@ export function extractIdParts(otherPropertyNames, properties) {
23
27
  }
24
28
 
25
29
 
26
- export function generateId(otherPropertyNames, properties) {
30
+ export function generateId(otherPropertyNames: string[], properties: Record<string, any>) {
27
31
  return otherPropertyNames.length > 1
28
32
  ? otherPropertyNames.map(p => JSON.stringify(properties[p])).join(':')
29
33
  : properties[otherPropertyNames[0]]
30
34
  }
31
35
 
32
- export function defineProperties(model, types, names) {
36
+ export function defineProperties(model: ModelDefinitionSpecificationWithAccessControl,
37
+ types: string[], names: string[])
38
+ : Record<string, PropertyDefinition<PropertyDefinitionSpecification>> {
33
39
  const identifiers = {}
34
40
  for (let i = 0; i < types.length; i++) {
35
41
  identifiers[names[i]] = new PropertyDefinition({
@@ -43,14 +49,16 @@ export function defineProperties(model, types, names) {
43
49
  return identifiers
44
50
  }
45
51
 
46
- export function defineIndex(model, what, props, multi = undefined) {
52
+ export function defineIndex(model: ModelDefinitionSpecificationWithAccessControl,
53
+ what: string, props: string[], multi = undefined) {
47
54
  console.log("DEFINE INDEX", model.name, what, props)
48
- model.indexes['by' + what] = new IndexDefinition({
55
+ model.indexes['by' + what] = {
49
56
  property: props,
50
57
  multi
51
- })
58
+ }
52
59
  }
53
- export function defineIndexes(model, props, types) {
60
+ export function defineIndexes(model: ModelDefinitionSpecificationWithAccessControl,
61
+ props: Record<string, any>, types: string[]) {
54
62
  console.log("DEFINE INDEXES!", model.name, props, types)
55
63
  const propCombinations = allCombinations(Object.keys(props)) /// combinations of indexes
56
64
  for(const propCombination of propCombinations) {
@@ -79,7 +87,47 @@ export function defineIndexes(model, props, types) {
79
87
  }
80
88
  }
81
89
 
82
- export function processModelsAnnotation(service, app, annotation, multiple, cb) {
90
+ export interface RelationConfig {
91
+ what: string | string[]
92
+ propertyNames?: string[]
93
+ writeableProperties?: string[]
94
+ prefix?: string
95
+ suffix?: string
96
+ globalView?: boolean
97
+ readAllAccess?: AccessSpecification
98
+ sortBy?: string[],
99
+ customDeleteTrigger?: boolean, /// TODO: check if this is needed
100
+ customParentCopyTrigger?: boolean /// TODO: check if this is needed
101
+ }
102
+
103
+ export interface ModelDefinitionSpecificationWithRelation extends ModelDefinitionSpecificationWithAccessControl {
104
+ }
105
+
106
+ interface RelationContext {
107
+ service: ServiceDefinition<ServiceDefinitionSpecification>
108
+ app: App
109
+ model: ModelDefinitionSpecificationWithRelation
110
+ originalModelProperties: Record<string, PropertyDefinitionSpecification>
111
+ modelProperties: string[]
112
+ modelPropertyName: string
113
+ modelName: string
114
+ modelRuntime: any
115
+ annotation: string
116
+ objectType: string
117
+ parentsTypes: string[]
118
+ otherPropertyNames: string[]
119
+ joinedOthersPropertyName: string
120
+ joinedOthersClassName: string
121
+ writeableProperties: string[]
122
+ others: any[],
123
+ reverseRelationWord?: string
124
+ relationWord?: string,
125
+ identifiers?: Record<string, PropertyDefinition<PropertyDefinitionSpecification>>
126
+ }
127
+
128
+ export function processModelsAnnotation<PreparedConfig extends RelationConfig>
129
+ (service: ServiceDefinition<ServiceDefinitionSpecification>, app: App, annotation: string, multiple: boolean,
130
+ cb: (config: PreparedConfig, context: RelationContext) => void) {
83
131
  if (!service) throw new Error("no service")
84
132
  if (!app) throw new Error("no app")
85
133
 
@@ -134,7 +182,7 @@ export function processModelsAnnotation(service, app, annotation, multiple, cb)
134
182
  const joinedOthersClassName = otherNames.join('And')
135
183
  const objectType = service.name + '_' + modelName
136
184
 
137
- const context = {
185
+ const context: RelationContext = {
138
186
  service, app, model, originalModelProperties, modelProperties, modelPropertyName, modelRuntime,
139
187
  otherPropertyNames, joinedOthersPropertyName, modelName, writeableProperties, joinedOthersClassName,
140
188
  others, annotation, objectType, parentsTypes: others
@@ -146,7 +194,7 @@ export function processModelsAnnotation(service, app, annotation, multiple, cb)
146
194
  }
147
195
  }
148
196
 
149
- export function addAccessControlParents(context) {
197
+ export function addAccessControlParents(context: RelationContext) {
150
198
  const { modelRuntime } = context
151
199
  context.model.accessControlParents = context.model.accessControlParents ?? (async (what) => {
152
200
  const id = what.object
@@ -169,29 +217,31 @@ export function addAccessControlParents(context) {
169
217
  )
170
218
  }
171
219
 
172
- export function prepareAccessControl(accessControl, names, types) {
220
+
221
+ export function prepareAccessControl(accessControl: AccessControlSettings, names: string[], types: string[]) {
173
222
  if(typeof accessControl == 'object') {
174
- accessControl.objects = accessControl.objects ?? ((params) => names.map((name, index) => ({
223
+ const ac = accessControl as PreparedAccessControlSettings
224
+ ac.objects = ac.objects ?? ((params) => names.map((name, index) => ({
175
225
  objectType: types[index],
176
226
  object: params[name]
177
227
  })))
178
- accessControl.objParams = { names, types }
228
+ ac.objParams = { names, types }
179
229
  }
180
230
  }
181
231
 
182
- export function cloneAndPrepareAccessControl(accessControl, names, types) {
232
+ export function cloneAndPrepareAccessControl(accessControl: AccessControlSettings, names: string[], types: string[]) {
183
233
  if(!accessControl) return accessControl
184
234
  if(Array.isArray(accessControl)) {
185
- accessControl = { roles: accessControl}
235
+ accessControl = { roles: accessControl }
186
236
  }
187
- const newAccessControl = { ...accessControl }
237
+ const newAccessControl: PreparedAccessControlSettings = { ...(accessControl as PreparedAccessControlSettings) }
188
238
  prepareAccessControl(newAccessControl, names, types)
189
239
  return newAccessControl
190
240
  }
191
241
 
192
- export function defineDeleteByOwnerEvents(config, context, generateId) {
242
+ export function defineDeleteByOwnerEvents(config: RelationConfig, context: RelationContext) {
193
243
  const {
194
- service, modelRuntime, joinedOthersPropertyName, modelName, modelPropertyName, otherPropertyNames, reverseRelationWord
244
+ service, modelRuntime, joinedOthersPropertyName, modelName, modelPropertyName, otherPropertyNames,
195
245
  } = context
196
246
  for(const propertyName of otherPropertyNames) {
197
247
  const eventName = modelName + 'DeleteByOwner'
@@ -224,15 +274,16 @@ export function defineDeleteByOwnerEvents(config, context, generateId) {
224
274
  }
225
275
  }
226
276
 
227
- export function defineParentDeleteTriggers(config, context) {
277
+ export function defineParentDeleteTriggers(config: RelationConfig, context: RelationContext) {
228
278
  registerParentDeleteTriggers(context, config)
229
279
  }
230
280
 
231
- export function defineParentCopyTriggers(config, context) {
281
+ export function defineParentCopyTriggers(config: RelationConfig, context: RelationContext) {
232
282
  registerParentCopyTriggers(context, config)
233
283
  }
234
284
 
235
- export function includeAccessRoles(model, access) {
285
+ export function includeAccessRoles(model: ModelDefinitionSpecificationWithAccessControl,
286
+ access: AccessControlSettings | AccessControlSettings[]) {
236
287
  if(!access) return
237
288
  if(!model.accessRoles) model.accessRoles = []
238
289
  if(typeof access === 'string' && !model.accessRoles.find(role => role === access)) {
@@ -242,13 +293,23 @@ export function includeAccessRoles(model, access) {
242
293
  for(const element of access) {
243
294
  includeAccessRoles(model, element)
244
295
  }
245
- }
246
- if(access.roles) {
247
- includeAccessRoles(model, access.roles)
296
+ } else if((access as PreparedAccessControlSettings).roles) {
297
+ includeAccessRoles(model, (access as PreparedAccessControlSettings).roles)
248
298
  }
249
299
  }
250
300
 
251
- export function defineGlobalRangeView(config, context, external = true) {
301
+ export function defineGlobalRangeView(config: {
302
+ prefix?: string
303
+ suffix?: string
304
+ globalView?: boolean
305
+ readAllAccess?: AccessSpecification
306
+ }, context: {
307
+ service: ServiceDefinition<ServiceDefinitionSpecification>
308
+ modelRuntime: any
309
+ modelPropertyName: string
310
+ modelName: string
311
+ model: ModelDefinitionSpecificationWithAccessControl
312
+ }, external:boolean = true) {
252
313
  const { service, modelRuntime, modelPropertyName, modelName, model } = context
253
314
  const alreadyPlural = pluralize.isPlural(modelPropertyName)
254
315
  const prefix = (config.prefix || '') + (alreadyPlural ? 'all' : '')
@@ -265,13 +326,13 @@ export function defineGlobalRangeView(config, context, external = true) {
265
326
  returns: {
266
327
  type: Array,
267
328
  of: {
268
- type: model
329
+ type: service.name + '_' + modelName
269
330
  }
270
331
  },
271
332
  internal: !external,
272
333
  global: config.globalView,
273
- access: external && config.readAllAccess,
274
- daoPath(properties, { client, context }) {
334
+ access: external ? (config.readAllAccess ?? undefined) : undefined,
335
+ daoPath(properties, { client, service }) {
275
336
  const range = App.extractRange(properties)
276
337
  const path = modelRuntime().rangePath(range)
277
338
  return path