From 1280d6ee1ff5babd0257e4cc3c0e7ea9cbd03065 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Wed, 27 May 2020 21:26:35 +0200 Subject: [PATCH] - Added getAll route for spells with bookshelf --- database/auracle_db_create.sql | 40 +++++------ database/auracle_db_data.sql | 6 +- database/connection.js | 16 ++++- models/{School.js => SchoolValidation.js} | 0 models/Spell.js | 39 ----------- models/SpellValidation.js | 39 +++++++++++ models/{User.js => UserValidation.js} | 0 routes/schools.js | 2 +- routes/spells.js | 84 ++++++++++++++++------- routes/users.js | 2 +- 10 files changed, 139 insertions(+), 89 deletions(-) rename models/{School.js => SchoolValidation.js} (100%) create mode 100644 models/SpellValidation.js rename models/{User.js => UserValidation.js} (100%) diff --git a/database/auracle_db_create.sql b/database/auracle_db_create.sql index f36cf18..c07b3c1 100644 --- a/database/auracle_db_create.sql +++ b/database/auracle_db_create.sql @@ -29,9 +29,9 @@ CREATE TABLE IF NOT EXISTS `school` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école", `description` VARCHAR(255) DEFAULT "Description de l'école", - `id_meta_school` INT UNSIGNED NOT NULL, + `meta_school_id` INT UNSIGNED NOT NULL, PRIMARY KEY (`id`), - FOREIGN KEY(`id_meta_school`) REFERENCES meta_school(`id`) + FOREIGN KEY(`meta_school_id`) REFERENCES meta_school(`id`) ); /* COMMON INGREDIENTS */ @@ -63,30 +63,30 @@ CREATE TABLE IF NOT EXISTS `user` ( /* SPELLS' SCHOOLS */ /* One spell can have multiple (up to 3) schools */ -CREATE TABLE IF NOT EXISTS `spells_schools` ( - `id_spell` INT UNSIGNED NOT NULL, - `id_school` INT UNSIGNED NOT NULL, - PRIMARY KEY (`id_spell`, `id_school`), - FOREIGN KEY(`id_spell`) REFERENCES spell(`id`), - FOREIGN KEY(`id_school`) REFERENCES school(`id`) +CREATE TABLE IF NOT EXISTS `spell_school` ( + `spell_id` INT UNSIGNED NOT NULL, + `school_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `school_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`school_id`) REFERENCES school(`id`) ); /* SPELLS' VARIABLES */ /* One spell can have multiple (up to 2) variables of cost */ -CREATE TABLE IF NOT EXISTS `spells_variables` ( - `id_spell` INT UNSIGNED NOT NULL, - `id_variable` INT UNSIGNED NOT NULL, - PRIMARY KEY (`id_spell`, `id_variable`), - FOREIGN KEY(`id_spell`) REFERENCES spell(`id`), - FOREIGN KEY(`id_variable`) REFERENCES variable(`id`) +CREATE TABLE IF NOT EXISTS `spell_variable` ( + `spell_id` INT UNSIGNED NOT NULL, + `variable_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `variable_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`variable_id`) REFERENCES variable(`id`) ); /* SPELLS' VARIABLES */ /* One spell can have multiple ingredients */ -CREATE TABLE IF NOT EXISTS `spells_ingredients` ( - `id_spell` INT UNSIGNED NOT NULL, - `id_ingredient` INT UNSIGNED NOT NULL, - PRIMARY KEY (`id_spell`, `id_ingredient`), - FOREIGN KEY(`id_spell`) REFERENCES spell(`id`), - FOREIGN KEY(`id_ingredient`) REFERENCES ingredient(`id`) +CREATE TABLE IF NOT EXISTS `spell_ingredient` ( + `spell_id` INT UNSIGNED NOT NULL, + `ingredient_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `ingredient_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`ingredient_id`) REFERENCES ingredient(`id`) ); diff --git a/database/auracle_db_data.sql b/database/auracle_db_data.sql index 350946c..e0a2fb3 100644 --- a/database/auracle_db_data.sql +++ b/database/auracle_db_data.sql @@ -12,7 +12,7 @@ INSERT INTO `meta_school` (name, description) VALUES ('Magies autres', 'Magies trop spécifiques et ne rentrant dans aucune autre grande école.'); -- SCHOOLS -INSERT INTO `school` (name, description, id_meta_school) VALUES +INSERT INTO `school` (name, description, meta_school_id) VALUES ('Lumomancie', 'Discipline arcanique de la lumière.', 1), ('Vitamancie', 'Discipline arcanique de la guérison et de l\'énergie vitale.', 1), ('Obstrumancie', 'Discipline arcanique de la protection et des sceaux.', 1), @@ -223,11 +223,11 @@ SCHEMA */ DELIMITER $$ -CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN school_id INT) +CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN id_school INT) BEGIN SET @i = delimiter_start; WHILE @i <= delimiter_end DO - INSERT INTO spells_schools (id_spell, id_school) VALUES (@i, school_id); + INSERT INTO spell_school (spell_id, school_id) VALUES (@i, id_school); SET @i = @i + 1; END WHILE; END$$ diff --git a/database/connection.js b/database/connection.js index 3084136..d890da9 100644 --- a/database/connection.js +++ b/database/connection.js @@ -6,6 +6,20 @@ const mysql = require('mysql') let credentials_data = fs.readFileSync('./database/credentials.json') let credentials = JSON.parse(credentials_data) +// Setting up the database connection +const knex = require('knex')({ + client: credentials.client, + connection: { + host : credentials.host, + user : credentials.user, + password : credentials.password, + database : credentials.database, + charset : credentials.charset + }, + debug: true +}) +const bookshelf = require('bookshelf')(knex) + const db = mysql.createConnection({ host: credentials.host, user: credentials.user, @@ -19,4 +33,4 @@ db.on('error', err => { } }) -module.exports = { db } \ No newline at end of file +module.exports = { db, bookshelf } \ No newline at end of file diff --git a/models/School.js b/models/SchoolValidation.js similarity index 100% rename from models/School.js rename to models/SchoolValidation.js diff --git a/models/Spell.js b/models/Spell.js index 38284fb..e69de29 100644 --- a/models/Spell.js +++ b/models/Spell.js @@ -1,39 +0,0 @@ -const Spell = { - "id": "/SpellModel", - "type": Object, - "properties": { - "name": { "type": "string" }, - "description": { "type": "string" }, - "level": { "type": "number" }, - "charge": { "type": "number" }, - "cost": { "type": "string" }, - "is_ritual": { "type": "boolean" }, - "schools": { - "type": { "type": "array" }, - "items": { - "properties": { - "id": { "type": "number" }, - } - } - }, - "variables": { - "type": { "type": "array" }, - "items": { - "properties": { - "id": { "type": "number" }, - } - } - }, - "ingredients": { - "type": { "type": "array" }, - "items": { - "properties": { - "id": { "type": "number" }, - } - } - } - }, - "required": ["name", "description"] -} - -module.exports = Spell \ No newline at end of file diff --git a/models/SpellValidation.js b/models/SpellValidation.js new file mode 100644 index 0000000..da0167e --- /dev/null +++ b/models/SpellValidation.js @@ -0,0 +1,39 @@ +const SpellModel = { + "id": "/SpellModel", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "level": { "type": "number" }, + "charge": { "type": "number" }, + "cost": { "type": "string" }, + "is_ritual": { "type": "boolean" }, + "schools": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + }, + "variables": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + }, + "ingredients": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + } + }, + "required": ["name", "description"] +} + +module.exports = SpellModel \ No newline at end of file diff --git a/models/User.js b/models/UserValidation.js similarity index 100% rename from models/User.js rename to models/UserValidation.js diff --git a/routes/schools.js b/routes/schools.js index cb5b825..daceed1 100644 --- a/routes/schools.js +++ b/routes/schools.js @@ -15,7 +15,7 @@ const regexXSS = RegExp(/<[^>]*script/) // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const School = require("../models/School") +const School = require("../models/SchoolValidation") v.addSchema(School, "/SchoolModel") // Error handling diff --git a/routes/spells.js b/routes/spells.js index d208043..8c31e98 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -12,11 +12,58 @@ const db = connection.db const regexInt = RegExp(/^[1-9]\d*$/) const regexXSS = RegExp(/<[^>]*script/) +// Bookshelf Models +const bookshelf = require('../database/connection').bookshelf + +const Spell = bookshelf.Model.extend({ + tableName: 'spell', + schools() { + return this.belongsToMany( School, 'spell_school', 'spell_id', 'school_id' ) + }, + variables() { + return this.belongsToMany( Variable, 'spell_variable', 'spell_id', 'variable_id' ) + }, + ingredients() { + return this.belongsToMany( Ingredient, 'spell_ingredient', 'spell_id', 'ingredient_id' ) + } +}) + +const MetaSchool = bookshelf.Model.extend({ + tableName: 'meta_school', + schools() { + return this.hasMany( School ) + } +}) + +const School = bookshelf.Model.extend({ + tableName: 'school', + spells() { + return this.belongsToMany( Spell, 'spell_school', 'school_id', 'spell_id') + }, + meta_school() { + return this.belongsTo( MetaSchool, 'meta_school_id') + } +}) + +const Ingredient = bookshelf.Model.extend({ + tableName: 'ingredient', + spells() { + return this.belongsToMany( Spell, 'spell_ingredient', 'ingredient_id', 'spell_id') + } +}) + +const Variable = bookshelf.Model.extend({ + tableName: 'variable', + spells() { + return this.belongsToMany( Spell, 'spell_variable', 'variable_id', 'spell_id') + } +}) + // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const Spell = require("../models/Spell") -v.addSchema(Spell, "/SpellModel") +const SpellModel = require("../models/SpellValidation") +v.addSchema(SpellModel, "/SpellModel") // Error handling const { HttpError } = require('../models/Errors') @@ -26,25 +73,14 @@ const { HttpError } = require('../models/Errors') const getSpells = () => { return new Promise((resolve, reject) => { - let query = "SELECT DISTINCT * FROM spell" - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else if (result.length == 0) { - reject(new HttpError(404, 'No spells were found')) - } - - // Loops over the results to fetch the associated tables - for (let i = 0; i < result.length; i++) { - try { - result[i] = await buildSpell(result[i]) - } catch (err) { - reject(err) - } - } - resolve(result) + Spell.forge().fetchAll({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) }) + .catch(err => { + reject(new HttpError(500, err.message)) + }) + }) .catch(err => { throw err @@ -113,8 +149,8 @@ const addSpell = (s) => { // Checks if body exists and if the model fits, and throws errors if it doesn't if (isEmptyObject(s)) { reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, Spell).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, Spell).errors)) + } else if (!v.validate(s, SpellModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { @@ -271,8 +307,8 @@ const updateSpell = (s, id) => { // Checks if body exists and if the model fits, and throws errors if it doesn't if (isEmptyObject(s)) { reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, Spell).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, Spell).errors)) + } else if (!v.validate(s, SpellModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { diff --git a/routes/users.js b/routes/users.js index e62360a..793526b 100644 --- a/routes/users.js +++ b/routes/users.js @@ -16,7 +16,7 @@ const regexXSS = RegExp(/<[^>]*script/) // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const User = require("../models/User") +const User = require("../models/UserValidation") v.addSchema(User, "/UserModel") // Error handling