houdini/app/javascript/legacy/common/restful_resource.js
2020-04-23 14:09:14 -05:00

139 lines
4 KiB
JavaScript

// License: LGPL-3.0-or-later
/* A simple module for dealing with ajax-based resources in viewscript
*
*
* Define a 'resource object' in appl that has these properties
* resource_name: 'donations' (plural name that matches the model)
* path_prefix: '/' (optional, defaults to empty string, or relative path)
* query: object of parameters to use for indexing (eg search queries)
* after_action: function callback run after the request (where action is fetch, index, etc)
* after_action_failure: callback for failed requests (where action is fetch, index, etc)
*
* Call the ajax functions like this:
* in js:
* appl.ajax.index(appl.resource_object)
* appl.ajax.create(appl.donations, {amount: 420})
* in viewscript in the dom:
* ajax.index resource_object
* ajax.create donations form_object
*/
var request = require('../common/client')
var restful_resource = {}
module.exports = restful_resource
appl.def('ajax', {
index: function(prop, node) {
var resource = appl.vs(prop) || {}
var name = resource.resource_name || prop
var path = resource.path_prefix || ''
before_request(prop)
return new Promise(function(resolve, reject) {
request.get(path + name).query(resource.query)
.end(function(err, resp) {
var tmp = resource.data
after_request(prop, err, resp)
if(resp.ok) {
if(resource.query && resource.query.page > 1 && resource.concat_data) {
appl.def(prop + '.data', tmp.concat(resp.body.data))
}
resolve(resp)
} else {
reject(resp)
}
})
})
},
fetch: function(prop, id, node) {
var resource = appl.vs(prop) || {}
var name = resource.resource_name || prop
var path = resource.path_prefix || ''
before_request(prop)
return new Promise(function(resolve, reject) {
request.get(path + name + '/' + id).query(resource.query)
.end(function(err, resp) {
after_request(prop, err, resp)
if(resp.ok) resolve(resp)
else reject(resp)
})
})
},
create: function(prop, form_obj, node) {
var resource = appl.vs(prop) || {}
var name = resource.resource_name || prop
var path = resource.path_prefix || ''
before_request(prop)
return new Promise(function(resolve, reject) {
request.post(path + name).send(nested_obj(name, form_obj))
.end(function(err, resp) {
after_request(prop, err, resp)
if(resp.ok) resolve(resp)
else reject(resp)
})
})
},
update: function(prop, id, form_obj, node) {
var resource = appl.vs(prop) || {}
var name = resource.resource_name || prop
var path = resource.path_prefix || ''
before_request(prop)
return new Promise(function(resolve, reject) {
request.put(path + name + '/' + id).send(nested_obj(name, form_obj))
.end(function(err, resp) {
after_request(prop, err, resp)
if(resp.ok) resolve(resp)
else reject(resp)
})
})
},
del: function(prop, id, node) {
var resource = appl.vs(prop) || {}
var path = (resource.path_prefix || '') + (resource.resource_name || prop)
before_request(prop)
return new Promise(function(resolve, reject) {
request.del(path + '/' + id)
.end(function(err, resp) {
after_request(prop, err, resp)
if(resp.ok) resolve(resp)
else reject(resp)
})
})
}
})
// Given a viewscript property, set some state before every request.
// Eg. appl.ajax.index('donations') will cause appl.donations.loading to be
// true before the request finishes
function before_request(prop) {
appl.def(prop + '.loading', true)
appl.def(prop + '.error', '')
}
// Set some data after each request.
function after_request(prop, err, resp) {
appl.def(prop + '.loading', false)
if(resp.ok) {
appl.def(prop, resp.body)
} else {
appl.def(prop + '.error', resp.body)
}
}
// Simply return an object nested under 'name'
// Will singularize the given name if plural
// eg: given 'donations' and {amount: 111}, return {donation: {amount: 111}}
function nested_obj(name, child_obj) {
var parent_obj = {}
parent_obj[appl.to_singular(name)] = child_obj
return parent_obj
}