From 0757cefac10cc6b5d0f2186b24b2dccd9f733a40 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Sat, 8 Feb 2020 23:09:18 -0600 Subject: [PATCH] finish database implementation --- .../api/v1/FormDatabase.controller.js | 214 ++++++++++++++++++ app/controllers/api/v1/Page.controller.js | 5 - app/models/api/db/ColumnDef.model.js | 24 ++ app/models/api/db/DBEntry.model.js | 22 ++ app/models/api/db/Database.model.js | 28 +++ .../api/GuaranteeRootNode.middleware.js | 2 + app/routing/routers/api/v1.routes.js | 8 + 7 files changed, 298 insertions(+), 5 deletions(-) create mode 100644 app/controllers/api/v1/FormDatabase.controller.js create mode 100644 app/models/api/db/ColumnDef.model.js create mode 100644 app/models/api/db/DBEntry.model.js create mode 100644 app/models/api/db/Database.model.js diff --git a/app/controllers/api/v1/FormDatabase.controller.js b/app/controllers/api/v1/FormDatabase.controller.js new file mode 100644 index 0000000..e383019 --- /dev/null +++ b/app/controllers/api/v1/FormDatabase.controller.js @@ -0,0 +1,214 @@ +const Controller = require('libflitter/controller/Controller') +const Page = require('../../../models/api/Page.model') +const Node = require('../../../models/api/Node.model') +const Database = require('../../../models/api/db/Database.model') +const ColumnDef = require('../../../models/api/db/ColumnDef.model') +const DBEntry = require('../../../models/api/db/DBEntry.model') + +/* + * Database Controller + * ------------------------------------------------------------- + * Put some description here! + */ +class FormDatabase extends Controller { + + async create_new(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const db = new Database({ + Name: req.body.name ? req.body.name : 'New Database', + NodeId: node.UUID, + PageId: page.UUID, + }) + + await db.save() + + req.user.allow(`database:${db.UUID}`); + await req.user.save() + + return res.api(db) + } + + async get_config(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const DatabaseId = req.params.DatabaseId + const db = await Database.findOne({UUID: DatabaseId}) + if ( !db ) return res.status(404).message('Database not found with that ID.').api({}) + if ( !db.accessible_by(req.user) ) return req.security.deny() + + return res.api(db) + } + + async get_columns(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const DatabaseId = req.params.DatabaseId + const db = await Database.findOne({UUID: DatabaseId}) + if ( !db ) return res.status(404).message('Database not found with that ID.').api({}) + if ( !db.accessible_by(req.user) ) return req.security.deny() + + const columns = await ColumnDef.find({ DatabaseId: db.UUID }) + return res.api(columns) + } + + async set_columns(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const DatabaseId = req.params.DatabaseId + const db = await Database.findOne({UUID: DatabaseId}) + if ( !db ) return res.status(404).message('Database not found with that ID.').api({}) + if ( !db.accessible_by(req.user, 'update') ) return req.security.deny() + + const existing_columns = await ColumnDef.find({ DatabaseId: db.UUID }) + const assoc_columns = [] + existing_columns.forEach(col => assoc_columns[col.UUID] = col) + + const update_columns = [] + for ( const col of req.body.columns ) { + if ( col.UUID && assoc_columns[col.UUID] ) { + assoc_columns[col.UUID].headerName = col.headerName + assoc_columns[col.UUID].field = col.field + assoc_columns[col.UUID].DatabaseId = db.UUID + assoc_columns[col.UUID].Type = col.Type + await assoc_columns[col.UUID].save() + update_columns.push(assoc_columns[col.UUID]) + } else { + const new_col = new ColumnDef({ + headerName: col.headerName, + field: col.field, + DatabaseId: db.UUID, + Type: col.Type, + }) + + await new_col.save() + update_columns.push(new_col) + } + } + + for ( const col of existing_columns ) { + if ( !update_columns.includes(col) ) { + await col.delete() + } + } + + return res.api(update_columns) + } + + async get_data(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const DatabaseId = req.params.DatabaseId + const db = await Database.findOne({UUID: DatabaseId}) + if ( !db ) return res.status(404).message('Database not found with that ID.').api({}) + if ( !db.accessible_by(req.user) ) return req.security.deny() + + const entries = await DBEntry.find({DatabaseId: db.UUID}) + entries.forEach(entry => entry.RowData.UUID = entry.UUID) + + return res.api(entries) + } + + async set_data(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const DatabaseId = req.params.DatabaseId + const db = await Database.findOne({UUID: DatabaseId}) + if ( !db ) return res.status(404).message('Database not found with that ID.').api({}) + if ( !db.accessible_by(req.user) ) return req.security.deny() + + await DBEntry.deleteMany({DatabaseId: db.UUID}) + + const new_recs = [] + for ( const rec of req.body ) { + const data = {DatabaseId: db.UUID} + if ( rec.UUID ) data.UUID = rec.UUID + delete rec.UUID + data.RowData = rec + + const dbe = new DBEntry(data) + await dbe.save() + dbe.RowData.UUID = dbe.UUID + new_recs.push(dbe) + } + return res.api(new_recs) + } + + async drop_database(req, res) { + const PageId = req.params.PageId + + let page = await Page.findOne({UUID: PageId}) + if ( !page ) return res.status(404).message('Page not found with that ID.').api({}) + if ( !page.accessible_by(req.user) ) return req.security.deny() + + const NodeId = req.params.NodeId + + let node = await Node.findOne({UUID: NodeId}) + if ( !node ) return res.status(404).message('Node not found with that ID.').api({}) + + const DatabaseId = req.params.DatabaseId + const db = await Database.findOne({UUID: DatabaseId}) + if ( !db ) return res.status(404).message('Database not found with that ID.').api({}) + if ( !db.accessible_by(req.user) ) return req.security.deny() + + await DBEntry.deleteMany({DatabaseId: db.UUID}) + await db.delete() + } + +} + +module.exports = exports = FormDatabase diff --git a/app/controllers/api/v1/Page.controller.js b/app/controllers/api/v1/Page.controller.js index 19ebca0..e4a4a3c 100644 --- a/app/controllers/api/v1/Page.controller.js +++ b/app/controllers/api/v1/Page.controller.js @@ -105,8 +105,6 @@ class Page extends Controller { if ( node.UUID && Object.keys(assoc_nodes).includes(node.UUID) ) updated_node_ids.push(node.UUID) } - console.log('req body', req.body) - const updated_nodes = [] for ( let node of req.body ) { if ( node.UUID && assoc_nodes[node.UUID] ) { @@ -142,10 +140,7 @@ class Page extends Controller { await node.delete() } - console.log('updated nodes', updated_nodes) - page.NodeIds = updated_nodes.map(x => x.UUID) - console.log({page}) await page.save() res.api(updated_nodes) diff --git a/app/models/api/db/ColumnDef.model.js b/app/models/api/db/ColumnDef.model.js new file mode 100644 index 0000000..89ae54f --- /dev/null +++ b/app/models/api/db/ColumnDef.model.js @@ -0,0 +1,24 @@ +const Model = require('flitter-orm/src/model/Model') +const uuid = require('uuid/v4') + +/* + * ColumnDef Model + * ------------------------------------------------------------- + * Put some description here! + */ +class ColumnDef extends Model { + static get schema() { + // Return a flitter-orm schema here. + return { + headerName: String, + field: String, + DatabaseId: String, + UUID: { type: String, default: () => uuid() }, + Type: { type: String, default: 'text' }, // text, number for now + } + } + + // Static and instance methods can go here +} + +module.exports = exports = ColumnDef diff --git a/app/models/api/db/DBEntry.model.js b/app/models/api/db/DBEntry.model.js new file mode 100644 index 0000000..2397e71 --- /dev/null +++ b/app/models/api/db/DBEntry.model.js @@ -0,0 +1,22 @@ +const Model = require('flitter-orm/src/model/Model') +const uuid = require('uuid/v4') + +/* + * DBEntry Model + * ------------------------------------------------------------- + * Put some description here! + */ +class DBEntry extends Model { + static get schema() { + // Return a flitter-orm schema here. + return { + DatabaseId: String, + RowData: Object, + UUID: { type: String, default: () => uuid() }, + } + } + + // Static and instance methods can go here +} + +module.exports = exports = DBEntry diff --git a/app/models/api/db/Database.model.js b/app/models/api/db/Database.model.js new file mode 100644 index 0000000..ff56dc7 --- /dev/null +++ b/app/models/api/db/Database.model.js @@ -0,0 +1,28 @@ +const Model = require('flitter-orm/src/model/Model') +const uuid = require('uuid/v4') + +/* + * Database Model + * ------------------------------------------------------------- + * Put some description here! + */ +class Database extends Model { + static get schema() { + // Return a flitter-orm schema here. + return { + Name: String, + NodeId: String, + PageId: String, + ColumnIds: [String], + UUID: { type: String, default: () => uuid() }, + } + } + + accessible_by(user, mode = 'view') { + return user.can(`database:${this.UUID}:${mode}`) + } + + // Static and instance methods can go here +} + +module.exports = exports = Database diff --git a/app/routing/middleware/api/GuaranteeRootNode.middleware.js b/app/routing/middleware/api/GuaranteeRootNode.middleware.js index 76a2424..c90e46b 100644 --- a/app/routing/middleware/api/GuaranteeRootNode.middleware.js +++ b/app/routing/middleware/api/GuaranteeRootNode.middleware.js @@ -30,6 +30,8 @@ class GuaranteeRootNode extends Middleware { }) await new_page.save() + req.user.allow(`page:${new_page.UUID}`) + await req.user.save() } } diff --git a/app/routing/routers/api/v1.routes.js b/app/routing/routers/api/v1.routes.js index aff538a..d6ee80f 100644 --- a/app/routing/routers/api/v1.routes.js +++ b/app/routing/routers/api/v1.routes.js @@ -41,6 +41,9 @@ const index = { '/page/:PageId/nodes': ['controller::api:v1:Page.get_nodes'], '/menu/items': ['controller::api:v1:Menu.get_items'], + '/db/:PageId/:NodeId/get/:DatabaseId': ['controller::api:v1:FormDatabase.get_config'], + '/db/:PageId/:NodeId/get/:DatabaseId/columns': [ 'controller::api:v1:FormDatabase.get_columns' ], + '/db/:PageId/:NodeId/get/:DatabaseId/data': [ 'controller::api:v1:FormDatabase.get_data' ], }, post: { @@ -49,6 +52,11 @@ const index = { '/page/create': ['controller::api:v1:Page.create_top_level'], '/page/create-child': ['controller::api:v1:Page.create_child'], '/page/delete/:PageId': ['controller::api:v1:Page.delete_page'], + + '/db/:PageId/:NodeId/create': ['controller::api:v1:FormDatabase.create_new'], + '/db/:PageId/:NodeId/set/:DatabaseId/columns': [ 'controller::api:v1:FormDatabase.set_columns' ], + '/db/:PageId/:NodeId/drop/:DatabaseId': [ 'controller::api:v1:FormDatabase.drop_database' ], + '/db/:PageId/:NodeId/set/:DatabaseId/data': ['controller::api:v1:FormDatabase.set_data'], }, }