Bläddra i källkod

add util/make_model

master
Bing Sun 5 år sedan
förälder
incheckning
838b8b10ce
Signerad av: sunb GPG-nyckel ID: F7795F8C590626AB
1 ändrade filer med 183 tillägg och 0 borttagningar
  1. +183
    -0
      src/util/make_model.js

+ 183
- 0
src/util/make_model.js Visa fil

@@ -0,0 +1,183 @@
/* building a model by postgrest
*
* a model is simply a cache layer between client
* and the database behind an api
*
* options: {
* endpoint: 'resource location',
* selects: [
* {
* 'label': 'column name',
* 'alias': 'displayed name',
* 'filter': v => value_processor(v)
* }
* ]
* }
*/

export default (options={}) => {
// TODO
// 1. sigular or prural

// sanity checks
if (typeof options.api == 'undefined' || typeof options.endpoint == 'undefined')
throw 'api and endpoint are required options'

// private model configs
let _configs = {
// url part
api: options.api,
endpoint: options.endpoint,
endpoint_type: options.endpoint.startsWith('/rpc') ? 'function' : 'relation',
// query part
selects: options.selects || [],
}

// private model cache & meta-info
let _cache = {
data: null,
count: null
}

// some random places to make things work
let _xhr = null

// construct the model
return {
// reflection methods
configs() { return _configs },
cache() { return _cache },

// main methods
select(from=0, to) {
// initialize data (singular or plural)
_cache.data = _cache.data || []

// now the hard work
return _configs.api.request({
method: 'GET',
url: _configs.endpoint,
headers: _cache.count == null ? {
Prefer: 'count=exact'
} : {},
config: xhr => _xhr = xhr,
//
// transform model state to postgest queries
//queries: [...ambient_queries, ...paging_queries]
}).then(response => {
// gather begin/end/count
let [_range, _count] = _xhr.getResponseHeader('content-range').split('/')
let [_begin, _end] = _range.split('-').map(v => ~~v)

// update count if presented
if (_count != '*') _cache.count = _count

// fill the data cache
response.forEach((data, i) => {
// _value_filters.forEach(([label, config]) => data[config.alias || label] = config.value_filter(data[config.alias || label]))
_cache.data[_begin + i] = data
})

// for promise
return _cache.data.slice(_begin, _end + 1)
})
}
}
}

export const default1 = (args) => {
// private model configs
let _configs = {
queries: args.queries || [],
order: args.order || []
}
_configs.selects.forEach((config, label, selects) => {
selects.set(label, config || {})
})

// prepare the model value filters
let _value_filters = Array.from(_configs.selects).filter(([label, config]) => config.value_filter)

return {
// model & model state
list: [],
count: null,
fully_loaded: false,
xhr: null,

// ambient
ambient: null,
last_ambient: null,
ambient_changed() {
return this.ambient != this.last_ambient
},
reset() {
// assemble queries for api.request
let ambient_queries = [
...(_configs.selects ? [{label: 'select', value: Array.from(_configs.selects, ([label, config]) => config.alias ? config.alias + ':' + label : label).join(',')}] : []),
...(_configs.queries ? _configs.queries.map(query => ({label: query.label, op: query.op, value: typeof query.value == 'function' ? query.value() : query.value})) : []),
...(_configs.order ? [{label: 'order', value: _configs.order.map(o => [o.label, o.direction, o.nulls ? 'nulls'+o.nulls : ''].filter(a => a).join('.')).join(',')}] : [])
]

this.last_ambient = this.ambient
this.ambient =JSON.stringify(ambient_queries)

if (this.ambient_changed()) {
this.list = []
this.count = null
this.fully_loaded = false
this.xhr = null
}

return ambient_queries
},

// load data
load(args) {
args = args || {begin: 0}

let ambient_queries = this.reset()

// be lazy: if the data is presented, return the value immediately
if (this.list.slice(args.begin, args.end).length == args.limit && this.list.slice(args.begin, args.end).every(data => data != undefined))
return Promise.resolve(this.list.slice(args.begin, args.end))

// create limit&offset for postgrest
let paging_queries = []
if (args.begin != undefined) {
paging_queries.push({label: 'offset', value: args.begin})
if (args.end != undefined)
paging_queries.push({label: 'limit', value: args.end - args.begin})
}

// now the hard work
},

// load full model if privileged
load_full() {
this.reset()

if (this.fully_loaded)
return Promise.resolve(this.list)

return api.request({
method: 'POST',
url: '/rpc/query_all',
body: {
endpoint: _configs.endpoint,
selects: Array.from(_configs.selects, ([label, config]) => ({label: label, alias: config.alias})),
queries: _configs.queries ? _configs.queries.map(query => ({label: query.label, op: query.op, value: typeof query.value == 'function' ? query.value() : query.value})) : [],
order: _configs.order
}
}).then(response => {
this.list = response.map(data => {
_value_filters.forEach(([label, config]) => data[config.alias || label] = config.value_filter(data[config.alias || label]))
return data
})
this.fully_loaded = true

return this.list
})
}
}
}

Laddar…
Avbryt
Spara