@live-change/user-service 0.2.24 → 0.2.27
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/combinations.js +17 -0
- package/contactOrUserItem.js +266 -0
- package/contactOrUserProperty.js +292 -0
- package/index.js +23 -0
- package/package.json +4 -4
- package/sessionOrUserItem.js +5 -3
- package/sessionOrUserProperty.js +60 -7
- package/userItem.js +2 -0
- package/userProperty.js +2 -0
- package/utils.js +16 -0
package/combinations.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function combinations(x, n ,p=[]) {
|
|
2
|
+
if(x.length == 0 || n > x.length) return []
|
|
3
|
+
if(n == 1 || x.length == 1) return x.map(e=>p.concat([e]))
|
|
4
|
+
let acc = []
|
|
5
|
+
for(let i = 0; i < x.length; i++) acc.push(
|
|
6
|
+
...combinations(x.slice(i+1), n - 1, p.concat([x[i]]))
|
|
7
|
+
)
|
|
8
|
+
return acc
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function allCombinations(x) {
|
|
12
|
+
let acc = []
|
|
13
|
+
for(let i = 1; i<=x.length; i++) acc.push(...combinations(x,i))
|
|
14
|
+
return acc
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = { combinations, allCombinations }
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
const definition = require("./definition.js")
|
|
2
|
+
const App = require("@live-change/framework")
|
|
3
|
+
const { PropertyDefinition, ViewDefinition, IndexDefinition, ActionDefinition, EventDefinition } = App
|
|
4
|
+
const { User } = require("./model.js")
|
|
5
|
+
|
|
6
|
+
const pluralize = require('pluralize')
|
|
7
|
+
|
|
8
|
+
definition.processor(function(service, app) {
|
|
9
|
+
|
|
10
|
+
for(let modelName in service.models) {
|
|
11
|
+
const model = service.models[modelName]
|
|
12
|
+
if(model.contactOrUserItem) {
|
|
13
|
+
if(model.properties.owner) throw new Error('user property already exists!!!')
|
|
14
|
+
|
|
15
|
+
const originalModelProperties = { ...model.properties }
|
|
16
|
+
const modelProperties = Object.keys(model.properties)
|
|
17
|
+
const defaults = App.utils.generateDefault(model.properties)
|
|
18
|
+
const modelPropertyName = modelName.slice(0, 1).toLowerCase() + modelName.slice(1)
|
|
19
|
+
|
|
20
|
+
function modelRuntime() {
|
|
21
|
+
return service._runtime.models[modelName]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const config = model.contactOrUserItem
|
|
25
|
+
const writeableProperties = modelProperties || config.writableProperties
|
|
26
|
+
|
|
27
|
+
//console.log("USER ITEM", model)
|
|
28
|
+
|
|
29
|
+
if(model.itemOfAny) throw new Error("model " + modelName + " already have owner")
|
|
30
|
+
model.itemOfAny = {
|
|
31
|
+
...config
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*await service.trigger({
|
|
35
|
+
type: 'contactConnected',
|
|
36
|
+
contactType: 'email',
|
|
37
|
+
contact: email,
|
|
38
|
+
user
|
|
39
|
+
})*/
|
|
40
|
+
|
|
41
|
+
service.trigger({
|
|
42
|
+
name: 'contactConnected',
|
|
43
|
+
properties: {
|
|
44
|
+
contactType: {
|
|
45
|
+
type: String,
|
|
46
|
+
validation: ['nonEmpty']
|
|
47
|
+
},
|
|
48
|
+
contact: {
|
|
49
|
+
type: String,
|
|
50
|
+
validation: ['nonEmpty']
|
|
51
|
+
},
|
|
52
|
+
user: {
|
|
53
|
+
type: User,
|
|
54
|
+
validation: ['nonEmpty']
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
async execute({ contactType, contact, user }, { service }, emit) {
|
|
58
|
+
const contactPath = [contactType, contact]
|
|
59
|
+
const contactItems = await modelRuntime().indexRangeGet('byOwner', contactPath, {} )
|
|
60
|
+
if(config.merge) {
|
|
61
|
+
const userPath = ['user_User', user]
|
|
62
|
+
const userItems = await modelRuntime().indexRangeGet('byOwner', userPath, {} )
|
|
63
|
+
const mergeResult = await config.merge(contactItems, userItems)
|
|
64
|
+
if(mergeResult) {
|
|
65
|
+
const { transferred, updated, deleted } = mergeResult
|
|
66
|
+
for(const entity of transferred) {
|
|
67
|
+
emit({
|
|
68
|
+
type: 'ownerOwned' + modelName + 'Transferred',
|
|
69
|
+
[modelPropertyName]: entity.id,
|
|
70
|
+
to: {
|
|
71
|
+
id: entity.id,
|
|
72
|
+
ownerType: 'user_User',
|
|
73
|
+
owner: user
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
for(const entity of updated) {
|
|
78
|
+
emit({
|
|
79
|
+
type: 'ownerOwned' + modelName + 'Updated',
|
|
80
|
+
[modelPropertyName]: entity.id,
|
|
81
|
+
identifiers: {
|
|
82
|
+
id: entity.id,
|
|
83
|
+
ownerType: 'user_User',
|
|
84
|
+
owner: user
|
|
85
|
+
},
|
|
86
|
+
data: entity
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
for(const entity of deleted) {
|
|
90
|
+
emit({
|
|
91
|
+
type: 'ownerOwned' + modelName + 'Deleted',
|
|
92
|
+
[modelPropertyName]: entity.id,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
for(const entity of contactItems) {
|
|
98
|
+
emit({
|
|
99
|
+
type: 'ownerOwned' + modelName + 'Transferred',
|
|
100
|
+
[modelPropertyName]: entity.id,
|
|
101
|
+
identifiers: {
|
|
102
|
+
id: entity.id,
|
|
103
|
+
ownerType: 'user_User',
|
|
104
|
+
owner: user
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
if(config.ownerReadAccess) {
|
|
113
|
+
const viewName = 'my' + pluralize(modelName)
|
|
114
|
+
service.views[viewName] = new ViewDefinition({
|
|
115
|
+
name: viewName,
|
|
116
|
+
access(params, context) {
|
|
117
|
+
return context.client.user && (config.ownerReadAccess ? config.ownerReadAccess(params, context) : true)
|
|
118
|
+
},
|
|
119
|
+
properties: App.rangeProperties,
|
|
120
|
+
daoPath(range, { client, context }) {
|
|
121
|
+
const owner = ['user_User', client.user]
|
|
122
|
+
const path = modelRuntime().indexRangePath('byOwner', owner, range )
|
|
123
|
+
return path
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
for(const sortField of config.sortBy || []) {
|
|
127
|
+
const sortFieldUc = sortField.slice(0, 1).toUpperCase() + sortField.slice(1)
|
|
128
|
+
const viewName = 'mySessionOrUser' + pluralize(modelName) + 'By' + sortFieldUc
|
|
129
|
+
service.views[viewName] = new ViewDefinition({
|
|
130
|
+
name: viewName,
|
|
131
|
+
access(params, context) {
|
|
132
|
+
if(!context.client.user) return false
|
|
133
|
+
return config.ownerReadAccess(params, context)
|
|
134
|
+
},
|
|
135
|
+
properties: App.rangeProperties,
|
|
136
|
+
daoPath(range, { client, context }) {
|
|
137
|
+
const owner = ['user_User', client.user]
|
|
138
|
+
return modelRuntime().sortedIndexRangePath('byOwner' + sortFieldUc, owner, range)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if(config.ownerCreateAccess || config.ownerWriteAccess) {
|
|
145
|
+
const eventName = 'ownerOwned' + modelName + 'Created'
|
|
146
|
+
const actionName = 'createMy' + modelName
|
|
147
|
+
service.actions[actionName] = new ActionDefinition({
|
|
148
|
+
name: actionName,
|
|
149
|
+
access: (params, context) => context.client.user
|
|
150
|
+
&& (config.ownerCreateAccess || config.ownerWriteAccess)(params,context),
|
|
151
|
+
properties: {
|
|
152
|
+
...originalModelProperties,
|
|
153
|
+
[modelPropertyName]: {
|
|
154
|
+
type: model,
|
|
155
|
+
validation: ['localId']
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
queuedBy: (command) => 'u:'+command.client.user,
|
|
159
|
+
waitForEvents: true,
|
|
160
|
+
async execute(properties, { client, service }, emit) {
|
|
161
|
+
const id = properties[modelPropertyName] || app.generateUid()
|
|
162
|
+
const entity = await modelRuntime().get(id)
|
|
163
|
+
if(entity) throw 'exists'
|
|
164
|
+
const identifiers = {
|
|
165
|
+
ownerType: 'user_User',
|
|
166
|
+
owner: client.user,
|
|
167
|
+
}
|
|
168
|
+
emit({
|
|
169
|
+
type: eventName,
|
|
170
|
+
[modelPropertyName]: id,
|
|
171
|
+
identifiers,
|
|
172
|
+
data: properties
|
|
173
|
+
})
|
|
174
|
+
return id
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
if(config.ownerUpdateAccess || config.ownerWriteAccess) {
|
|
179
|
+
const eventName = 'ownerOwned' + modelName + 'Updated'
|
|
180
|
+
const actionName = 'updateMy' + modelName
|
|
181
|
+
service.actions[actionName] = new ActionDefinition({
|
|
182
|
+
name: actionName,
|
|
183
|
+
access: (params, context) => context.client.user
|
|
184
|
+
&& (config.ownerUpdateAccess || config.ownerWriteAccess)(params,context),
|
|
185
|
+
properties: {
|
|
186
|
+
...originalModelProperties,
|
|
187
|
+
[modelPropertyName]: {
|
|
188
|
+
type: model,
|
|
189
|
+
validation: ['nonEmpty']
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
skipValidation: true,
|
|
193
|
+
queuedBy: (command) => command.client.user ? 'u:'+command.client.user : 's:'+command.client.session,
|
|
194
|
+
waitForEvents: true,
|
|
195
|
+
async execute(properties, { client, service }, emit) {
|
|
196
|
+
const entity = await modelRuntime().get(properties[modelPropertyName])
|
|
197
|
+
if(!entity) throw 'not_found'
|
|
198
|
+
if(entity.ownerType == 'user_User') {
|
|
199
|
+
if(entity.owner != client.user) throw 'not_authorized'
|
|
200
|
+
} else throw 'not_authorized'
|
|
201
|
+
let updateObject = {}
|
|
202
|
+
for(const propertyName of writeableProperties) {
|
|
203
|
+
if(properties.hasOwnProperty(propertyName)) {
|
|
204
|
+
updateObject[propertyName] = properties[propertyName]
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const merged = App.utils.mergeDeep({}, entity, updateObject)
|
|
208
|
+
await App.validation.validate(merged, validators, { source: action, action, service, app, client })
|
|
209
|
+
const identifiers = client.user ? {
|
|
210
|
+
ownerType: 'user_User',
|
|
211
|
+
owner: client.user,
|
|
212
|
+
} : {
|
|
213
|
+
ownerType: 'session_Session',
|
|
214
|
+
owner: client.session,
|
|
215
|
+
}
|
|
216
|
+
emit({
|
|
217
|
+
type: eventName,
|
|
218
|
+
[modelPropertyName]: entity.id,
|
|
219
|
+
identifiers,
|
|
220
|
+
data: properties
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
const action = service.actions[actionName]
|
|
225
|
+
const validators = App.validation.getValidators(action, service, action)
|
|
226
|
+
}
|
|
227
|
+
if(config.userDeleteAccess || config.userWriteAccess) {
|
|
228
|
+
const eventName = 'userOwned' + modelName + 'Deleted'
|
|
229
|
+
const actionName = 'deleteMyUser' + modelName
|
|
230
|
+
service.actions[actionName] = new ActionDefinition({
|
|
231
|
+
name: actionName,
|
|
232
|
+
access: (params, context) => context.client.user
|
|
233
|
+
&& (config.ownerDeleteAccess || config.ownerWriteAccess)(params,context),
|
|
234
|
+
properties: {
|
|
235
|
+
[modelPropertyName]: {
|
|
236
|
+
type: model,
|
|
237
|
+
validation: ['nonEmpty']
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
queuedBy: (command) => command.client.user ? 'u:'+command.client.user : 's:'+command.client.session,
|
|
241
|
+
waitForEvents: true,
|
|
242
|
+
async execute(properties, { client, service }, emit) {
|
|
243
|
+
const entity = await modelRuntime().get(properties[modelPropertyName])
|
|
244
|
+
if(!entity) throw 'not_found'
|
|
245
|
+
if(entity.ownerType == 'user_User') {
|
|
246
|
+
if(entity.owner != client.user) throw 'not_authorized'
|
|
247
|
+
} else throw 'not_authorized'
|
|
248
|
+
const identifiers = client.user ? {
|
|
249
|
+
ownerType: 'user_User',
|
|
250
|
+
owner: client.user,
|
|
251
|
+
} : {
|
|
252
|
+
ownerType: 'session_Session',
|
|
253
|
+
owner: client.session,
|
|
254
|
+
}
|
|
255
|
+
emit({
|
|
256
|
+
type: eventName,
|
|
257
|
+
[modelPropertyName]: entity.id,
|
|
258
|
+
identifiers
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
})
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
const definition = require("./definition.js")
|
|
2
|
+
const App = require("@live-change/framework")
|
|
3
|
+
const { PropertyDefinition, ViewDefinition, IndexDefinition, ActionDefinition, EventDefinition } = App
|
|
4
|
+
const { User } = require("./model.js")
|
|
5
|
+
const { allCombinations } = require("./combinations.js")
|
|
6
|
+
const { createIdentifiersProperties } = require('./utils.js')
|
|
7
|
+
|
|
8
|
+
const pluralize = require('pluralize')
|
|
9
|
+
|
|
10
|
+
definition.processor(function(service, app) {
|
|
11
|
+
|
|
12
|
+
for(let modelName in service.models) {
|
|
13
|
+
const model = service.models[modelName]
|
|
14
|
+
|
|
15
|
+
if(model.contactOrUserProperty) {
|
|
16
|
+
console.log("MODEL " + modelName + " IS SESSION OR USER PROPERTY, CONFIG:", model.userProperty)
|
|
17
|
+
if (model.properties.contactOrUser) throw new Error('owner property already exists!!!')
|
|
18
|
+
|
|
19
|
+
const originalModelProperties = { ...model.properties }
|
|
20
|
+
const modelProperties = Object.keys(model.properties)
|
|
21
|
+
const defaults = App.utils.generateDefault(model.properties)
|
|
22
|
+
|
|
23
|
+
function modelRuntime() {
|
|
24
|
+
return service._runtime.models[modelName]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const config = model.contactOrUserProperty
|
|
28
|
+
const writeableProperties = modelProperties || config.writableProperties
|
|
29
|
+
|
|
30
|
+
if(model.propertyOf) throw new Error("model " + modelName + " already have owner")
|
|
31
|
+
if(model.propertyOfAny) throw new Error("model " + modelName + " already have owner")
|
|
32
|
+
|
|
33
|
+
const extendedWith = config.extendedWith
|
|
34
|
+
? (Array.isArray(config.extendedWith) ? config.extendedWith : [config.extendedWith])
|
|
35
|
+
: []
|
|
36
|
+
|
|
37
|
+
model.propertyOfAny = {
|
|
38
|
+
...config,
|
|
39
|
+
to: ['contactOrUser', ...extendedWith]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
service.trigger({
|
|
43
|
+
name: 'contactConnected',
|
|
44
|
+
properties: {
|
|
45
|
+
contactType: {
|
|
46
|
+
type: String,
|
|
47
|
+
validation: ['nonEmpty']
|
|
48
|
+
},
|
|
49
|
+
contact: {
|
|
50
|
+
type: String,
|
|
51
|
+
validation: ['nonEmpty']
|
|
52
|
+
},
|
|
53
|
+
user: {
|
|
54
|
+
type: User,
|
|
55
|
+
validation: ['nonEmpty']
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
async execute({ contactType, contact, user }, { service }, emit) {
|
|
59
|
+
const contactPath = [contactType, contact]
|
|
60
|
+
const contactPropertyId = contactPath.map(p => JSON.stringify(p)).join(':')
|
|
61
|
+
const contactProperty = await modelRuntime().get(contactPropertyId)
|
|
62
|
+
if(contactProperty) {
|
|
63
|
+
const userPath = ['user_User', user]
|
|
64
|
+
const userPropertyId = userPath.map(p => JSON.stringify(p)).join(':')
|
|
65
|
+
const userProperty = await modelRuntime().get(userPropertyId)
|
|
66
|
+
if(config.merge) {
|
|
67
|
+
const mergeResult = await config.merge(contactProperty, userProperty)
|
|
68
|
+
if(mergeResult && userProperty) {
|
|
69
|
+
emit({
|
|
70
|
+
type: 'contactOrUserOwned' + modelName + 'Updated',
|
|
71
|
+
identifiers: {
|
|
72
|
+
ownerType: 'user_User',
|
|
73
|
+
owner: user
|
|
74
|
+
},
|
|
75
|
+
data: mergeResult
|
|
76
|
+
})
|
|
77
|
+
} else {
|
|
78
|
+
emit({
|
|
79
|
+
type: 'contactOrUserOwned' + modelName + 'Set',
|
|
80
|
+
identifiers: {
|
|
81
|
+
ownerType: 'user_User',
|
|
82
|
+
owner: user
|
|
83
|
+
},
|
|
84
|
+
data: mergeResult
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
emit({
|
|
88
|
+
type: 'contactOrUserOwned' + modelName + 'Reset',
|
|
89
|
+
identifiers: {
|
|
90
|
+
ownerType: contactType,
|
|
91
|
+
owner: contact
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
} else {
|
|
95
|
+
if(!userProperty) {
|
|
96
|
+
emit({
|
|
97
|
+
type: 'contactOrUserOwned' + modelName + 'Transferred',
|
|
98
|
+
from: {
|
|
99
|
+
ownerType: contactType,
|
|
100
|
+
owner: contact
|
|
101
|
+
},
|
|
102
|
+
to: {
|
|
103
|
+
ownerType: 'user_User',
|
|
104
|
+
owner: user
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
if(config.ownerReadAccess) { // single item view
|
|
114
|
+
const viewName = 'my' + modelName
|
|
115
|
+
const identifiers = createIdentifiersProperties(extendedWith)
|
|
116
|
+
service.views[viewName] = new ViewDefinition({
|
|
117
|
+
name: viewName,
|
|
118
|
+
properties: {
|
|
119
|
+
...identifiers
|
|
120
|
+
},
|
|
121
|
+
access(params, context) {
|
|
122
|
+
return context.client.user && (config.ownerReadAccess ? config.ownerReadAccess(params, context) : true)
|
|
123
|
+
},
|
|
124
|
+
daoPath(params, { client, context }) {
|
|
125
|
+
const owner = ['user_User', client.user]
|
|
126
|
+
for(const key of extendedWith) {
|
|
127
|
+
owner.push(params[key+'Type'], params[key])
|
|
128
|
+
}
|
|
129
|
+
const id = owner.map(p => JSON.stringify(p)).join(':')
|
|
130
|
+
return modelRuntime().path(id)
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if(config.ownerReadAccess && config.extendedWith) {
|
|
136
|
+
const extendedCombinations = [[]].concat(allCombinations(extendedWith).slice(0, -1))
|
|
137
|
+
for(const combination of extendedCombinations) {
|
|
138
|
+
const propsUpperCase = combination.map(prop => prop[0].toUpperCase() + prop.slice(1))
|
|
139
|
+
const indexName = 'by' + (combination).map(prop => prop[0].toUpperCase() + prop.slice(1))
|
|
140
|
+
const viewName = 'my' + propsUpperCase.join('And') + pluralize(modelName)
|
|
141
|
+
const identifiers = createIdentifiersProperties(combination)
|
|
142
|
+
service.views[viewName] = new ViewDefinition({
|
|
143
|
+
name: viewName,
|
|
144
|
+
properties: {
|
|
145
|
+
...identifiers,
|
|
146
|
+
...App.rangeProperties,
|
|
147
|
+
},
|
|
148
|
+
access(params, context) {
|
|
149
|
+
return context.client.user && (config.ownerReadAccess ? config.ownerReadAccess(params, context) : true)
|
|
150
|
+
},
|
|
151
|
+
daoPath(params, { client, context }) {
|
|
152
|
+
const owner = ['user_User', client.user]
|
|
153
|
+
for (const key of combination) {
|
|
154
|
+
owner.push(params[key + 'Type'], params[key])
|
|
155
|
+
}
|
|
156
|
+
return modelRuntime().indexRangePath(indexName, owner, App.extractRange(params) )
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if(config.ownerViews) {
|
|
163
|
+
for(const view of config.userViews) {
|
|
164
|
+
const viewName = view.name || ('my' + (view.prefix || '') + modelName + (view.suffix || ''))
|
|
165
|
+
service.views[viewName] = new ViewDefinition({
|
|
166
|
+
name: viewName,
|
|
167
|
+
access(params, context) {
|
|
168
|
+
return context.client.user && (view.access ? view.access(params, context) : true)
|
|
169
|
+
},
|
|
170
|
+
daoPath(params, { client, context }) {
|
|
171
|
+
const owner = ['user_User', client.user]
|
|
172
|
+
const id = owner.map(p => JSON.stringify(p)).join(':')
|
|
173
|
+
return view.fields
|
|
174
|
+
? modelRuntime().limitedPath(id, view.fields)
|
|
175
|
+
: modelRuntime().path(id)
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const eventPrefix = ['contactOrUser',
|
|
182
|
+
...(extendedWith.map(p => p[0].toUpperCase()+p.slice(1)))
|
|
183
|
+
].join('And') +'Owned'
|
|
184
|
+
|
|
185
|
+
if(config.ownerSetAccess || config.ownerWriteAccess) {
|
|
186
|
+
const eventName = eventPrefix + modelName + 'Set'
|
|
187
|
+
const actionName = 'setMy' + modelName
|
|
188
|
+
service.actions[actionName] = new ActionDefinition({
|
|
189
|
+
name: actionName,
|
|
190
|
+
properties: {
|
|
191
|
+
...originalModelProperties
|
|
192
|
+
},
|
|
193
|
+
access: (params, context) => context.client.user
|
|
194
|
+
&& (config.ownerSetAccess || config.ownerWriteAccess)(params, context),
|
|
195
|
+
skipValidation: true,
|
|
196
|
+
queuedBy: (command) => command.client.user ? 'u:'+command.client.user : 's:'+command.client.session,
|
|
197
|
+
waitForEvents: true,
|
|
198
|
+
async execute(properties, {client, service}, emit) {
|
|
199
|
+
let newObject = {}
|
|
200
|
+
for(const propertyName of writeableProperties) {
|
|
201
|
+
if(properties.hasOwnProperty(propertyName)) {
|
|
202
|
+
newObject[propertyName] = properties[propertyName]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const data = App.utils.mergeDeep({}, defaults, newObject)
|
|
206
|
+
await App.validation.validate(data, validators, { source: action, action, service, app, client })
|
|
207
|
+
const identifiers = {
|
|
208
|
+
ownerType: 'user_User',
|
|
209
|
+
owner: client.user,
|
|
210
|
+
}
|
|
211
|
+
emit({
|
|
212
|
+
type: eventName,
|
|
213
|
+
identifiers,
|
|
214
|
+
data
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
const action = service.actions[actionName]
|
|
219
|
+
const validators = App.validation.getValidators(action, service, action)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if(config.ownerUpdateAccess || config.ownerWriteAccess) {
|
|
223
|
+
const eventName = eventPrefix + modelName + 'Updated'
|
|
224
|
+
const actionName = 'updateMy' + modelName
|
|
225
|
+
service.actions[actionName] = new ActionDefinition({
|
|
226
|
+
name: actionName,
|
|
227
|
+
properties: {
|
|
228
|
+
...originalModelProperties
|
|
229
|
+
},
|
|
230
|
+
access: (params, context) => context.client.user
|
|
231
|
+
&& (config.ownerUpdateAccess || config.ownerWriteAccess)(params, context),
|
|
232
|
+
skipValidation: true,
|
|
233
|
+
queuedBy: (command) => command.client.user ? 'u:'+command.client.user : 's:'+command.client.session,
|
|
234
|
+
waitForEvents: true,
|
|
235
|
+
async execute(properties, { client, service }, emit) {
|
|
236
|
+
const owner = client.user ? ['user_User', client.user] : ['session_Session', client.session]
|
|
237
|
+
const id = owner.map(p => JSON.stringify(p)).join(':')
|
|
238
|
+
const entity = await modelRuntime().get(id)
|
|
239
|
+
if(!entity) throw 'not_found'
|
|
240
|
+
let updateObject = {}
|
|
241
|
+
for(const propertyName of writeableProperties) {
|
|
242
|
+
if(properties.hasOwnProperty(propertyName)) {
|
|
243
|
+
updateObject[propertyName] = properties[propertyName]
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const merged = App.utils.mergeDeep({}, entity, updateObject)
|
|
247
|
+
await App.validation.validate(merged, validators, { source: action, action, service, app, client })
|
|
248
|
+
const identifiers = {
|
|
249
|
+
ownerType: 'user_User',
|
|
250
|
+
owner: client.user,
|
|
251
|
+
}
|
|
252
|
+
emit({
|
|
253
|
+
type: eventName,
|
|
254
|
+
identifiers,
|
|
255
|
+
data: properties || {}
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
const action = service.actions[actionName]
|
|
260
|
+
const validators = App.validation.getValidators(action, service, action)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if(config.ownerResetAccess || config.ownerWriteAccess) {
|
|
264
|
+
const eventName = eventPrefix + modelName + 'Reset'
|
|
265
|
+
const actionName = 'resetMy' + modelName
|
|
266
|
+
service.actions[actionName] = new ActionDefinition({
|
|
267
|
+
name: actionName,
|
|
268
|
+
access: (params, context) => context.client.user
|
|
269
|
+
&& (config.ownerResetAccess || config.ownerWriteAccess)(params, context),
|
|
270
|
+
queuedBy: (command) => command.client.user ? 'u:'+command.client.user : 's:'+command.client.session,
|
|
271
|
+
waitForEvents: true,
|
|
272
|
+
async execute(properties, {client, service}, emit) {
|
|
273
|
+
const owner = client.user ? ['user_User', client.user] : ['session_Session', client.session]
|
|
274
|
+
const id = owner.map(p => JSON.stringify(p)).join(':')
|
|
275
|
+
const entity = await modelRuntime().get(id)
|
|
276
|
+
if (!entity) throw 'not_found'
|
|
277
|
+
const identifiers = {
|
|
278
|
+
ownerType: 'user_User',
|
|
279
|
+
owner: client.user,
|
|
280
|
+
}
|
|
281
|
+
emit({
|
|
282
|
+
type: eventName,
|
|
283
|
+
identifiers
|
|
284
|
+
})
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
})
|
package/index.js
CHANGED
|
@@ -7,6 +7,8 @@ require('./userProperty.js')
|
|
|
7
7
|
require('./userItem.js')
|
|
8
8
|
require('./sessionOrUserProperty.js')
|
|
9
9
|
require('./sessionOrUserItem.js')
|
|
10
|
+
require('./contactOrUserProperty.js')
|
|
11
|
+
require('./contactOrUserItem.js')
|
|
10
12
|
|
|
11
13
|
const Session = definition.foreignModel('session', 'Session')
|
|
12
14
|
|
|
@@ -44,6 +46,10 @@ definition.trigger({
|
|
|
44
46
|
async execute({ user, session }, { client, service }, emit) {
|
|
45
47
|
const userData = await User.get(user)
|
|
46
48
|
if(!userData) throw 'userNotFound'
|
|
49
|
+
await service.trigger({
|
|
50
|
+
type: 'signedIn',
|
|
51
|
+
session, user
|
|
52
|
+
})
|
|
47
53
|
emit({
|
|
48
54
|
type: "signedIn",
|
|
49
55
|
user, session
|
|
@@ -55,6 +61,11 @@ definition.action({
|
|
|
55
61
|
name: 'signOut',
|
|
56
62
|
async execute({ }, { client, service }, emit) {
|
|
57
63
|
if(!client.user) throw "notSignedIn"
|
|
64
|
+
await service.trigger({
|
|
65
|
+
type: 'signedOut',
|
|
66
|
+
session: client.session,
|
|
67
|
+
user: client.user
|
|
68
|
+
})
|
|
58
69
|
emit({
|
|
59
70
|
type: "signedOut",
|
|
60
71
|
user: client.user,
|
|
@@ -78,6 +89,10 @@ definition.trigger({
|
|
|
78
89
|
if(!user) {
|
|
79
90
|
user = app.generateUid()
|
|
80
91
|
}
|
|
92
|
+
await service.trigger({
|
|
93
|
+
type: 'signedIn',
|
|
94
|
+
session, user
|
|
95
|
+
})
|
|
81
96
|
emit([{
|
|
82
97
|
type: "created",
|
|
83
98
|
user
|
|
@@ -98,6 +113,14 @@ definition.action({
|
|
|
98
113
|
},
|
|
99
114
|
async execute({ }, { client, service }, emit) {
|
|
100
115
|
const user = client.user
|
|
116
|
+
await service.trigger({
|
|
117
|
+
type: 'signedOut',
|
|
118
|
+
session: client.session, user: client.user
|
|
119
|
+
})
|
|
120
|
+
await service.trigger({
|
|
121
|
+
type: 'userDeleted',
|
|
122
|
+
user: client.user
|
|
123
|
+
})
|
|
101
124
|
await service.trigger({
|
|
102
125
|
type: 'userDeleted',
|
|
103
126
|
user
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/user-service",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.27",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"url": "https://www.viamage.com/"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@live-change/framework": "0.
|
|
25
|
-
"@live-change/relations-plugin": "
|
|
24
|
+
"@live-change/framework": "0.6.0",
|
|
25
|
+
"@live-change/relations-plugin": "0.6.0",
|
|
26
26
|
"pluralize": "8.0.0"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "9a82ff0e7a7003d5b4e34ef9aef1ad4d7d8605dd"
|
|
29
29
|
}
|
package/sessionOrUserItem.js
CHANGED
|
@@ -30,6 +30,9 @@ definition.processor(function(service, app) {
|
|
|
30
30
|
...config
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/// TODO: merge on signedIn trigger
|
|
34
|
+
/// TODO: delete on userDeleted trigger
|
|
35
|
+
|
|
33
36
|
if(config.ownerReadAccess) {
|
|
34
37
|
const viewName = 'my' + pluralize(modelName)
|
|
35
38
|
service.views[viewName] = new ViewDefinition({
|
|
@@ -50,13 +53,12 @@ definition.processor(function(service, app) {
|
|
|
50
53
|
service.views[viewName] = new ViewDefinition({
|
|
51
54
|
name: viewName,
|
|
52
55
|
access(params, context) {
|
|
53
|
-
|
|
54
|
-
return config.userReadAccess ? config.userReadAccess(params, context) : true
|
|
56
|
+
return config.ownerReadAccess(params, context)
|
|
55
57
|
},
|
|
56
58
|
properties: App.rangeProperties,
|
|
57
59
|
daoPath(range, { client, context }) {
|
|
58
60
|
const owner = client.user ? ['user_User', client.user] : ['session_Session', client.session]
|
|
59
|
-
return modelRuntime().sortedIndexRangePath('byOwner' + sortFieldUc, owner, range)
|
|
61
|
+
return modelRuntime().sortedIndexRangePath('byOwner' + sortFieldUc, owner, App.extractRange(range))
|
|
60
62
|
}
|
|
61
63
|
})
|
|
62
64
|
}
|
package/sessionOrUserProperty.js
CHANGED
|
@@ -2,6 +2,11 @@ const definition = require("./definition.js")
|
|
|
2
2
|
const App = require("@live-change/framework")
|
|
3
3
|
const { PropertyDefinition, ViewDefinition, IndexDefinition, ActionDefinition, EventDefinition } = App
|
|
4
4
|
const { User } = require("./model.js")
|
|
5
|
+
const { allCombinations } = require("./combinations.js")
|
|
6
|
+
const { createIdentifiersProperties } = require('./utils.js')
|
|
7
|
+
|
|
8
|
+
const pluralize = require('pluralize')
|
|
9
|
+
|
|
5
10
|
|
|
6
11
|
definition.processor(function(service, app) {
|
|
7
12
|
|
|
@@ -9,8 +14,8 @@ definition.processor(function(service, app) {
|
|
|
9
14
|
const model = service.models[modelName]
|
|
10
15
|
|
|
11
16
|
if(model.sessionOrUserProperty) {
|
|
12
|
-
console.log("MODEL " + modelName + " IS SESSION OR USER PROPERTY, CONFIG:", model.
|
|
13
|
-
if (model.properties.
|
|
17
|
+
console.log("MODEL " + modelName + " IS SESSION OR USER PROPERTY, CONFIG:", model.sessionOrUserProperty)
|
|
18
|
+
if (model.properties.sessionOrUser) throw new Error('sessionOrUser property already exists!!!')
|
|
14
19
|
|
|
15
20
|
const originalModelProperties = { ...model.properties }
|
|
16
21
|
const modelProperties = Object.keys(model.properties)
|
|
@@ -23,26 +28,70 @@ definition.processor(function(service, app) {
|
|
|
23
28
|
const config = model.sessionOrUserProperty
|
|
24
29
|
const writeableProperties = modelProperties || config.writableProperties
|
|
25
30
|
|
|
31
|
+
if(model.propertyOf) throw new Error("model " + modelName + " already have owner")
|
|
26
32
|
if(model.propertyOfAny) throw new Error("model " + modelName + " already have owner")
|
|
33
|
+
|
|
34
|
+
const extendedWith = config.extendedWith
|
|
35
|
+
? (Array.isArray(config.extendedWith) ? config.extendedWith : [config.extendedWith])
|
|
36
|
+
: []
|
|
27
37
|
model.propertyOfAny = {
|
|
28
|
-
...config
|
|
38
|
+
...config,
|
|
39
|
+
to: ['sessionOrUser', ...extendedWith]
|
|
29
40
|
}
|
|
30
41
|
|
|
31
|
-
|
|
42
|
+
/// TODO: merge on signedIn trigger
|
|
43
|
+
/// TODO: delete on userDeleted trigger
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if(config.ownerReadAccess) { // single item view
|
|
32
47
|
const viewName = 'my' + modelName
|
|
48
|
+
const identifiers = createIdentifiersProperties(extendedWith)
|
|
33
49
|
service.views[viewName] = new ViewDefinition({
|
|
34
50
|
name: viewName,
|
|
51
|
+
properties: {
|
|
52
|
+
...identifiers
|
|
53
|
+
},
|
|
35
54
|
access(params, context) {
|
|
36
55
|
return config.ownerReadAccess ? config.ownerReadAccess(params, context) : true
|
|
37
56
|
},
|
|
38
57
|
daoPath(params, { client, context }) {
|
|
39
58
|
const owner = client.user ? ['user_User', client.user] : ['session_Session', client.session]
|
|
59
|
+
for(const key of extendedWith) {
|
|
60
|
+
owner.push(params[key+'Type'], params[key])
|
|
61
|
+
}
|
|
40
62
|
const id = owner.map(p => JSON.stringify(p)).join(':')
|
|
41
63
|
return modelRuntime().path(id)
|
|
42
64
|
}
|
|
43
65
|
})
|
|
44
66
|
}
|
|
45
67
|
|
|
68
|
+
if(config.ownerReadAccess && config.extendedWith) {
|
|
69
|
+
const extendedCombinations = [[]].concat(allCombinations(extendedWith).slice(0, -1))
|
|
70
|
+
for(const combination of extendedCombinations) {
|
|
71
|
+
const propsUpperCase = combination.map(prop => prop[0].toUpperCase() + prop.slice(1))
|
|
72
|
+
const indexName = 'by' + (combination).map(prop => prop[0].toUpperCase() + prop.slice(1))
|
|
73
|
+
const viewName = 'my' + propsUpperCase.join('And') + pluralize(modelName)
|
|
74
|
+
const identifiers = createIdentifiersProperties(combination)
|
|
75
|
+
service.views[viewName] = new ViewDefinition({
|
|
76
|
+
name: viewName,
|
|
77
|
+
properties: {
|
|
78
|
+
...identifiers,
|
|
79
|
+
...App.rangeProperties,
|
|
80
|
+
},
|
|
81
|
+
access(params, context) {
|
|
82
|
+
return config.ownerReadAccess ? config.ownerReadAccess(params, context) : true
|
|
83
|
+
},
|
|
84
|
+
daoPath(params, { client, context }) {
|
|
85
|
+
const owner = client.user ? ['user_User', client.user] : ['session_Session', client.session]
|
|
86
|
+
for (const key of combination) {
|
|
87
|
+
owner.push(params[key + 'Type'], params[key])
|
|
88
|
+
}
|
|
89
|
+
return modelRuntime().indexRangePath(indexName, owner, App.extractRange(params) )
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
46
95
|
if(config.ownerViews) {
|
|
47
96
|
for(const view of config.userViews) {
|
|
48
97
|
const viewName = view.name || ('my' + (view.prefix || '') + modelName + (view.suffix || ''))
|
|
@@ -62,8 +111,12 @@ definition.processor(function(service, app) {
|
|
|
62
111
|
}
|
|
63
112
|
}
|
|
64
113
|
|
|
114
|
+
const eventPrefix = ['sessionOrUser',
|
|
115
|
+
...(extendedWith.map(p => p[0].toUpperCase()+p.slice(1)))
|
|
116
|
+
].join('And') +'Owned'
|
|
117
|
+
|
|
65
118
|
if(config.ownerSetAccess || config.ownerWriteAccess) {
|
|
66
|
-
const eventName =
|
|
119
|
+
const eventName = eventPrefix + modelName + 'Set'
|
|
67
120
|
const actionName = 'setMy' + modelName
|
|
68
121
|
service.actions[actionName] = new ActionDefinition({
|
|
69
122
|
name: actionName,
|
|
@@ -102,7 +155,7 @@ definition.processor(function(service, app) {
|
|
|
102
155
|
}
|
|
103
156
|
|
|
104
157
|
if(config.ownerUpdateAccess || config.ownerWriteAccess) {
|
|
105
|
-
const eventName =
|
|
158
|
+
const eventName = eventPrefix + modelName + 'Updated'
|
|
106
159
|
const actionName = 'updateMy' + modelName
|
|
107
160
|
service.actions[actionName] = new ActionDefinition({
|
|
108
161
|
name: actionName,
|
|
@@ -145,7 +198,7 @@ definition.processor(function(service, app) {
|
|
|
145
198
|
}
|
|
146
199
|
|
|
147
200
|
if(config.ownerResetAccess || config.ownerWriteAccess) {
|
|
148
|
-
const eventName =
|
|
201
|
+
const eventName = eventPrefix + modelName + 'Reset'
|
|
149
202
|
const actionName = 'resetMy' + modelName
|
|
150
203
|
service.actions[actionName] = new ActionDefinition({
|
|
151
204
|
name: actionName,
|
package/userItem.js
CHANGED
package/userProperty.js
CHANGED
package/utils.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function createIdentifiersProperties(keys) {
|
|
2
|
+
const identifiers = {}
|
|
3
|
+
if(keys) for(const key of keys) {
|
|
4
|
+
identifiers[key] = {
|
|
5
|
+
type: String,
|
|
6
|
+
validation: ['nonEmpty']
|
|
7
|
+
}
|
|
8
|
+
identifiers[key + 'Type'] = {
|
|
9
|
+
type: String,
|
|
10
|
+
validation: ['nonEmpty']
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return identifiers
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { createIdentifiersProperties }
|