@live-change/framework 0.9.137 → 0.9.139
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/lib/definition/QueryDefinition.ts +59 -0
- package/lib/definition/ServiceDefinition.ts +27 -1
- package/lib/definition/types.ts +1 -0
- package/lib/runtime/LiveDao.js +4 -1
- package/lib/runtime/Query.js +101 -0
- package/lib/runtime/Service.js +5 -0
- package/lib/updaters/database.js +38 -4
- package/package.json +4 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AccessSpecification } from "../processors/accessMethod.js"
|
|
2
|
+
import PropertyDefinition, { PropertyDefinitionSpecification } from "./PropertyDefinition.js"
|
|
3
|
+
import type { ContextBase, QueryParameters } from "./types.js"
|
|
4
|
+
|
|
5
|
+
type QueryCode = string | ((input: any, output: any, parameters: QueryParameters) => Promise<any>)
|
|
6
|
+
|
|
7
|
+
export interface QueryDefinitionSpecification {
|
|
8
|
+
name: string
|
|
9
|
+
properties: Record<string, PropertyDefinitionSpecification>
|
|
10
|
+
returns?: PropertyDefinitionSpecification,
|
|
11
|
+
code: QueryCode,
|
|
12
|
+
sourceName: string,
|
|
13
|
+
update: boolean,
|
|
14
|
+
|
|
15
|
+
validation?: (parameters: QueryParameters, context: ContextBase) => Promise<any>
|
|
16
|
+
waitForEvents?: boolean,
|
|
17
|
+
timeout?: number,
|
|
18
|
+
requestTimeout?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class QueryDefinition<T extends QueryDefinitionSpecification> {
|
|
22
|
+
[key: string]: any
|
|
23
|
+
|
|
24
|
+
constructor(definition: T) {
|
|
25
|
+
this.properties = {}
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
for(let key in definition) this[key] = definition[key]
|
|
28
|
+
if(definition.properties) {
|
|
29
|
+
for (let propName in definition.properties) {
|
|
30
|
+
const propDefn = definition.properties[propName]
|
|
31
|
+
this.createAndAddProperty(propName, propDefn)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if(definition.returns) {
|
|
35
|
+
this.returns = new PropertyDefinition(definition.returns)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
createAndAddProperty(name, definition) {
|
|
40
|
+
const property = new PropertyDefinition(definition)
|
|
41
|
+
this.properties[name] = property
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
toJSON() {
|
|
45
|
+
let properties = {}
|
|
46
|
+
for(let propName in this.properties) {
|
|
47
|
+
properties[propName] = this.properties[propName].toJSON()
|
|
48
|
+
}
|
|
49
|
+
let returns = this.returns ? this.returns.toJSON() : null
|
|
50
|
+
return {
|
|
51
|
+
... this,
|
|
52
|
+
properties,
|
|
53
|
+
returns
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default QueryDefinition
|
|
@@ -8,6 +8,7 @@ import ViewDefinition, { ViewDefinitionSpecification } from "./ViewDefinition.js
|
|
|
8
8
|
import EventDefinition from "./EventDefinition.js"
|
|
9
9
|
import defaultValidators from '../utils/validators.js'
|
|
10
10
|
import { crudChanges, definitionToJSON } from "../utils.js"
|
|
11
|
+
import QueryDefinition, { QueryDefinitionSpecification } from "./QueryDefinition.js"
|
|
11
12
|
|
|
12
13
|
function createModelProxy(definition, model) {
|
|
13
14
|
return new Proxy(model, {
|
|
@@ -72,6 +73,21 @@ function createForeignIndexProxy(definition, model) {
|
|
|
72
73
|
})
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
function createQueryProxy(definition, query) {
|
|
77
|
+
return new Proxy(query, {
|
|
78
|
+
get(target, prop, receiver) {
|
|
79
|
+
const runtime = definition._runtime
|
|
80
|
+
if(runtime) {
|
|
81
|
+
const queryRuntime = runtime.queries[query.name]
|
|
82
|
+
if(queryRuntime[prop]) {
|
|
83
|
+
return Reflect.get(queryRuntime, prop, receiver)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return Reflect.get(target, prop, receiver)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
75
91
|
|
|
76
92
|
|
|
77
93
|
export interface ServiceDefinitionSpecification {
|
|
@@ -99,6 +115,7 @@ class ServiceDefinition<T extends ServiceDefinitionSpecification> {
|
|
|
99
115
|
this.endpoints = []
|
|
100
116
|
this.validators = { ...defaultValidators }
|
|
101
117
|
this.clientSideFilters = []
|
|
118
|
+
this.queries = {}
|
|
102
119
|
// @ts-ignore
|
|
103
120
|
for(let key in definition) this[key] = definition[key]
|
|
104
121
|
}
|
|
@@ -193,6 +210,13 @@ class ServiceDefinition<T extends ServiceDefinitionSpecification> {
|
|
|
193
210
|
this.clientSideFilters.push(filter)
|
|
194
211
|
}
|
|
195
212
|
|
|
213
|
+
query<T extends QueryDefinitionSpecification>(definition: T) {
|
|
214
|
+
if(this.queries[definition.name]) throw new Error('query ' + definition.name + ' already exists')
|
|
215
|
+
const query = new QueryDefinition<T>(definition)
|
|
216
|
+
this.queries[query.name] = query
|
|
217
|
+
return createQueryProxy(this, query)
|
|
218
|
+
}
|
|
219
|
+
|
|
196
220
|
toJSON() {
|
|
197
221
|
let models = {}
|
|
198
222
|
for(let key in this.models) models[key] = this.models[key].toJSON()
|
|
@@ -235,9 +259,11 @@ class ServiceDefinition<T extends ServiceDefinitionSpecification> {
|
|
|
235
259
|
let oldModule = JSON.parse(JSON.stringify(oldModuleParam))
|
|
236
260
|
let changes: Record<string, any>[] = []
|
|
237
261
|
changes.push(...crudChanges(oldModule.models || {}, this.models || {},
|
|
238
|
-
|
|
262
|
+
"Model", "model", { }))
|
|
239
263
|
changes.push(...crudChanges(oldModule.indexes || {}, this.indexes || {},
|
|
240
264
|
"Index", "index", { }))
|
|
265
|
+
changes.push(...crudChanges(oldModule.queries || {}, this.queries || {},
|
|
266
|
+
"Query", "query", { }))
|
|
241
267
|
return changes
|
|
242
268
|
}
|
|
243
269
|
}
|
package/lib/definition/types.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface ViewContext extends ContextBase {
|
|
|
19
19
|
export type ActionParameters = Record<string, any>
|
|
20
20
|
export type TriggerParameters = Record<string, any>
|
|
21
21
|
export type EventParameters = Record<string, any>
|
|
22
|
+
export type QueryParameters = Record<string, any>
|
|
22
23
|
|
|
23
24
|
export interface TriggerSettings {
|
|
24
25
|
service?: string,
|
package/lib/runtime/LiveDao.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import LcDao from "@live-change/dao"
|
|
2
2
|
import Dao from "./Dao.js"
|
|
3
|
+
import { originalCredentialsSymbol } from "@live-change/dao"
|
|
3
4
|
|
|
4
5
|
import { waitForSignal } from './utils.js'
|
|
5
6
|
|
|
@@ -31,12 +32,14 @@ class LiveDao extends LcDao.DaoProxy {
|
|
|
31
32
|
|
|
32
33
|
computeCredentials() {
|
|
33
34
|
let credentials = JSON.parse(JSON.stringify(this.initialCredentials))
|
|
35
|
+
const originalCredentials = JSON.parse(JSON.stringify(this.initialCredentials))
|
|
34
36
|
const keys = Object.keys(credentials).filter(key => key.endsWith("Key"))
|
|
35
37
|
for(const credentialsObserver of this.credentialsObservations) {
|
|
36
38
|
credentials = {
|
|
37
39
|
...credentials,
|
|
38
40
|
...credentialsObserver.credentials,
|
|
39
|
-
roles: [...credentials.roles, ...(credentialsObserver.credentials.roles || [])]
|
|
41
|
+
roles: [...credentials.roles, ...(credentialsObserver.credentials.roles || [])],
|
|
42
|
+
[originalCredentialsSymbol]: originalCredentials
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
for(const key of keys) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { prepareParameters, processReturn, preFilterParameters } from "./params.js"
|
|
2
|
+
|
|
3
|
+
class Query {
|
|
4
|
+
|
|
5
|
+
constructor(definition, service) {
|
|
6
|
+
this.definition = definition
|
|
7
|
+
this.service = service
|
|
8
|
+
this.queryKey = `${service.name}.${definition.name}`
|
|
9
|
+
this.isObjectQuery = definition.returns?.type !== Array && definition.returns?.type !== 'Array'
|
|
10
|
+
&& definition.returns?.type !== 'array'
|
|
11
|
+
this.codeString = typeof definition.code === 'function' ? `(${definition.code.toString()})` : definition.code
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async run(parameters, context) {
|
|
15
|
+
if(!this.definition.update) throw new Error("Only update queries can be run")
|
|
16
|
+
|
|
17
|
+
if(!parameters) parameters = {}
|
|
18
|
+
if(!context) context = {
|
|
19
|
+
client: {
|
|
20
|
+
session: null,
|
|
21
|
+
user: null,
|
|
22
|
+
ip: null,
|
|
23
|
+
roles: []
|
|
24
|
+
},
|
|
25
|
+
service: this.service,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let preparedParams = await prepareParameters(parameters, this.definition.properties, this.service)
|
|
29
|
+
|
|
30
|
+
const resultPromise = this.service.app.dao.requestWithSettings({
|
|
31
|
+
timeout: this.definition.timeout,
|
|
32
|
+
}, ['database', 'runQuery'], this.service.app.databaseName, 'queries', this.queryKey, preparedParams)
|
|
33
|
+
|
|
34
|
+
resultPromise.catch(error => {
|
|
35
|
+
console.error(`Query ${this.definition.name} error `, error.stack || error)
|
|
36
|
+
})
|
|
37
|
+
return resultPromise
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get(parameters, context) {
|
|
41
|
+
if(this.definition.update) throw new Error("Only non-update queries can be get")
|
|
42
|
+
|
|
43
|
+
if(!parameters) parameters = {}
|
|
44
|
+
if(!context) context = {
|
|
45
|
+
client: {
|
|
46
|
+
session: null,
|
|
47
|
+
user: null,
|
|
48
|
+
ip: null,
|
|
49
|
+
roles: []
|
|
50
|
+
},
|
|
51
|
+
service: this.service,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const resultPromise = this.service.app.dao.get([
|
|
55
|
+
'database', this.definition.isObjectQuery ? 'runQueryObject' : 'runQuery',
|
|
56
|
+
this.service.app.databaseName, 'queries', this.queryKey, parameters
|
|
57
|
+
])
|
|
58
|
+
resultPromise.catch(error => {
|
|
59
|
+
console.error(`Query ${this.definition.name} error `, error.stack || error)
|
|
60
|
+
})
|
|
61
|
+
return resultPromise
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
observable(parameters, context) {
|
|
65
|
+
if(this.definition.update) throw new Error("Only non-update queries can be observable")
|
|
66
|
+
if(!parameters) parameters = {}
|
|
67
|
+
if(!context) context = {
|
|
68
|
+
client: {
|
|
69
|
+
session: null,
|
|
70
|
+
user: null,
|
|
71
|
+
ip: null,
|
|
72
|
+
roles: []
|
|
73
|
+
},
|
|
74
|
+
service: this.service,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const resultPromise = this.service.app.dao.observable([
|
|
78
|
+
'database', this.definition.isObjectQuery ? 'runQueryObject' : 'runQuery',
|
|
79
|
+
this.service.app.databaseName, 'queries', this.queryKey, parameters
|
|
80
|
+
])
|
|
81
|
+
resultPromise.catch(error => {
|
|
82
|
+
console.error(`Query ${this.definition.name} error `, error.stack || error)
|
|
83
|
+
})
|
|
84
|
+
return resultPromise
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
toJSON() {
|
|
88
|
+
return {
|
|
89
|
+
name: this.definition.name,
|
|
90
|
+
properties: this.definition.properties,
|
|
91
|
+
returns: this.definition.returns,
|
|
92
|
+
code: this.codeString,
|
|
93
|
+
sourceName: this.definition.sourceName,
|
|
94
|
+
update: this.definition.update,
|
|
95
|
+
timeout: this.definition.timeout,
|
|
96
|
+
requestTimeout: this.definition.requestTimeout,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default Query
|
package/lib/runtime/Service.js
CHANGED
|
@@ -70,6 +70,11 @@ class Service {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
this.authenticators = this.definition.authenticators
|
|
73
|
+
|
|
74
|
+
this.queries = {}
|
|
75
|
+
for(let queryName in this.definition.queries) {
|
|
76
|
+
this.queries[queryName] = new Query( this.definition.queries[queryName], this )
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
async start(config) {
|
package/lib/updaters/database.js
CHANGED
|
@@ -10,6 +10,8 @@ async function update(changes, service, app, force) {
|
|
|
10
10
|
const dao = app.dao
|
|
11
11
|
const database = app.databaseName
|
|
12
12
|
|
|
13
|
+
dao.request(['database', 'createTable'], database, 'queries').catch(e => 'ok')
|
|
14
|
+
|
|
13
15
|
if(!app.noCache) {
|
|
14
16
|
dao.request(['database', 'createTable'], database, 'cache').catch(e => 'ok')
|
|
15
17
|
dao.request(['database', 'createIndex'], database, 'cache_byTimestamp', `${
|
|
@@ -110,10 +112,6 @@ async function update(changes, service, app, force) {
|
|
|
110
112
|
|
|
111
113
|
debug("CREATE INDEX", indexName, index)
|
|
112
114
|
|
|
113
|
-
const options = {
|
|
114
|
-
multi: index.multi || false
|
|
115
|
-
}
|
|
116
|
-
|
|
117
115
|
if(index.function) {
|
|
118
116
|
const functionCode = `(${index.function})`
|
|
119
117
|
;(globalThis.compiledFunctionsCandidates = globalThis.compiledFunctionsCandidates || {})[functionCode] = index.function
|
|
@@ -322,6 +320,42 @@ async function update(changes, service, app, force) {
|
|
|
322
320
|
}
|
|
323
321
|
})`, { table, property: change.name })*/
|
|
324
322
|
} break;
|
|
323
|
+
case "createQuery": {
|
|
324
|
+
const query = change.query
|
|
325
|
+
const queryKey = service.name + '.' + query.name
|
|
326
|
+
await dao.request(['database', 'put'], database, 'queries', {
|
|
327
|
+
id: queryKey,
|
|
328
|
+
code: typeof query.code === 'function' ? `(${query.code.toString()})` : query.code,
|
|
329
|
+
sourceName: query.sourceName,
|
|
330
|
+
update: query.update,
|
|
331
|
+
timeout: query.timeout,
|
|
332
|
+
properties: query.properties,
|
|
333
|
+
returns: query.returns
|
|
334
|
+
})
|
|
335
|
+
debug("QUERY CREATED!", query.name)
|
|
336
|
+
} break;
|
|
337
|
+
case "renameQuery": {
|
|
338
|
+
const query = change.to
|
|
339
|
+
const queryKey = service.name + '.' + query.name
|
|
340
|
+
const oldQueryKey = service.name + '.' + change.from.name
|
|
341
|
+
const oldQuery = await dao.get(['database', 'get', database, 'queries', oldQueryKey])
|
|
342
|
+
await dao.request(['database', 'put'], database, 'queries', {
|
|
343
|
+
id: queryKey,
|
|
344
|
+
code: typeof oldQuery.code === 'function' ? `(${oldQuery.code.toString()})` : oldQuery.code,
|
|
345
|
+
sourceName: oldQuery.sourceName,
|
|
346
|
+
update: oldQuery.update,
|
|
347
|
+
timeout: oldQuery.timeout,
|
|
348
|
+
properties: oldQuery.properties,
|
|
349
|
+
returns: oldQuery.returns
|
|
350
|
+
})
|
|
351
|
+
await dao.request(['database', 'delete'], database, 'queries', oldQueryKey)
|
|
352
|
+
debug("QUERY RENAMED!", query.name)
|
|
353
|
+
} break;
|
|
354
|
+
case "deleteQuery": {
|
|
355
|
+
const queryKey = service.name + '.' + change.name
|
|
356
|
+
await dao.request(['database', 'delete'], database, 'queries', queryKey)
|
|
357
|
+
debug("QUERY DELETED!", change.name)
|
|
358
|
+
} break;
|
|
325
359
|
default:
|
|
326
360
|
}
|
|
327
361
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/framework",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.139",
|
|
4
4
|
"description": "Live Change Framework - ultimate solution for real time mobile/web apps",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/live-change/live-change-stack",
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@live-change/dao": "^0.9.
|
|
26
|
-
"@live-change/uid": "^0.9.
|
|
25
|
+
"@live-change/dao": "^0.9.139",
|
|
26
|
+
"@live-change/uid": "^0.9.139",
|
|
27
27
|
"typedoc": "0.28.3",
|
|
28
28
|
"typedoc-plugin-markdown": "^4.6.3",
|
|
29
29
|
"typedoc-plugin-rename-defaults": "^0.7.3"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "9202c36abc25e3baf5fe39806d89c3fab203f428"
|
|
32
32
|
}
|