Skip to content

Commit

Permalink
Refactoring of how we pass data back and forth
Browse files Browse the repository at this point in the history
  • Loading branch information
andyslack committed Sep 12, 2024
1 parent 08aacba commit f9770bc
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 199 deletions.
60 changes: 31 additions & 29 deletions src/app.controller.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,15 @@ export class GetController {
const table_name = UrlToTable(req.originalUrl, 1)
const id = req.params.id

let schema: DatabaseSchema
const options: DatabaseFindOneOptions = {
schema: null,
fields: [],
where: [],
relations: [],
}

try {
schema = await this.schema.getSchema(table_name)
options.schema = await this.schema.getSchema(table_name)
} catch (e) {
return res.status(404).send(this.response.text(e.message))
}
Expand All @@ -97,13 +102,6 @@ export class GetController {
return res.status(401).send(this.response.text(auth.message))
}

const options: DatabaseFindOneOptions = {
schema,
fields: [],
where: [],
relations: [],
}

//perform role check
if (auth.user_identifier) {
let permission = await this.roles.tablePermission(auth.user_identifier, table_name, RolePermission.READ)
Expand All @@ -116,27 +114,30 @@ export class GetController {
permission = permission as AuthTablePermissionSuccessResponse

if (permission.restriction.column.includes('.')) {
options.relations.concat(await this.schema.convertDeepWhere(permission.restriction, schema))
options.relations.concat(await this.schema.convertDeepWhere(permission.restriction, options.schema))
} else {
options.where.push(permission.restriction)
}
}
}

//validate :id field
const primary_key = this.schema.getPrimaryKey(schema)
const primary_key = this.schema.getPrimaryKey(options.schema)

if (!primary_key) {
return res.status(400).send(this.response.text(`No primary key found for table ${table_name}`))
}

const validateKey = await this.schema.validateData(schema, { [primary_key]: id })
const validateKey = await this.schema.validateData(options.schema, { [primary_key]: id })
if (!validateKey.valid) {
return res.status(400).send(this.response.text(validateKey.message))
}

if (req.query.fields) {
const { valid, message, fields, relations } = await this.schema.validateFields(schema, req.query.fields)
const { valid, message, fields, relations } = await this.schema.validateFields(
options.schema,
req.query.fields,
)
if (!valid) {
return res.status(400).send(this.response.text(message))
}
Expand All @@ -156,7 +157,7 @@ export class GetController {

if (req.query.relations) {
const { valid, message, relations } = await this.schema.validateRelations(
schema,
options.schema,
req.query.relations,
options.relations,
)
Expand Down Expand Up @@ -201,10 +202,16 @@ export class GetController {
async list(@Req() req, @Res() res): Promise<FindManyResponseObject> {
const table_name = UrlToTable(req.originalUrl, 1)

let schema: DatabaseSchema
const options: DatabaseFindManyOptions = {
schema: null,
fields: [],
where: [],
relations: [],
sort: [],
}

try {
schema = await this.schema.getSchema(table_name)
options.schema = await this.schema.getSchema(table_name)
} catch (e) {
return res.status(404).send(this.response.text(e.message))
}
Expand All @@ -214,14 +221,6 @@ export class GetController {
return res.status(401).send(this.response.text(auth.message))
}

const options: DatabaseFindManyOptions = {
schema,
fields: [],
where: [],
relations: [],
sort: [],
}

//perform role check
if (auth.user_identifier) {
let permission = await this.roles.tablePermission(auth.user_identifier, table_name, RolePermission.READ)
Expand All @@ -234,7 +233,7 @@ export class GetController {
permission = permission as AuthTablePermissionSuccessResponse

if (permission.restriction.column.includes('.')) {
options.relations.concat(await this.schema.convertDeepWhere(permission.restriction, schema))
options.relations.concat(await this.schema.convertDeepWhere(permission.restriction, options.schema))
} else {
options.where.push(permission.restriction)
}
Expand All @@ -246,7 +245,10 @@ export class GetController {
options.offset = offset

if (req.query.fields) {
const { valid, message, fields, relations } = await this.schema.validateFields(schema, req.query.fields)
const { valid, message, fields, relations } = await this.schema.validateFields(
options.schema,
req.query.fields,
)
if (!valid) {
return res.status(400).send(this.response.text(message))
}
Expand All @@ -266,7 +268,7 @@ export class GetController {

if (req.query.relations) {
const { valid, message, relations } = await this.schema.validateRelations(
schema,
options.schema,
req.query.relations,
options.relations,
)
Expand All @@ -283,7 +285,7 @@ export class GetController {
}
}

const validateWhere = await this.schema.validateWhereParams(schema, req.query)
const validateWhere = await this.schema.validateWhereParams(options.schema, req.query)
if (!validateWhere.valid) {
return res.status(400).send(this.response.text(validateWhere.message))
}
Expand All @@ -294,7 +296,7 @@ export class GetController {

let validateSort
if (req.query.sort) {
validateSort = this.schema.validateSort(schema, req.query.sort)
validateSort = this.schema.validateSort(options.schema, req.query.sort)
if (!validateSort.valid) {
return res.status(400).send(this.response.text(validateSort.message))
}
Expand Down
37 changes: 20 additions & 17 deletions src/app.controller.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Response } from './helpers/Response'
import { Roles } from './helpers/Roles'
import { Schema } from './helpers/Schema'
import { AuthTablePermissionFailResponse } from './types/auth.types'
import { DatabaseSchema, QueryPerform } from './types/database.types'
import { DatabaseCreateOneOptions, QueryPerform } from './types/database.types'
import { FindOneResponseObject, IsUniqueResponse } from './types/response.types'
import { RolePermission } from './types/roles.types'

Expand Down Expand Up @@ -37,10 +37,13 @@ export class PostController {
const table_name = UrlToTable(req.originalUrl, 1)
const body = req.body

let schema: DatabaseSchema
const options: DatabaseCreateOneOptions = {
schema: null,
data: {},
}

try {
schema = await this.schema.getSchema(table_name)
options.schema = await this.schema.getSchema(table_name)
} catch (e) {
return res.status(404).send(this.response.text(e.message))
}
Expand All @@ -52,33 +55,33 @@ export class PostController {

//perform role check
if (auth.user_identifier) {
const permission = await this.roles.tablePermission(auth.user_identifier, table_name, RolePermission.WRITE)
const { valid, message } = (await this.roles.tablePermission(
auth.user_identifier,
table_name,
RolePermission.WRITE,
)) as AuthTablePermissionFailResponse

if (!permission.valid) {
return res.status(401).send(this.response.text((permission as AuthTablePermissionFailResponse).message))
if (!valid) {
return res.status(401).send(this.response.text(message))
}
}

//validate input data
const validate = await this.schema.validateData(schema, body)
if (!validate.valid) {
return res.status(400).send(this.response.text(validate.message))
const { valid, message, instance } = await this.schema.validateData(options.schema, body)
if (!valid) {
return res.status(400).send(this.response.text(message))
}

options.data = instance

//validate uniqueness
const uniqueValidation = (await this.query.perform(QueryPerform.UNIQUE, {
schema,
data: body,
})) as IsUniqueResponse
const uniqueValidation = (await this.query.perform(QueryPerform.UNIQUE, options)) as IsUniqueResponse
if (!uniqueValidation.valid) {
return res.status(400).send(this.response.text(uniqueValidation.message))
}

try {
const result = await this.query.perform(QueryPerform.CREATE, {
schema,
data: validate.instance,
})
const result = await this.query.perform(QueryPerform.CREATE, options)

return res.status(201).send(result)
} catch (e) {
Expand Down
97 changes: 56 additions & 41 deletions src/databases/mysql.database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ import {
DatabaseFindManyOptions,
DatabaseFindOneOptions,
DatabaseFindTotalRecords,
DatabaseJoinStage,
DatabaseSchema,
DatabaseSchemaColumn,
DatabaseSchemaRelation,
DatabaseUniqueCheckOptions,
DatabaseUpdateOneOptions,
DatabaseWhere,
WhereOperator,
} from '../types/database.types'
import { MySQLColumnType } from '../types/databases/mysql.types'
Expand Down Expand Up @@ -169,7 +167,7 @@ export class MySQL {
return
}

return this.pipeMySQLToObject({ schema: options.schema, data: results[0] })
return this.formatOutput(options, results[0])
}

/**
Expand Down Expand Up @@ -203,7 +201,7 @@ export class MySQL {
const results = await this.performQuery(command, values)

for (const r in results) {
results[r] = this.pipeMySQLToObject({ schema: options.schema, data: results[r] })
results[r] = this.formatOutput(options, results[r])
}

return {
Expand Down Expand Up @@ -379,28 +377,39 @@ export class MySQL {
const table_name = options.schema.table
let values: any[] = []

console.log(options)

const fields = options.fields?.length ? options.fields : options.schema.columns.map(column => column.field)

for (const f in fields) {
if (!fields[f].includes('.')) {
fields[f] = `${table_name}.` + fields[f]
}
}

let command

if (count) {
command = `SELECT COUNT(*) as total `
} else {
command = `SELECT ${fields?.length ? fields.join(`, `) : '*'} `
command = `SELECT `

if (options.fields?.length) {
for (const f in options.fields) {
command += ` \`${options.schema.table}\`.\`${options.fields[f]}\` as \`${options.fields[f]}\`,`
}
command = command.slice(0, -1)
} else {
command += ` \`${options.schema.table}\`.* `
}

if (options.relations?.length) {
for (const r in options.relations) {
if (options.relations[r].columns?.length) {
for (const c in options.relations[r].columns) {
command += `, \`${options.relations[r].table}\`.\`${options.relations[r].columns[c]}\` as \`${options.relations[r].table}.${options.relations[r].columns[c]}\` `
}
}
}
}
}

command += `FROM ${table_name} `
command += ` FROM ${table_name} `

for (const relation of options.relations) {
command += `${relation.join.type ?? 'INNER JOIN'} ${relation.join.table} ON ${relation.join.org_table}.${relation.join.org_column} = ${relation.join.table}.${relation.join.column} `
if (options.relations?.length) {
for (const relation of options.relations) {
command += `${relation.join.type ?? 'INNER JOIN'} ${relation.join.table} ON ${relation.join.org_table}.${relation.join.org_column} = ${relation.join.table}.${relation.join.column} `
}
}

if (options.where?.length) {
Expand Down Expand Up @@ -519,34 +528,40 @@ export class MySQL {
return options
}

private pipeMySQLToObject(options: { schema: DatabaseSchema; data: { [key: string]: any } }): object {
private formatOutput(options: DatabaseFindOneOptions, data: { [key: string]: any }): object {
//TODO: this will only work for one level deep, need to refactor to work with multiple levels

//TODO: remove items not in the object, loop over relations and do the same for them... (returning in nice clean json format)

for (const column of options.schema.columns) {
if (!options.data[column.field]) {
continue
for (const key in data) {
if (key.includes('.')) {
const [table, field] = key.split('.')
const relation = options.relations.find(r => r.table === table)
data[key] = this.formatField(relation.schema.columns.find(c => c.field === field).type, data[key])
} else {
const column = options.schema.columns.find(c => c.field === key)
data[key] = this.formatField(column.type, data[key])
}
}

switch (column.type) {
case DatabaseColumnType.BOOLEAN:
if (options.data[column.field] === 1) {
options.data[column.field] = true
} else if (options.data[column.field] === 0) {
options.data[column.field] = false
}
break
case DatabaseColumnType.DATE:
if (options.data[column.field]) {
options.data[column.field] = new Date(options.data[column.field]).toISOString()
}
break
default:
continue
}
return data
}

/**
*
*/

private formatField(type: DatabaseColumnType, value: any): any {
if (value === null) {
return null
}

return options.data
switch (type) {
case DatabaseColumnType.BOOLEAN:
return value === 1
case DatabaseColumnType.DATE:
return new Date(value).toISOString()
default:
return value
}
}

async truncate(table_name: string): Promise<void> {
Expand Down
Loading

0 comments on commit f9770bc

Please sign in to comment.