|
|
|
@ -3,10 +3,18 @@ import knexConfig from 'knex'; |
|
|
|
|
import assert from 'assert'; |
|
|
|
|
import { attachPaginate } from 'knex-paginate'; |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
A preconfigured knex.js driver using the config.development configuration |
|
|
|
|
by default. |
|
|
|
|
|
|
|
|
|
_TODO_: Need to make this configurable, even though I just use one config right now since I run sqlite3 all the time. |
|
|
|
|
*/ |
|
|
|
|
export const knex = knexConfig(config.development); |
|
|
|
|
|
|
|
|
|
// run the PERF_TRICKS to configure sqlite3 when thing start, really need to make this
|
|
|
|
|
// a configuration and only do it with sqlite3, but for now just get this done
|
|
|
|
|
/* |
|
|
|
|
run the PERF_TRICKS to configure sqlite3 when thing start, really need to make this |
|
|
|
|
a configuration and only do it with sqlite3, but for now just get this done |
|
|
|
|
*/ |
|
|
|
|
if(config.development.client === "sqlite3") { |
|
|
|
|
const PERF_TRICKS = [ |
|
|
|
|
"pragma journal_mode = WAL", // use a WAL journal to not block writers/readers
|
|
|
|
@ -25,6 +33,10 @@ if(config.development.client === "sqlite3") { |
|
|
|
|
|
|
|
|
|
attachPaginate(); |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
Filled in by `load_schema` to give access to the database scheme in the admin |
|
|
|
|
tool and generally through the API. |
|
|
|
|
*/ |
|
|
|
|
export const SCHEMA = {}; |
|
|
|
|
|
|
|
|
|
const load_schema = async () => { |
|
|
|
@ -43,11 +55,11 @@ await load_schema(); |
|
|
|
|
is called by Model.validation and you can call it directly to get rules |
|
|
|
|
for a database table. |
|
|
|
|
|
|
|
|
|
+ `name string` - the table name. |
|
|
|
|
+ `rules Object` - default rules with empty "" for the rules you want filled in |
|
|
|
|
+ `all boolean` - set this to true if you want everything |
|
|
|
|
+ `no_id boolean` - defaults to true, set false if you also want the id |
|
|
|
|
+ `return Object` - the resulting rules to use with Validator |
|
|
|
|
1. `name string` - the table name. |
|
|
|
|
2. `rules Object` - default rules with empty "" for the rules you want filled in |
|
|
|
|
3. `all boolean` - set this to true if you want everything |
|
|
|
|
4. `no_id boolean` - defaults to true, set false if you also want the id |
|
|
|
|
5. `return Object` - the resulting rules to use with Validator |
|
|
|
|
*/ |
|
|
|
|
export const validation = (name, rules, all=false, no_id=true) => { |
|
|
|
|
assert(rules, "rules parameter is required and will be modified"); |
|
|
|
@ -99,20 +111,73 @@ export const validation = (name, rules, all=false, no_id=true) => { |
|
|
|
|
return rules; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
The base class for all models found in `lib/models.js`. You use this by extending it with: |
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
class User extends Model.from_table('user') { |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
This will create a `User` class that is automatically configured using the SCHEMA create from the `user` table in your database. You won't need to define the attributes on this class as it will be correctly populated from the database. |
|
|
|
|
|
|
|
|
|
The database is therefore the "source of truth" for all of the models. You can then add functions to extend what this class does. |
|
|
|
|
*/ |
|
|
|
|
export class Model { |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
Allows you to build a new object of this Model with the given `attr` |
|
|
|
|
already set, but really you should use the `Model.from(attr)` method instead. |
|
|
|
|
This does _no_ object sanitization with `Model.clean(attr)` method, and if |
|
|
|
|
it doesn't match the underlying database it will throw an exception. |
|
|
|
|
|
|
|
|
|
- `attr Object` - the attributes for this model |
|
|
|
|
*/ |
|
|
|
|
constructor(attr) { |
|
|
|
|
assert(attr, "Must give attributes."); |
|
|
|
|
Object.assign(this, attr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
How to actually create a new instance of this model. This |
|
|
|
|
will do two things: |
|
|
|
|
|
|
|
|
|
1. Correctly use the schema for the subclass model. |
|
|
|
|
2. Sanitize the input to remove anything that shouldn't be in the database. |
|
|
|
|
|
|
|
|
|
The `also_remove` parameter is a list of additional keys to also scrub from the object. |
|
|
|
|
|
|
|
|
|
- `attr Object` -- The attributes this should start with. |
|
|
|
|
- `also_remove Array` -- list of additional attributes to remove. |
|
|
|
|
*/ |
|
|
|
|
static from(attr, also_remove=undefined) { |
|
|
|
|
return new this(this.clean(attr, also_remove)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
Returns an object representing the schema for this Model. Remember that this |
|
|
|
|
will reflect what's in the database schema, which is formatted however |
|
|
|
|
`knex.js` formats your database Schema. Might not be portable between |
|
|
|
|
databases and only tested with SQlite3. |
|
|
|
|
|
|
|
|
|
_This is an attribute accessor, so just do `obj.schema` rather than call it like a function._ |
|
|
|
|
|
|
|
|
|
- `return Object` - The schema for this model. |
|
|
|
|
*/ |
|
|
|
|
get schema() { |
|
|
|
|
return this.constructor.schema; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
Uses the `this.schema` scrub out any attributes that are not valid for the |
|
|
|
|
schema. This is effectively a whitelist for the allowed attributes based on |
|
|
|
|
the database schema. You can use the `also_remove` parameter to list |
|
|
|
|
additional attributes to remove, which you should do to sanitize incoming |
|
|
|
|
objects for things like password fields. |
|
|
|
|
|
|
|
|
|
- `attr Object` - The attributes to clean. |
|
|
|
|
- `also_remove Array` - Additional attributes to remove. |
|
|
|
|
*/ |
|
|
|
|
static clean(attr, also_remove=undefined) { |
|
|
|
|
assert(attr, "Must give attributes to clean."); |
|
|
|
|
|
|
|
|
@ -128,18 +193,18 @@ export class Model { |
|
|
|
|
return this.constructor.table_name; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns an object of basic rules meant for lib/api.js:validate |
|
|
|
|
* based on what's in the database. It's meant to be an easy to |
|
|
|
|
* pass in starter which you can augment. It expects a set of rules |
|
|
|
|
* with keys you want configured. Any key that's set to an empty string "" |
|
|
|
|
* will be filled in with a minimum rule to match the database schema. |
|
|
|
|
* |
|
|
|
|
* It's designed to be called once at the top of an api/ handler to get |
|
|
|
|
* a basic set of rules. You could also run it to print out the rules then |
|
|
|
|
* simply write the rules directly where you need them. |
|
|
|
|
* |
|
|
|
|
* @param rules {Object} - rules specifier |
|
|
|
|
/* |
|
|
|
|
Returns an object of basic rules meant for lib/api.js:validate |
|
|
|
|
based on what's in the database. It's meant to be an easy to |
|
|
|
|
pass in starter which you can augment. It expects a set of rules |
|
|
|
|
with keys you want configured. Any key that's set to an empty string "" |
|
|
|
|
will be filled in with a minimum rule to match the database schema. |
|
|
|
|
|
|
|
|
|
It's designed to be called once at the top of an api/ handler to get |
|
|
|
|
a basic set of rules. You could also run it to print out the rules then |
|
|
|
|
simply write the rules directly where you need them. |
|
|
|
|
|
|
|
|
|
- `param rules {Object}` - rules specifier |
|
|
|
|
*/ |
|
|
|
|
static validation(rules) { |
|
|
|
|
return validation(this.table_name, rules); |
|
|
|
@ -204,13 +269,13 @@ export class Model { |
|
|
|
|
return new this(attr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Implements an upsert (insert but update on conflict) for Postgres, MySQL, and SQLite3 only. |
|
|
|
|
* |
|
|
|
|
* @param { Object } attr - The attributes to insert or update. |
|
|
|
|
* @param { string } conflict_key - The key that can cause a conflict then update. |
|
|
|
|
* @param { boolean } merge - Defaults to true and will change the record. false will ignore and not update on conflict. |
|
|
|
|
* @return { number } - id or undefined |
|
|
|
|
/* |
|
|
|
|
Implements an upsert (insert but update on conflict) for Postgres, MySQL, and SQLite3 only. |
|
|
|
|
|
|
|
|
|
+ attr { Object } - The attributes to insert or update. |
|
|
|
|
+ conflict_key { string } - The key that can cause a conflict then update. |
|
|
|
|
+ merge { boolean } - Defaults to true and will change the record. false will ignore and not update on conflict. |
|
|
|
|
+ return { number } - id or undefined |
|
|
|
|
*/ |
|
|
|
|
static async upsert(attr, conflict_key, merge=true) { |
|
|
|
|
assert(conflict_key !== undefined, `You forgot to set the conflict_key on upsert to table ${this.table_name}`); |
|
|
|
|