Massive cleanup

Functionnal basic API calls for spells
Cleaned up some functions
Moved some files around
This commit is contained in:
Alexis
2021-07-30 12:24:11 +02:00
parent a15a6614e7
commit 4cc7c5533e
21 changed files with 334 additions and 148 deletions

View File

@@ -1,74 +1,111 @@
import express, { Application } from 'express'
import { Sequelize } from 'sequelize'
// import ControllerBase from './class/controller.base'
import { AuracleApiRouter } from './AuracleApiRouter'
export class AuracleApi {
public app: Application
public port: number
public base_url = '/api/v1/'
public version = 'v1'
public baseUrl = `/api/${this.version}/`
private database?: Sequelize;
constructor(init: {
port: any
middlewares: any
// controllers: Array<ControllerBase>
modules?: Array<any>
database?: Sequelize
}) {
constructor(
init: {
port: any
database: Sequelize
routers?: Array<AuracleApiRouter>
modules?: Array<any>
middlewares?: Array<any>
}
) {
this.app = express()
this.port = Number(init.port)
this.database = init.database
if (init.modules) {
this.modules(init.modules)
}
if (init.middlewares) {
this.middlewares(init.middlewares)
}
// if (init.controllers) {
// this.controller(init.controllers)
// }
// Making sure the DB is up before init routers, modules, etc...
if (init.database) {
this.db_connect(init.database)
.then(() => {
// Sets up tasks to execute...
const tasks = [
this.modules(init.modules),
this.routers(init.routers),
this.middlewares(init.middlewares)
]
Promise.all(tasks)
.catch((err) => {
throw err
})
})
.catch((err) => {
console.error("App couldn't boot up with the following errors : ")
console.error(err)
})
}
}
private modules = (modules: Array<any>) => {
modules.forEach((module) => {
this.app.use(module)
})
}
// private controller = (controllers: Array<ControllerBase>) => {
// controllers.forEach((controller) => {
// this.app.use(this.base_url + controller.path, controller.router)
// console.log(`${controller.name} chargé`)
// })
// }
private middlewares = (middleWares: {
forEach: (arg0: (middleWare: any) => void) => void;
}) => {
middleWares.forEach((middleWare) => {
this.app.use(middleWare)
})
}
/**
* Links the API with a Sequelize Database Object
* @param db_driver A Sequelize instance
*/
private db_connect = async (db_driver: Sequelize) => {
try {
await db_driver.authenticate();
const connection = await db_driver.authenticate();
console.info('Connection has been established successfully.')
return connection
} catch (err) {
console.error('Unable to connect to the database')
console.error(err)
console.error('Unable to connect to the database.')
throw err
}
}
/**
* Initializes the App with a bunch of modules like helmet, cors, morgan...
* @param modules An array of modules and extensions
*/
private modules = async (modules: Array<any>) => {
try {
modules.forEach((module) => {
this.app.use(module)
})
} catch (err) {
console.error('Unable to initialize modules and extensions.')
throw err
}
}
/**
* Initializes routers
* @param routers An array of AuracleApiRouter children objects
*/
private routers = async (routers: Array<AuracleApiRouter>) => {
try {
routers.forEach((router) => {
this.app.use(`${this.baseUrl}`, router.instance)
})
} catch (err) {
console.error('Unable to initialize routers.')
throw err
}
}
private middlewares = async (middleWares: {
forEach: (arg0: (middleWare: any) => void) => void;
}) => {
try {
middleWares.forEach((middleWare) => {
this.app.use(middleWare)
})
} catch (err) {
console.error('Unable to initialize middlewares.')
throw err
}
}
/**
* Allows the App to run on its given port
*/
public listen = () => {
this.app.listen(this.port, () => {
console.log(`App listening on port ${this.port}`)

View File

@@ -0,0 +1,9 @@
import { AuracleApiRepository } from "./AuracleApiRepository";
export class AuracleApiController {
public repository: AuracleApiRepository
constructor(repository: AuracleApiRepository) {
this.repository = repository
}
}

View File

@@ -0,0 +1,9 @@
import { Model } from "sequelize/types";
export class AuracleApiRepository {
public model: Model<any, any>
constructor(model: Model<any, any>) {
this.model = model
}
}

View File

@@ -0,0 +1,52 @@
import { Response } from "express"
/**
* An enum that contains all the default value for the ApiResponse class
*/
enum AuracleApiMessages {
defaultValid = "La requête a été effectuée avec succès.",
defaultRedirect = "La requête redirige vers une autre URL.",
defaultClientError = "Une erreur inconnue dûe à une erreur client s'est produite",
defaultServerError = "Une erreur serveur inconnue s'est produite, veuillez contactez l'administrateur de l'application."
}
export class AuracleApiResponse {
public status: number
public message?: string
public data?: Object | Array<any>
constructor(status: number, data?: Array<any> | Object, message?: string) {
this.status = status
this.data = data
if (message) {
this.message = message
}
// If a message was not provided, gets one by default
else {
// HTTP Valid ranges
if ((this.status >= 200) && (this.status <= 299)) {
this.message = AuracleApiMessages.defaultValid
}
// HTTP Redirect ranges
else if ((this.status >= 300) && (this.status <= 399)) {
this.message = AuracleApiMessages.defaultRedirect
}
// HTTP Client Error ranges
else if ((this.status >= 400) && (this.status <= 499)) {
this.message = AuracleApiMessages.defaultClientError
}
// HTTP Server Error ranges
else if ((this.status >= 500) && (this.status <= 599)) {
this.message = AuracleApiMessages.defaultServerError
}
}
}
public send(res: Response): Response {
return res.status(this.status).json(this)
}
}

View File

@@ -0,0 +1,17 @@
import { Router, RouterOptions } from "express";
import { AuracleApiController } from "./AuracleApiController";
export abstract class AuracleApiRouter {
public instance: Router
public routerOptions: RouterOptions
public resource: string
public controller: AuracleApiController
constructor(resource: string, controller: AuracleApiController, options?: RouterOptions) {
this.resource = resource
this.routerOptions = options
this.controller = controller
this.instance = Router(this.routerOptions) as Router
}
}

View File

@@ -0,0 +1,54 @@
import express from 'express';
import { AuracleApiController } from "../common/classes/AuracleApiController";
import { AuracleApiResponse } from '../common/classes/AuracleApiResponse';
import { Spell, SpellCreationAttributes } from '../database/models/spells/Spell';
import { SpellRepository } from "../repositories/SpellRepository";
export class SpellController extends AuracleApiController {
public repository: SpellRepository
constructor() {
super(new SpellRepository)
}
public async getAll(req: express.Request, res: express.Response) {
let spells: Spell[]
try {
spells = await this.repository.fetchAll()
res = new AuracleApiResponse(200, spells).send(res)
} catch (err) {
res = new AuracleApiResponse(400).send(res)
}
}
public async getOne(req: express.Request, res: express.Response) {
const uuid = req.params.uuid
let spell: Spell
try {
spell = await this.repository.fetchOne(uuid)
res = new AuracleApiResponse(200, spell).send(res)
} catch (err) {
res = new AuracleApiResponse(400).send(res)
}
}
public async createOne(req: express.Request, res: express.Response) {
const spell = {
name: req.body.name,
description: req.body.description,
level: req.body.level,
charge: req.body.charge,
cost: req.body.cost,
isRitual: req.body.isRitual
} as SpellCreationAttributes
try {
const newSpell = await this.repository.createOne(spell)
res = new AuracleApiResponse(201, newSpell).send(res)
} catch (err) {
console.log(err)
}
}
}

View File

@@ -7,15 +7,6 @@ export let AuracleDatabaseDriver: Sequelize;
// Creates the sequelize instance
AuracleDatabaseDriver = new Sequelize(dbConfig)
/**
* If the environement is production...
*/
if (process.env.NODE_ENV != 'production') {
AuracleDatabaseDriver.sync({
force: true
})
}
/**
* Fetches all Typescript files from a given directory
*
@@ -49,3 +40,12 @@ models.forEach(modelPath => {
const registerModel = require(modelPath).default as Function
registerModel()
})
/**
* If the environement is production, sync the DB
*/
if (process.env.NODE_ENV != 'production') {
AuracleDatabaseDriver.sync({
force: true,
})
}

View File

@@ -20,9 +20,5 @@ export const dbConfig: Options = {
* Sequelize Hooks
* Docs : https://sequelize.org/master/manual/hooks.html
*/
hooks: {
beforeDefine: (columns: any, model: ModelOptions<Model<any, any>>) => {
model.tableName = tablePrefix + model.tableName
}
}
hooks: {}
}

View File

@@ -3,7 +3,6 @@ import { DataTypes, Model, Optional } from "sequelize";
import { AuracleDatabaseDriver } from "../../AuracleDatabaseDriver";
interface IngredientAttributes {
id: number
uuid: string
name: string
description: string
@@ -11,10 +10,9 @@ interface IngredientAttributes {
published: boolean
}
interface IngredientCreationAttributes extends Optional<IngredientAttributes, 'id'> { }
interface IngredientCreationAttributes extends Optional<IngredientAttributes, 'uuid'> { }
export class Ingredient extends Model<IngredientAttributes, IngredientCreationAttributes> implements IngredientAttributes {
public readonly id!: number
public readonly uuid!: string
public name!: string
public description!: string
@@ -28,16 +26,12 @@ export class Ingredient extends Model<IngredientAttributes, IngredientCreationAt
export default () => {
Ingredient.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
defaultValue: DataTypes.UUIDV4
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING,
@@ -55,7 +49,7 @@ export default () => {
}
},
{
tableName: 'ingredients',
tableName: 'au_ingredients',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -3,7 +3,6 @@ import { DataTypes, Model, Optional } from "sequelize";
import { AuracleDatabaseDriver } from "../../AuracleDatabaseDriver";
interface MetaSchoolAttributes {
id: number
uuid: string
name: string
description: string
@@ -11,10 +10,9 @@ interface MetaSchoolAttributes {
published: boolean
}
interface MetaSchoolCreationAttributes extends Optional<MetaSchoolAttributes, 'id'> { }
interface MetaSchoolCreationAttributes extends Optional<MetaSchoolAttributes, 'uuid'> { }
export class MetaSchool extends Model<MetaSchoolAttributes, MetaSchoolCreationAttributes> implements MetaSchoolAttributes {
public readonly id!: number
public readonly uuid!: string
public name!: string
public description!: string
@@ -28,16 +26,12 @@ export class MetaSchool extends Model<MetaSchoolAttributes, MetaSchoolCreationAt
export default () => {
MetaSchool.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
defaultValue: DataTypes.UUIDV4
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING,
@@ -55,7 +49,7 @@ export default () => {
}
},
{
tableName: 'meta_schools',
tableName: 'au_meta_schools',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -3,7 +3,6 @@ import { DataTypes, Model, Optional } from "sequelize";
import { AuracleDatabaseDriver } from "../../AuracleDatabaseDriver";
interface SchoolAttributes {
id: number
uuid: string
name: string
description: string
@@ -11,10 +10,9 @@ interface SchoolAttributes {
published: boolean
}
interface SchoolCreationAttributes extends Optional<SchoolAttributes, 'id'> { }
interface SchoolCreationAttributes extends Optional<SchoolAttributes, 'uuid'> { }
export class School extends Model<SchoolAttributes, SchoolCreationAttributes> implements SchoolAttributes {
public readonly id!: number
public readonly uuid!: string
public name!: string
public description!: string
@@ -28,16 +26,12 @@ export class School extends Model<SchoolAttributes, SchoolCreationAttributes> im
export default () => {
School.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
defaultValue: DataTypes.UUIDV4
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING,
@@ -55,7 +49,7 @@ export default () => {
}
},
{
tableName: 'schools',
tableName: 'au_schools',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -1,55 +1,49 @@
import { DataTypes, Model, Optional } from "sequelize";
import { DataTypes, Model, Optional, TableHints } from "sequelize";
import { AuracleDatabaseDriver } from "../../AuracleDatabaseDriver";
interface SpellAttributes {
id: number
uuid: string
name: string
description: string
level: number
charge: number
cost: string
isRitual: boolean
level?: number
charge?: number
cost?: string
isRitual?: boolean
published: boolean
public: boolean
published?: boolean
public?: boolean
}
interface SpellCreationAttributes extends Optional<SpellAttributes, 'id'> { }
export interface SpellCreationAttributes extends Optional<SpellAttributes, 'uuid'> { }
export class SpellModel extends Model<SpellAttributes, SpellCreationAttributes> implements SpellAttributes {
public readonly id!: number
export class Spell extends Model<SpellAttributes, SpellCreationAttributes> implements SpellAttributes {
public readonly uuid!: string
public name!: string
public description!: string
public level: number
public charge: number
public cost: string
public isRitual: boolean
public level?: number
public charge?: number
public cost?: string
public isRitual?: boolean
public published: boolean
public public: boolean
public published?: boolean
public public?: boolean
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
export default () => {
SpellModel.init(
export default async () => {
Spell.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
defaultValue: DataTypes.UUIDV4
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING,
@@ -88,7 +82,7 @@ export default () => {
},
},
{
tableName: 'spells',
tableName: 'au_spells',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -3,17 +3,15 @@ import { DataTypes, Model, Optional } from "sequelize";
import { AuracleDatabaseDriver } from "../../AuracleDatabaseDriver";
interface VariableAttributes {
id: number
uuid: string
description: string
published: boolean
}
interface VariableCreationAttributes extends Optional<VariableAttributes, 'id'> { }
interface VariableCreationAttributes extends Optional<VariableAttributes, 'uuid'> { }
export class Variable extends Model<VariableAttributes, VariableCreationAttributes> implements VariableAttributes {
public readonly id!: number
public readonly uuid!: string
public description!: string
@@ -26,16 +24,12 @@ export class Variable extends Model<VariableAttributes, VariableCreationAttribut
export default () => {
Variable.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
defaultValue: DataTypes.UUIDV4
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
description: {
type: DataTypes.STRING,
@@ -48,7 +42,7 @@ export default () => {
}
},
{
tableName: 'variables',
tableName: 'au_variables',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -3,15 +3,13 @@ import { DataTypes, Model, Optional } from "sequelize";
import { AuracleDatabaseDriver } from "../../AuracleDatabaseDriver";
interface PermissionAttributes {
id: number
uuid: string
slug: string
}
interface PermissionCreationAttributes extends Optional<PermissionAttributes, 'id'> { }
interface PermissionCreationAttributes extends Optional<PermissionAttributes, 'uuid'> { }
export class Permission extends Model<PermissionAttributes, PermissionCreationAttributes> implements PermissionAttributes {
public readonly id!: number
public readonly uuid!: string
public slug!: string
@@ -22,16 +20,12 @@ export class Permission extends Model<PermissionAttributes, PermissionCreationAt
export default () => {
Permission.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
defaultValue: DataTypes.UUIDV4
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
slug: {
type: DataTypes.STRING,
@@ -40,7 +34,7 @@ export default () => {
},
},
{
tableName: 'permissions',
tableName: 'au_permissions',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -47,7 +47,7 @@ export default () => {
},
},
{
tableName: 'roles',
tableName: 'au_roles',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -70,7 +70,7 @@ export default () => {
},
},
{
tableName: 'users',
tableName: 'au_users',
sequelize: AuracleDatabaseDriver,
}
)

View File

@@ -14,11 +14,16 @@ import express from 'express'
import { AuracleApi } from './common/classes/AuracleApi'
import { AuracleDatabaseDriver } from './database/AuracleDatabaseDriver'
import { SpellRouter } from './routes/SpellRouter';
const apiPort = process.env.API_PORT
const app = new AuracleApi({
port: apiPort,
middlewares: [],
routers: [
new SpellRouter()
],
modules: [
express.json({ limit: '10kb' }),
morgan('dev'),

View File

@@ -0,0 +1,20 @@
import { AuracleApiRepository } from "../common/classes/AuracleApiRepository"
import { Spell, SpellCreationAttributes } from "../database/models/spells/Spell"
export class SpellRepository extends AuracleApiRepository {
constructor() {
super(new Spell)
}
public async fetchAll(): Promise<Spell[]> {
return Spell.findAll()
}
public async fetchOne(uuid: string): Promise<Spell> {
return Spell.findByPk(uuid)
}
public async createOne(spell: SpellCreationAttributes): Promise<Spell> {
return Spell.create(spell)
}
}

23
src/routes/SpellRouter.ts Normal file
View File

@@ -0,0 +1,23 @@
import { AuracleApiRouter } from "../common/classes/AuracleApiRouter";
import { SpellController } from "../controllers/SpellController";
export class SpellRouter extends AuracleApiRouter {
public controller: SpellController
constructor() {
super('/spells', new SpellController)
this.initRoutes()
}
public initRoutes() {
// GET: /spells
this.instance.get(this.resource, this.controller.getAll.bind(this.controller))
// GET: /spells/uuid
this.instance.get(`${this.resource}/:uuid`, this.controller.getOne.bind(this.controller))
// POST: /spells
this.instance.post(this.resource, this.controller.createOne.bind(this.controller))
}
}

View File

@@ -11,7 +11,7 @@
"sourceMap": true,
"outDir": "./dist",
"esModuleInterop": true,
"baseUrl": "./src",
"baseUrl": "/src",
"incremental": true,
"types": [
"node",