| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 |
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
|
import _ from 'lodash'
import { marshallComparisonFields, marshallSortFields, marshallTime } from '../marshall'
import { ObjectID } from 'mongodb'
import makeDebug from 'debug'
const debug = makeDebug('kalisio:kCore:query:hooks')
export function marshallTimeQuery (hook) {
let query = hook.params.query
if (query) {
// Need to convert from client/server side types : string or moment dates
marshallTime(query, 'time')
}
}
export function marshallComparisonQuery (hook) {
let query = hook.params.query
Eif (query) {
// Complex queries might have nested objects so we call a recursive function to handle this
marshallComparisonFields(query)
}
}
export function marshallSortQuery (hook) {
let query = hook.params.query
if (query && query.$sort) {
// Complex queries might have nested objects so we call a recursive function to handle this
marshallSortFields(query.$sort)
}
}
export function marshallCollationQuery (hook) {
let query = hook.params.query
Iif (!query) return
// Locale shortcut or whole query provided
Eif (query.$locale) {
hook.params.collation = { locale: query.$locale }
delete query.$locale
} else if (query.$collation) {
hook.params.collation = query.$collation
delete query.$collation
}
return hook
}
export function populateObject (options) {
return function (hook) {
let app = hook.app
let data = hook.data
let params = hook.params
let query = params.query
const context = hook.service.context
const idProperty = options.nameIdAs || options.idField
const serviceProperty = options.nameServiceAs || options.serviceField
// Check if not already done
if (typeof _.get(params, idProperty) === 'object') {
debug(`Skipping populating ${idProperty} as already done`)
return Promise.resolve(hook)
}
if (typeof _.get(params, serviceProperty) === 'object') {
debug(`Skipping populating ${serviceProperty} as already done`)
return Promise.resolve(hook)
}
// Get service where we can find the object to populate
// Make hook usable with query params as well and service name or real object
let service = _.get(data, options.serviceField) || _.get(query, options.serviceField)
if (typeof service === 'string') {
const message = `Cannot find the service for ${options.serviceField} = ${service} to dynamically populate.`
service = app.getService(service, context)
if (!service) {
if (options.throwOnNotFound) throw new Error(message)
else return Promise.resolve(hook)
}
} else if (!service) {
if (options.throwOnNotFound) throw new Error(`No ${options.serviceField} given to dynamically populate.`)
else return Promise.resolve(hook)
}
// Then the object ID
let id = _.get(data, options.idField) || _.get(query, options.idField) || _.get(hook, 'id')
if (!id) {
if (options.throwOnNotFound) throw new Error(`Cannot find the ${options.idField} to dynamically populate.`)
else return Promise.resolve(hook)
}
// Then the perspective if any
let perspective = _.get(data, options.perspectiveField) || _.get(query, options.perspectiveField)
debug(`Populating ${idProperty} with ID ${id}`)
// Set the retrieved service on the same field or given one in hook params
_.set(params, serviceProperty, service)
// Let it work with id string or real object
if (typeof id === 'string' || ObjectID.isValid(id)) {
let args = { user: hook.params.user }
if (perspective) args = Object.assign(args, { query: { $select: [perspective] } })
return service.get(id.toString(), args).then(object => {
if (!object) {
if (options.throwOnNotFound) throw new Error(`Cannot find object with id ${id} to dynamically populate.`)
else return hook
}
// Set the retrieved object on the same field or given one in hook params
_.set(params, idProperty, object)
return hook
})
} else {
// Set the object on the same field or given one in hook params
_.set(params, idProperty, id)
return Promise.resolve(hook)
}
}
}
export function unpopulateObject (options) {
return function (hook) {
let params = hook.params
const idProperty = options.nameIdAs || options.idField
const serviceProperty = options.nameServiceAs || options.serviceField
// Check if not already done
_.unset(params, idProperty)
_.unset(params, serviceProperty)
return hook
}
}
export function populateObjects (options) {
return function (hook) {
let app = hook.app
let data = hook.data
let params = hook.params
let query = params.query
const context = hook.service.context
const idProperty = options.nameIdAs || options.idField
const serviceProperty = options.nameServiceAs || options.serviceField
// Check if not already done
if (Array.isArray(_.get(params, idProperty))) {
debug(`Skipping populating ${idProperty} as already done`)
return Promise.resolve(hook)
}
if (typeof _.get(params, serviceProperty) === 'object') {
debug(`Skipping populating ${serviceProperty} as already done`)
return Promise.resolve(hook)
}
// Get service where we can find the object to populate
// Make hook usable with query params as well and service name or real object
let service = _.get(data, options.serviceField) || _.get(query, options.serviceField)
if (typeof service === 'string') {
const message = `Cannot find the service for ${options.serviceField} = ${service} to dynamically populate.`
service = app.getService(service, context)
if (!service) {
if (options.throwOnNotFound) throw new Error(message)
else return Promise.resolve(hook)
}
} else if (!service) {
if (options.throwOnNotFound) throw new Error(`No ${options.serviceField} given to dynamically populate.`)
else return Promise.resolve(hook)
}
// Set the retrieved service on the same field or given one in hook params
_.set(params, serviceProperty, service)
// Then the object ID
let id = _.get(data, options.idField) || _.get(query, options.idField)
// If no ID given we perform a find, no pagination to be sure we get all objects
if (!id) {
debug(`Populating ${idProperty}`)
return service.find({ query: {}, paginate: false, user: hook.params.user }).then(objects => {
// Set the retrieved objects on the same field or given one in hook params
debug(`Populated ${objects.length} ${idProperty}`)
_.set(params, idProperty, objects)
return hook
})
} else {
debug(`Populating ${idProperty} with ID ${id}`)
// Let it work with id string or real object
if (typeof id === 'string' || ObjectID.isValid(id)) {
return service.get(id.toString(), { user: hook.params.user }).then(object => {
if (!object) {
if (options.throwOnNotFound) throw new Error(`Cannot find ${options.idField} = ${id} to dynamically populate.`)
else return hook
}
// Set the retrieved object on the same field or given one in hook params
_.set(params, idProperty, [object])
return hook
})
} else {
// Set the object on the same field or given one in hook params
_.set(params, idProperty, [id])
return Promise.resolve(hook)
}
}
}
}
export function unpopulateObjects (options) {
// These are similar behaviour
return unpopulateObject(options)
}
|