import first from "lodash/first"
import keys from "lodash/keys"
import getValues from "lodash/values"
import get from "lodash/get"
import Database from "../configure/Database"
/**
* Initialize your model with the table name
*
* @class BaseModel
* @param {string} name | table name that will be used in all your queries
* @extends {Database}
*/
class BaseModel extends Database {
constructor(name, propTypes) {
super()
this.name = name
this.propTypes = propTypes
/**
* Holds the error related to the current instance
* @type string | object
* @memberof BaseModel
*/
this.error = null
this.isValid = false
/**
* Holds whether or not the instance has been initialized and validated
* @type boolean
* @memberof BaseModel
*/
this.initialized = false
/**
* Holds the properties of the current instance
* @type object
* @memberof BaseModel
*/
this.props = {}
this.select = this.select.bind(this)
this.create = this.create.bind(this)
}
/**
* Validates the properties values
* @param {object} props | Props used to initialize the instance
* @returns {string} | returns the error while validating. It will return null if no error
*/
validate(props) {
if (!get(keys(props), "length", 0)) {
this.error = "props_must_have_at_least_one_attr"
this.isValid = false
return Promise.reject(this.error)
}
const columns = keys(this.propTypes)
for (let idx = 0; idx <= columns.length - 1; idx += 1) {
const propName = columns[idx]
const propValue = get(props, propName)
const propType = get(this.propTypes, propName)
const isRequired = propType.required
if (isRequired) {
if (!get(props, propName)) {
this.error = `${propName}_is_required`
break
}
const isValid = propType.isValid(propValue)
if (!isValid) {
this.error = `${propName}_is_not_${propType.type.toLowerCase()}`
break
}
}
}
this.isValid = !this.error
return this.error
}
/**
* Select entries from the table you initialized your class with
* @param {object} params | The paramerters of your search in the current entity table
* @returns {promise}
*/
select(params) {
const columns = keys(params)
if (!params || !columns.length) {
return this.query(`
SELECT *
FROM "${this.name}";
`)
}
const values = getValues(params)
let query = `"${first(columns)}"=$1`
for (let idx = 1; idx < columns.length; idx += 1) {
query = `${query} AND "${columns[idx]}" = $${idx + 1}`
}
return this.query(
`
SELECT *
FROM "${this.name}"
WHERE (${query});
`,
values,
)
}
/**
* Insert entries in the database table
* @param {object} props | The proprities your entity is composed from
* @returns {promise}
*/
insert(props) {
if (!this.isValid) {
if (!this.error) {
throw new Error(`ERROR: ${this.name} wasn't validated before inserting. Run ${this.name}.validate(props) and then try again.`)
} else {
return Promise.reject(this.error)
}
}
const columns = keys(props)
const values = getValues(props)
let query = `("${first(columns)}"`
let valuesPlaceholders = "$1"
for (let idx = 1; idx < columns.length; idx += 1) {
query = `${query}, "${columns[idx]}"`
valuesPlaceholders = `${valuesPlaceholders}, $${idx + 1}`
}
query = `${query})`
return this.query(
`
INSERT INTO "${this.name}" ${query}
VALUES (${valuesPlaceholders})
RETURNING *;
`,
values,
)
}
/**
* Update entries in the table you initialized your class with
* @param {object} props | The proprities your entity is composed from
* @returns {promise}
*/
updateById(id, props) {
if (!this.isValid) {
if (!this.error) {
throw new Error(`ERROR: ${this.name} wasn't validated before inserting. Run ${this.name}.validate(props) and then try again.`)
} else {
return Promise.reject(this.error)
}
}
if (!id) throw new Error("ERROR: {id} is required to perform UPDATE. Maybe you forgot to add your id?")
const columns = keys(props)
const values = getValues(props)
let query = `"${first(columns)}" = $1`
for (let idx = 1; idx < columns.length; idx += 1) {
query = `${query}, "${columns[idx]}" = $${idx + 1}`
}
query = `${query}`
return this.query(
`
UPDATE "${this.name}"
SET ${query}
WHERE id = ${id}
RETURNING *;
`,
values,
)
}
/**
* Deletes entries in the table you initialized your class with
* @param {int} id | The identifier of the instance your want to delete
*/
deleteById(id) {
if (!this.isValid) {
if (!this.error) {
throw new Error(`ERROR: ${this.name} wasn't validated before inserting. Run ${this.name}.validate(props) and then try again.`)
} else {
return Promise.reject(this.error)
}
}
if (!id) throw new Error("ERROR: {id} is required to perform DELETE. Maybe you forgot to add your id?")
return this.query(`
DELETE
FROM "${this.name}"
WHERE id = ${id}
RETURNING *;
`)
}
}
export default BaseModel