@janiscommerce/model-controller
Advanced tools
Comparing version 1.4.0 to 1.5.0
@@ -7,2 +7,14 @@ # Changelog | ||
## [1.5.0] - 2019-07-04 | ||
### Added | ||
- `Controller` getController method | ||
- `Controller` with Client injected | ||
- `Controller` Client propagation for controllers and models | ||
- `Model` with Client injected | ||
- `Controller` getPaged method | ||
### Changed | ||
- `README` updated | ||
- `tests` coverage 100% | ||
## [1.4.0] - 2019-06-24 | ||
@@ -9,0 +21,0 @@ ### Added |
@@ -8,3 +8,4 @@ 'use strict'; | ||
return { | ||
INVALID_CONTROLLER: 1 | ||
INVALID_CONTROLLER: 1, | ||
WRONG_CALLBACK: 2 | ||
}; | ||
@@ -11,0 +12,0 @@ } |
@@ -12,4 +12,18 @@ 'use strict'; | ||
const DEFAULT_PAGE_LIMIT = 500; | ||
class Controller { | ||
set client(client) { | ||
this._client = client; | ||
} | ||
get client() { | ||
return this._client; | ||
} | ||
static get defaultPageLimit() { | ||
return DEFAULT_PAGE_LIMIT; | ||
} | ||
static get folder() { | ||
@@ -19,6 +33,11 @@ return 'controllers'; | ||
static get controllersFilePath() { | ||
const prefix = typeof process.env.MS_PATH === 'string' ? process.env.MS_PATH : ''; | ||
return path.join(prefix, this.folder); | ||
} | ||
static get(controllerName) { | ||
const prefix = typeof process.env.MS_PATH === 'string' ? process.env.MS_PATH : ''; | ||
const modulePath = ModulesPath.get(path.join(prefix, this.folder), controllerName); | ||
controllerName = controllerName.toLowerCase(); | ||
const modulePath = ModulesPath.get(this.controllersFilePath, controllerName); | ||
@@ -42,8 +61,21 @@ if(!modulePath) | ||
if(!this._modelInstance) | ||
if(!this._modelInstance) { | ||
this._modelInstance = Model.getInstance(this.constructor.name); | ||
if(this.client) | ||
this._modelInstance.client = this.client; | ||
} | ||
return this._modelInstance; | ||
} | ||
getController(controllerName) { | ||
const controller = this.constructor.getInstance(controllerName); | ||
if(this.client) | ||
controller.client = this.client; | ||
return controller; | ||
} | ||
/** | ||
@@ -61,2 +93,5 @@ * Get database data | ||
if(typeof items === 'undefined') | ||
return null; | ||
const results = await this.prepareGetResults(items, params); | ||
@@ -69,5 +104,2 @@ | ||
if(typeof items === 'undefined') | ||
return null; | ||
const wasObject = !Array.isArray(items); | ||
@@ -83,22 +115,58 @@ | ||
items.forEach((item, index) => { | ||
let newItems = items.map((item, index) => { | ||
if(this.formatGet) | ||
this.formatGet(item); | ||
const newItem = this.formatGet ? this.formatGet(item) : item; | ||
if(this.afterGet && item.id) { | ||
indexes[item.id] = index; | ||
ids.push(item.id); | ||
if(this.afterGet && newItem.id) { | ||
indexes[newItem.id] = index; | ||
ids.push(newItem.id); | ||
} | ||
return newItem; | ||
}); | ||
if(this.afterGet) | ||
await this.afterGet(items, indexes, ids, params); | ||
newItems = await this.afterGet([...newItems], params, indexes, ids); | ||
if(wasObject) | ||
return items[0]; | ||
return newItems[0]; | ||
return params.changeKeys ? Utils.changeKeys(items, params.changeKeys) : items; | ||
return params.changeKeys ? Utils.changeKeys(newItems, params.changeKeys) : newItems; | ||
} | ||
/** | ||
* Get Paged database data | ||
* | ||
* @param {object} data Data for where | ||
* @param {function} callback Function to call for each batch of items | ||
*/ | ||
async getPaged(data = {}, callback) { | ||
if(!callback || typeof callback !== 'function') | ||
throw new ControllerError('Callback should be a function', ControllerError.codes.WRONG_CALLBACK); | ||
// se copia para que no se alteren las paginas y limites originales | ||
const params = { ...data }; | ||
if(!params.page) | ||
params.page = 1; | ||
if(!params.limit) | ||
params.limit = this.constructor.defaultPageLimit; | ||
const items = await this.get(params); | ||
if(!items || !items.length) | ||
return; | ||
await callback.call(null, items, params.page, params.limit); | ||
const newParams = { ...params }; | ||
newParams.page++; | ||
if(items.length === newParams.limit) | ||
this.getPaged(newParams, callback); | ||
} | ||
async getTotals() { | ||
@@ -105,0 +173,0 @@ |
@@ -8,2 +8,4 @@ 'use strict'; | ||
const logger = require('@janiscommerce/logger'); | ||
const ModelError = require('./model-error'); | ||
@@ -15,2 +17,73 @@ | ||
set client(client) { | ||
this._client = client; | ||
} | ||
get client() { | ||
return this._client; | ||
} | ||
static get clientConfigFilePath() { | ||
return path.join(process.cwd(), 'config', 'client-fields.json'); | ||
} | ||
static get clientFields() { | ||
if(typeof this._clientFields === 'undefined') { | ||
try { | ||
/* eslint-disable global-require, import/no-dynamic-require */ | ||
this._clientFields = require(this.clientConfigFilePath); | ||
/* eslint-enable */ | ||
if(typeof this._clientFields !== 'object' || Array.isArray(this._clientFields)) { | ||
logger.error('Client config fields bad format', this._clientFields); | ||
throw new Error('Client config fields bad format'); | ||
} | ||
} catch(error) { | ||
this._clientFields = {}; | ||
} | ||
this._prepareClientFields(); | ||
} | ||
return this._clientFields; | ||
} | ||
static _prepareClientFields() { | ||
const clientFields = this._clientFields; | ||
this._clientFields.read = { | ||
dbHost: clientFields.dbReadHost || 'dbReadHost', | ||
dbName: clientFields.dbReadName || 'dbReadName', | ||
dbUser: clientFields.dbReadUser || 'dbReadUser', | ||
dbPass: clientFields.dbReadPass || 'dbReadPass', | ||
dbPort: clientFields.dbReadPort || 'dbReadPort' | ||
}; | ||
this._clientFields.write = { | ||
dbHost: clientFields.dbWriteHost || 'dbWriteHost', | ||
dbName: clientFields.dbWriteName || 'dbWriteName', | ||
dbUser: clientFields.dbWriteUser || 'dbWriteUser', | ||
dbPass: clientFields.dbWritePass || 'dbWritePass', | ||
dbPort: clientFields.dbWritePort || 'dbWritePort' | ||
}; | ||
} | ||
get clientConfig() { | ||
const dbType = this.useReadDB ? 'read' : 'write'; | ||
const clientFields = this.constructor.clientFields[dbType]; | ||
return { | ||
host: this.client[clientFields.dbHost], | ||
database: this.client[clientFields.dbName], | ||
user: this.client[clientFields.dbUser], | ||
pass: this.client[clientFields.dbPass], | ||
port: this.client[clientFields.dbPort] | ||
}; | ||
} | ||
static get folder() { | ||
@@ -20,6 +93,11 @@ return 'models'; | ||
static get modelsFilePath() { | ||
const prefix = typeof process.env.MS_PATH === 'string' ? process.env.MS_PATH : ''; | ||
return path.join(prefix, this.folder); | ||
} | ||
static get(modelName) { | ||
const prefix = typeof process.env.MS_PATH === 'string' ? process.env.MS_PATH : ''; | ||
const modulePath = ModulesPath.get(path.join(prefix, this.folder), modelName); | ||
modelName = modelName.toLowerCase(); | ||
const modulePath = ModulesPath.get(this.modelsFilePath, modelName); | ||
@@ -42,6 +120,16 @@ if(!modulePath) | ||
get db() { | ||
return DatabaseDispatcher.getDatabase(this.databaseKey); | ||
if(this.databaseKey) | ||
return DatabaseDispatcher.getDatabaseByKey(this.databaseKey); | ||
if(this.client) | ||
return DatabaseDispatcher.getDatabaseByConfig(this.clientConfig); | ||
throw new ModelError(`Invalid Model ${this.constructor.name} - No database config`, ModelError.codes.DATABASE_CONFIG_NOT_FOUND); | ||
} | ||
async get(params = {}) { | ||
if(params.readonly) | ||
this.useReadDB = true; | ||
return this.db.get(this, params); | ||
@@ -48,0 +136,0 @@ } |
@@ -8,3 +8,4 @@ 'use strict'; | ||
return { | ||
INVALID_MODEL: 1 | ||
INVALID_MODEL: 1, | ||
DATABASE_CONFIG_NOT_FOUND: 2 | ||
}; | ||
@@ -11,0 +12,0 @@ } |
@@ -17,7 +17,8 @@ 'use strict'; | ||
} catch(e) { | ||
} catch(err) { | ||
if(e instanceof ReferenceError || e instanceof TypeError || e instanceof SyntaxError || e instanceof RangeError | ||
|| e.code !== 'MODULE_NOT_FOUND' || !(~e.message.indexOf(modulePath))) | ||
logger.error('Module', e); | ||
/* istanbul ignore if */ | ||
if(err instanceof ReferenceError || err instanceof TypeError || err instanceof SyntaxError || err instanceof RangeError | ||
|| err.code !== 'MODULE_NOT_FOUND' || !(~err.message.indexOf(modulePath))) | ||
logger.error('Module', err); | ||
@@ -30,4 +31,28 @@ moduleContent = false; | ||
/** | ||
* Change keys | ||
* | ||
* @param {array} items The items | ||
* @param {string} newKey The new key | ||
* @return {object} the new list of items with keys if exist | ||
* @example | ||
* Utils.changeKeys([{ id: 1, foo: 'bar' }, { id: 2, foo: 'bar2' }], 'id'); // { 1: { id: 1, foo: 'bar' }, 2: { id: 2, foo: 'bar2' } } | ||
* Utils.changeKeys([{ id: 1, foo: 'bar' }, { id: 2, foo: 'bar2' }], 'foo'); // { bar: { id: 1, foo: 'bar' }, bar2: { id: 2, foo: 'bar2' } } | ||
* Utils.changeKeys([{ id: 1, foo: 'bar' }, { id: 2, foo: 'bar2' }], 'wrongKey'); // {} | ||
*/ | ||
static changeKeys(items, newKey) { | ||
const newItems = {}; | ||
items.forEach(item => { | ||
if(newKey in item && item[newKey] !== null) | ||
newItems[item[newKey]] = item; | ||
}); | ||
return newItems; | ||
} | ||
} | ||
module.exports = Utils; |
{ | ||
"name": "@janiscommerce/model-controller", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"description": "The `model-controller` module allows you to get a controller/model class or instance easily", | ||
@@ -27,3 +27,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"@janiscommerce/database-dispatcher": "^1.1.0", | ||
"@janiscommerce/database-dispatcher": "^1.2.0", | ||
"@janiscommerce/logger": "^1.1.0", | ||
@@ -34,5 +34,5 @@ "@janiscommerce/modules-path": "^1.1.0" | ||
"eslint": "^5.16.0", | ||
"eslint-config-airbnb-base": "^13.1.0", | ||
"eslint-plugin-import": "^2.17.3", | ||
"husky": "^2.4.1", | ||
"eslint-config-airbnb-base": "^13.2.0", | ||
"eslint-plugin-import": "^2.18.0", | ||
"husky": "^2.7.0", | ||
"mocha": "^5.2.0", | ||
@@ -39,0 +39,0 @@ "mock-require": "^3.0.3", |
126
README.md
@@ -10,3 +10,3 @@ # Model Controller | ||
``` | ||
```bash | ||
npm install @janiscommerce/model-controller | ||
@@ -17,48 +17,112 @@ ``` | ||
### Controller.get(string) | ||
Example: Controller.get('category'); | ||
This method returns the controller class to use static methods or instance a controller. | ||
### Controller.get(string) **static** | ||
- This method returns the controller class to use static methods or instance a controller. | ||
**Example:** Controller.get('category'); | ||
### Controller.getInstance(string) | ||
Example: Controller.getInstance('category'); | ||
This method returns the instance of a controller class. | ||
### Controller.getInstance(string) **static** | ||
- This method returns the instance of a controller class. | ||
**Example:** Controller.getInstance('category'); | ||
### Controller.getModel(string) | ||
Example: myController.getModel(); | ||
This methods returns a Model Instance from a controller using his name. The method will cache the model to simple return it the next time. | ||
### Controller.getModel(string) **non-static** | ||
- This methods returns a Model Instance from a controller using his name. The method will cache the model to simple return it the next time. | ||
**Example:** myController.getModel(); | ||
### Model.get(string) | ||
Example: Model.get('category'); | ||
This method returns the model class to use static methods or instance a model. | ||
### Controller.client(any) *setter* **non-static** | ||
- This methods sets the client in the controller instance. | ||
**Example:** myController.client = { id: 1 }; | ||
**Example:** myController.client = 1; | ||
**Example:** myController.client = 'my-client-name'; | ||
### Model.getInstance(string) | ||
Example: Model.getInstance('category'); | ||
This method returns the instance of a model class. | ||
### Controller.client() *getter* **non-static** | ||
- This methods returns the client if any | ||
**Example:** myController.client; | ||
### Controller.getController(string) **non-static** | ||
- This methods returns an Controller instance. It propagates the client if any. | ||
**Example:** myController.getController('brand'); | ||
### Model.get(string) **static** | ||
- This method returns the model class to use static methods or instance a model. | ||
**Example:** Model.get('category'); | ||
### Model.getInstance(string) **static** | ||
- This method returns the instance of a model class. | ||
**Example:** Model.getInstance('category'); | ||
## Usage | ||
### How to get a `Product` class | ||
```js | ||
const { Model, Controller } = require('@janiscommerce/model-controller'); | ||
const { Controller } = require('@janiscommerce/model-controller'); | ||
// To get the Product class from e.g. 'path/to/node/process/controllers/product.js' | ||
const ProductController = Controller.get('product'); // this returns the product class stored in | ||
// To get the Product class from e.g. 'path/to/root/controllers/product.js' | ||
const ProductController = Controller.get('product'); // this returns the product class | ||
``` | ||
// To get a Product instance | ||
const myProduct = Controller.getInstance('product'); | ||
### How to get a `Product` instance | ||
```js | ||
const { Controller } = require('@janiscommerce/model-controller'); | ||
// To get a Product Model instance | ||
const productModel = myProduct.getModel(); | ||
const otherProductModel = myProduct.getModel(); | ||
// To get the Product instance from e.g. 'path/to/root/controllers/product.js' | ||
const productController = Controller.getInstance('product'); | ||
``` | ||
console.log(`'productModel' and 'otherProductModel' are ${productModel === otherProductModel ? 'equal' : 'different'}`); | ||
// expected output: 'productModel' and 'otherProductModel' are equal | ||
### How to get a `Product` model instance from a `Product` instance | ||
```js | ||
const { Controller } = require('@janiscommerce/model-controller'); | ||
// To get the Product Model class from e.g. 'path/to/node/process/models/product.js' | ||
// To get the Product instance from e.g. 'path/to/root/controllers/product.js' | ||
const productController = Controller.getInstance('product'); | ||
// To get the Product Model class from e.g. 'path/to/root/models/product.js' | ||
const myProduct = productController.getModel(); | ||
``` | ||
### How to get a `Product` model | ||
```js | ||
const { Model } = require('@janiscommerce/model-controller'); | ||
// To get the Product Model class from e.g. 'path/to/root/models/product.js' | ||
const ProductModel = Model.get('product'); | ||
``` | ||
// To get a Product Model instance | ||
const myProductModel = Model.getInstance('product'); | ||
### How to get a `Product` model instance | ||
console.log(`'productModel' and 'myProductModel' are ${productModel === myProductModel ? 'equal' : 'different'}`); | ||
// expected output: 'productModel' and 'myProductModel' are different | ||
```js | ||
const { Model } = require('@janiscommerce/model-controller'); | ||
// To get the Product Model class from e.g. 'path/to/root/models/product.js' | ||
const productModel = Model.getInstance('product'); | ||
``` | ||
### How to handle Client and propagation between controllers and models | ||
```js | ||
const { Controller } = require('@janiscommerce/model-controller'); | ||
const productController = Controller.getInstance('product'); | ||
productController.client = { | ||
id: 1, | ||
name: 'my-client-name', | ||
foo: 'bar' | ||
}; | ||
const products = await productController.get(); // get from DB using model + database-dispatcher. see @janiscommerce/database-dispatcher | ||
const categoryController = productController.getController('category'); | ||
console.log(categoryController.client); | ||
/** -- Expected output: | ||
{ | ||
id: 1, | ||
name: 'my-client-name', | ||
foo: 'bar' | ||
} | ||
*/ | ||
const categories = await categoryController.get(); // get from DB, should be the same DB than productsController | ||
``` |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
17120
358
126
6