@live-change/relations-plugin 0.9.79 → 0.9.81

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.79",
3
+ "version": "0.9.81",
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.79",
25
+ "@live-change/framework": "^0.9.81",
26
26
  "pluralize": "^8.0.0"
27
27
  },
28
- "gitHead": "68d2965e5a3b780063a5d0f9ce71d911fd1fb6be"
28
+ "gitHead": "d70a8bdf4d60b7826576d477b9dff5714c31763d"
29
29
  }
package/src/entity.ts CHANGED
@@ -4,23 +4,26 @@ import {
4
4
 
5
5
  import {
6
6
  defineView, defineCreatedEvent, defineUpdatedEvent, defineDeletedEvent, defineCreateAction,
7
- defineUpdateAction, defineDeleteAction, defineCreateTrigger, defineUpdateTrigger, defineDeleteTrigger
7
+ defineUpdateAction, defineDeleteAction, defineCreateTrigger, defineUpdateTrigger, defineDeleteTrigger,
8
+ ModelDefinitionSpecificationWithEntity,
9
+ EntityConfig,
10
+ EntityContext
8
11
  } from './entityUtils.js'
9
12
 
10
- const annotation = 'entity'
11
-
13
+ export type { EntityConfig, ModelDefinitionSpecificationWithEntity } from './entityUtils.js'
12
14
 
13
15
  export default function(service, app) {
14
16
  if (!service) throw new Error("no service")
15
17
  if (!app) throw new Error("no app")
16
18
 
17
19
  for(let modelName in service.models) {
18
- const model = service.models[modelName]
19
- const config = model[annotation]
20
+ const model: ModelDefinitionSpecificationWithEntity =
21
+ service.models[modelName] as ModelDefinitionSpecificationWithEntity
22
+ const config:EntityConfig = model.entity
20
23
  if(!config) continue
21
24
 
22
- if (model[annotation + 'Processed']) throw new Error("duplicated processing of " + annotation + " processor")
23
- model[annotation + 'Processed'] = true
25
+ if (model.entityProcessed) throw new Error("duplicated processing of entity processor")
26
+ model.entityProcessed = true
24
27
 
25
28
  const originalModelProperties = { ...model.properties }
26
29
  const modelProperties = Object.keys(model.properties)
@@ -45,13 +48,13 @@ export default function(service, app) {
45
48
  //console.log("PPP", others)
46
49
  const objectType = service.name + '_' + modelName
47
50
 
48
- const context = {
51
+ const context: EntityContext = {
49
52
  service, app, model, originalModelProperties, modelProperties, modelPropertyName, modelRuntime,
50
- modelName, writeableProperties, annotation, objectType
53
+ modelName, writeableProperties, annotation: 'entity', objectType
51
54
  }
52
55
 
53
56
  defineView(config, context, config.readAccess || config.readAccessControl || config.writeAccessControl)
54
- defineGlobalRangeView(config, context, config.readAllAccess)
57
+ defineGlobalRangeView(config, context, !!config.readAllAccess)
55
58
  /// TODO: multiple views with limited fields
56
59
 
57
60
  defineCreatedEvent(config, context)
@@ -1,19 +1,56 @@
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"
7
- import App, { ServiceDefinition } from '@live-change/framework'
7
+ import App, { AccessSpecification, ModelDefinitionSpecification, PropertyDefinitionSpecification, ServiceDefinition } from '@live-change/framework'
8
8
  import {
9
9
  PropertyDefinition, ViewDefinition, IndexDefinition, ActionDefinition, EventDefinition, TriggerDefinition
10
10
  } from "@live-change/framework"
11
11
  import pluralize from 'pluralize'
12
12
 
13
13
  import { ServiceDefinitionSpecification } from "@live-change/framework"
14
- import { ActionDefinitionSpecificationAC, ViewDefinitionSpecificationAC, TriggerDefinitionSpecificationAC } from "./types.js"
14
+ import { ActionDefinitionSpecificationAC, ViewDefinitionSpecificationAC, TriggerDefinitionSpecificationAC,
15
+ ModelDefinitionSpecificationExtended, AccessControlSettings } from "./types.js"
15
16
  import { EventDefinitionSpecification } from "@live-change/framework"
16
17
 
18
+ export interface EntityConfig {
19
+ readAccessControl?: AccessControlSettings
20
+ writeAccessControl?: AccessControlSettings
21
+ createAccessControl?: AccessControlSettings
22
+ updateAccessControl?: AccessControlSettings
23
+ deleteAccessControl?: AccessControlSettings
24
+
25
+ readAllAccess?: AccessSpecification
26
+ readAccess?: AccessSpecification
27
+ writeAccess?: AccessSpecification
28
+ createAccess?: AccessSpecification
29
+ updateAccess?: AccessSpecification
30
+ deleteAccess?: AccessSpecification
31
+
32
+ writeableProperties?: string[]
33
+ }
34
+
35
+ export interface ModelDefinitionSpecificationWithEntity extends ModelDefinitionSpecificationExtended {
36
+ entity: EntityConfig
37
+ entityProcessed: boolean
38
+ }
39
+
40
+ export interface EntityContext {
41
+ service: ServiceDefinition<ServiceDefinitionSpecification>
42
+ modelName: string
43
+ modelPropertyName: string
44
+ model: ModelDefinitionSpecificationWithEntity
45
+ originalModelProperties: Record<string, PropertyDefinitionSpecification>
46
+ modelProperties: string[]
47
+ writeableProperties: string[]
48
+ objectType: string
49
+ app: App,
50
+ modelRuntime: any,
51
+ annotation: 'entity'
52
+ }
53
+
17
54
 
18
55
  export function entityAccessControl({service, modelName, modelPropertyName}, accessControl) {
19
56
  if(!accessControl) return undefined
@@ -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/propertyOf.js CHANGED
@@ -55,7 +55,6 @@ export default function(service, app) {
55
55
 
56
56
  defineGlobalRangeView(config, context, config.readAllAccess)
57
57
 
58
-
59
58
  defineSetEvent(config, context, generateId)
60
59
  defineUpdatedEvent(config, context, generateId)
61
60
  defineTransferredEvent(config, context, generateId)
package/src/types.ts CHANGED
@@ -2,13 +2,17 @@ import {
2
2
  ViewContext, ViewDefinitionSpecificationObservable, ViewDefinitionSpecificationDaoPath,
3
3
  ViewDefinitionSpecificationFetch,
4
4
  ActionDefinitionSpecification,
5
- TriggerDefinitionSpecification
5
+ TriggerDefinitionSpecification,
6
+ ModelDefinitionSpecification
6
7
  } from "@live-change/framework"
7
8
 
8
- export type AccessControlSettings = string | string[] | {
9
+ export type PreparedAccessControlSettings = {
9
10
  roles: string | string[]
10
11
  objects?: (properties: any) => any[]
12
+ objParams?: { names: string[], types: string[] }
11
13
  }
14
+
15
+ export type AccessControlSettings = string | string[] | PreparedAccessControlSettings
12
16
  export interface ViewDefinitionSpecificationObservableAC extends ViewDefinitionSpecificationObservable {
13
17
  accessControl?: AccessControlSettings
14
18
  }
@@ -33,4 +37,29 @@ export interface ActionDefinitionSpecificationAC extends ActionDefinitionSpecifi
33
37
 
34
38
  export interface TriggerDefinitionSpecificationAC extends TriggerDefinitionSpecification {
35
39
  accessControl?: AccessControlSettings
36
- }
40
+ }
41
+
42
+ export interface CrudSettings {
43
+ read?: string
44
+ create?: string
45
+ update?: string
46
+ delete?: string
47
+ range?: string
48
+ }
49
+
50
+ export interface Identifier {
51
+ name: string
52
+ field: string
53
+ }
54
+
55
+ export interface ModelDefinitionSpecificationExtended extends ModelDefinitionSpecification {
56
+ crud: CrudSettings,
57
+ identifiers: Identifier[]
58
+ }
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
+ }
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,15 +293,30 @@ 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
- const viewName = (config.prefix || '' ) + pluralize(config.prefix ? modelName : modelPropertyName) + (config.suffix || '')
314
+ const alreadyPlural = pluralize.isPlural(modelPropertyName)
315
+ const prefix = (config.prefix || '') + (alreadyPlural ? 'all' : '')
316
+ const viewName =
317
+ prefix
318
+ + pluralize(prefix ? modelName : modelPropertyName)
319
+ + (config.suffix || '')
254
320
  if(external) model.crud.range = viewName
255
321
  service.views[viewName] = new ViewDefinition({
256
322
  name: viewName,
@@ -260,16 +326,16 @@ export function defineGlobalRangeView(config, context, external = true) {
260
326
  returns: {
261
327
  type: Array,
262
328
  of: {
263
- type: model
329
+ type: service.name + '_' + modelName
264
330
  }
265
331
  },
266
332
  internal: !external,
267
333
  global: config.globalView,
268
- access: external && config.readAllAccess,
269
- daoPath(properties, { client, context }) {
334
+ access: external ? (config.readAllAccess ?? undefined) : undefined,
335
+ daoPath(properties, { client, service }) {
270
336
  const range = App.extractRange(properties)
271
337
  const path = modelRuntime().rangePath(range)
272
338
  return path
273
339
  }
274
340
  })
275
- }
341
+ }