Skip to content

Commit

Permalink
Merge pull request #977 from dbauszus-glx/template-caching
Browse files Browse the repository at this point in the history
Template caching
RobAndrewHurst authored Nov 9, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents cf5ded3 + aaee50c commit adecc64
Showing 45 changed files with 1,001 additions and 1,121 deletions.
48 changes: 10 additions & 38 deletions api/api.js
Original file line number Diff line number Diff line change
@@ -8,10 +8,8 @@ const auth = require('../mod/user/auth')

const saml = process.env.SAML_ENTITY_ID && require('../mod/user/saml')

const workspaceCache = require('../mod/workspace/cache')

const routes = {
layer: require('../mod/layer/_layer'),
mvt: require('../mod/mvt'),
location: require('../mod/location/_location'),
provider: require('../mod/provider/_provider'),
query: require('../mod/query'),
@@ -107,9 +105,10 @@ module.exports = async (req, res) => {
}

// Language param will default to english [en] is not explicitly set.
req.params.language = req.params.language || 'en'
req.params.language ??= 'en'

req.params.template = req.params._template || req.params.template
// Assign from _template if provided as path param.
req.params.template ??= req.params._template

// Short circuit login view or post request.
if (req.params.login || req.body && req.body.login) return login(req, res)
@@ -124,7 +123,7 @@ module.exports = async (req, res) => {
res.setHeader('Set-Cookie', `${process.env.TITLE}=null;HttpOnly;Max-Age=0;Path=${process.env.DIR || '/'}`)

// Remove logout parameter.
res.setHeader('location', `${process.env.DIR || '/'}`)
res.setHeader('location', (process.env.DIR || '/') + (req.params.msg && `?msg=${req.params.msg}`||''))

return res.status(302).send()
}
@@ -141,8 +140,10 @@ module.exports = async (req, res) => {
// Remove cookie.
res.setHeader('Set-Cookie', `${process.env.TITLE}=null;HttpOnly;Max-Age=0;Path=${process.env.DIR || '/'};SameSite=Strict${!req.headers.host.includes('localhost') && ';Secure' || ''}`)

req.params.msg = user.msg

// Return login view with error message.
return login(req, res, user.msg)
return login(req, res)
}

// Set user as request parameter.
@@ -157,11 +158,7 @@ module.exports = async (req, res) => {
if (req.url.match(/(?<=\/api\/user)/)) {

// A msg will be returned if the user does not met the required priviliges.
const msg = routes.user(req, res)

// Return the login view with the msg.
msg && login(req, res, msg)
return
return routes.user(req, res)
}

// The login view will be returned for all PRIVATE requests without a valid user.
@@ -175,34 +172,9 @@ module.exports = async (req, res) => {
return login(req, res)
}

// Retrieve workspace and assign to request params.
const workspace = await workspaceCache()

if (workspace instanceof Error) {
return res.status(500).send(workspace.message)
}

req.params.workspace = workspace

// Retrieve query or view template from workspace
if (req.params.template) {

const template = workspace.templates[req.params.template]

if (!template) return res.status(404).send('Template not found.')

if (template.err) return res.status(500).send(template.err.message)

if (!user && (template.login || template.admin)) return login(req, res, 'login_required')

if (user && (!user.admin && template.admin)) return login(req, res, 'admin_required')

req.params.template = template
}

// Layer route
if (req.url.match(/(?<=\/api\/layer)/)) {
return routes.layer(req, res)
return routes.mvt(req, res)
}

// Location route
39 changes: 23 additions & 16 deletions lib/utils/xhr.mjs
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
const requestMap = new Map()

export default params => new Promise((resolve, reject) => {
export default params => new Promise(resolve => {

if (!params) return reject()
// Return if params are falsy.
if (!params) {
console.error(`xhr params are falsy.`)
return;
}

// Set params as object with url from string.
params = typeof params === 'string' ? { url: params } : params

// A request url must be provided.
if (!params.url) {
console.error(`no xhr request url has been provided.`)
return;
};

// Check whether a request with the same params has already been resolved.
if (params.cache && requestMap.has(params)) return resolve(requestMap.get(params))

// Assign 'GET' as default method.
params.method ??= 'GET'

const xhr = new XMLHttpRequest()

xhr.open(params.method || 'GET', params.url)
xhr.open(params.method, params.url)

// Use requestHeader: null to prevent assignment of requestHeader.
if (params.requestHeader !== null) {

// Butter (spread) over requestHeader.
const requestHeader = {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
...params.requestHeader
}

Object.assign(requestHeader, params.requestHeader || {})

Object.entries(requestHeader).forEach(entry=>xhr.setRequestHeader(...entry))

Object.entries(requestHeader).forEach(entry => xhr.setRequestHeader(...entry))
}

xhr.responseType = params.responseType || 'json'

xhr.onload = e => {

if (e.target.status >= 400) {
reject(new Error(e.target.status))
resolve(new Error(e.target.status))
return;
}

// Cache the response in the requestMap
params.cache && requestMap.set(params, e.target.response)

resolve(params.resolveTarget ? e.target : e.target.response)

}

// xhr.onerror = err => {
// console.error(err)
// reject(err);
// };

xhr.send(params.body)

})
46 changes: 0 additions & 46 deletions mod/layer/_layer.js

This file was deleted.

31 changes: 14 additions & 17 deletions mod/location/_location.js
Original file line number Diff line number Diff line change
@@ -5,30 +5,27 @@ const methods = {
delete: require('./delete'),
}

const Roles = require('../utils/roles')

const getLayer = require('../workspace/getLayer')

module.exports = async (req, res) => {

if (!Object.hasOwn(methods, req.params.method)) {
return res.send(`Failed to evaluate 'method' param.<br><br>
<a href="https://geolytix.github.io/xyz/docs/develop/api/location/">Location API</a>`)
return res.send(`Failed to evaluate 'method' param.`)
}

const method = methods[req.params.method]

if (typeof method !== 'function') return;

const locale = req.params.locale && req.params.workspace.locales[req.params.locale]

const layer = locale && locale.layers[req.params.layer] || req.params.workspace.templates[req.params.layer]
const layer = await getLayer(req.params)

if (!layer) return res.status(400).send('Layer not found.')

req.params.layer = layer
if (layer instanceof Error) {
return res.status(400).send('Failed to access layer.')
}

if (!req.params.layer) {
return res.status(400).send(`Failed to evaluate 'layer' param.<br><br>
<a href="https://geolytix.github.io/xyz/docs/develop/api/location/">Location API</a>`)
if (!Roles.check(layer, req.params.user?.roles)) {
return res.status(403).send('Role access denied for layer.')
}

return method(req, res)

req.params.layer = layer

return methods[req.params.method](req, res)
}
8 changes: 6 additions & 2 deletions mod/location/delete.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
const dbs = require('../utils/dbs')()

const workspaceCache = require('../workspace/cache')

module.exports = async (req, res) => {

const workspace = await workspaceCache()

const layer = req.params.layer

// Validate dynamic method call.
if (typeof dbs[layer.dbs || req.params.workspace.dbs] !== 'function') return;
if (typeof dbs[layer.dbs || workspace.dbs] !== 'function') return;

var rows = await dbs[layer.dbs || req.params.workspace.dbs](`
let rows = await dbs[layer.dbs || workspace.dbs](`
DELETE FROM ${req.params.table} WHERE ${layer.qID} = $1;`, [req.params.id])

if (rows instanceof Error) return res.status(500).send('PostgreSQL query error - please check backend logs.')
12 changes: 8 additions & 4 deletions mod/location/get.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const dbs = require('../utils/dbs')()

const sqlFilter = require('../utils/sqlFilter.js')
const sqlFilter = require('../utils/sqlFilter')

const Roles = require('../utils/roles.js')
const Roles = require('../utils/roles')

const workspaceCache = require('../workspace/cache')

module.exports = async (req, res) => {

const workspace = await workspaceCache()

// Check the layer.roles{} against the user.roles[]
const layer = Roles.check(req.params.layer, req.params.user?.roles)

@@ -38,9 +42,9 @@ module.exports = async (req, res) => {
${roleFilter}`

// Validate dynamic method call.
if (!Object.hasOwn(dbs, layer.dbs || req.params.workspace.dbs) || typeof dbs[layer.dbs || req.params.workspace.dbs] !== 'function') return;
if (!Object.hasOwn(dbs, layer.dbs || workspace.dbs) || typeof dbs[layer.dbs || workspace.dbs] !== 'function') return;

const rows = await dbs[layer.dbs || req.params.workspace.dbs](q, SQLparams)
const rows = await dbs[layer.dbs || workspace.dbs](q, SQLparams)

if (rows instanceof Error) return res.status(500).send('Failed to query PostGIS table.')

8 changes: 6 additions & 2 deletions mod/location/new.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
const dbs = require('../utils/dbs')()

const workspaceCache = require('../workspace/cache')

module.exports = async (req, res) => {

const workspace = await workspaceCache()

const layer = req.params.layer

// vals for variable substitution in query
@@ -39,9 +43,9 @@ module.exports = async (req, res) => {
RETURNING ${layer.qID} AS id;`

// Validate dynamic method call.
if (!Object.hasOwn(dbs, layer.dbs || req.params.workspace.dbs) || typeof dbs[layer.dbs || req.params.workspace.dbs] !== 'function') return;
if (!Object.hasOwn(dbs, layer.dbs || workspace.dbs) || typeof dbs[layer.dbs || workspace.dbs] !== 'function') return;

var rows = await dbs[layer.dbs || req.params.workspace.dbs](q, vals)
let rows = await dbs[layer.dbs || workspace.dbs](q, vals)

if (rows instanceof Error) return res.status(500).send('Failed to query PostGIS table.')

8 changes: 6 additions & 2 deletions mod/location/update.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
const dbs = require('../utils/dbs')()

const workspaceCache = require('../workspace/cache')

module.exports = async (req, res) => {

const workspace = await workspaceCache()

const layer = req.params.layer

const fields = Object.entries(req.body)
@@ -45,11 +49,11 @@ module.exports = async (req, res) => {
})

// Validate dynamic method call.
if (!Object.hasOwn(dbs, layer.dbs || req.params.workspace.dbs) || typeof dbs[layer.dbs || req.params.workspace.dbs] !== 'function') return;
if (!Object.hasOwn(dbs, layer.dbs || workspace.dbs) || typeof dbs[layer.dbs || workspace.dbs] !== 'function') return;

var q = `UPDATE ${req.params.table} SET ${fields.join()} WHERE ${layer.qID} = $1;`

var rows = await dbs[layer.dbs || req.params.workspace.dbs](q, [req.params.id])
let rows = await dbs[layer.dbs || workspace.dbs](q, [req.params.id])

if (rows instanceof Error) return res.status(500).send('PostgreSQL query error - please check backend logs.')

Loading

0 comments on commit adecc64

Please sign in to comment.