@janiscommerce/client-creator
Version:
A package that wraps all the client creation in Janis Services
495 lines (367 loc) • 13.2 kB
Markdown
# client-creator

[](https://coveralls.io/github/janis-commerce/client-creator?branch=master)
[](https://www.npmjs.com/package/@janiscommerce/client-creator)
## Introduction
This package includes all the generic functionality to create, update and remove a client from the services. Its main purpose is to avoid code repetition.
## :inbox_tray: Installation
```sh
npm install @janiscommerce/client-creator
```
## :hammer_and_wrench: Configuration
### AWS Parameter Store
_Since 7.1.0_
Retrieves database configurations for clients directly from AWS Parameter Store `{process.env.JANIS_SERVICE_NAME}-databases` and store configuration in `db` new field
Parameter Store expected parsed content
```json
{
"newClientsDatabases": {
"default": "6728b0de39a492eee4fcdaa8"
}
}
```
Client created
```json
{
"code": "client-code",
"db": {
"default": {
"id": "6728b0de39a492eee4fcdaa8",
"database": "service-name-client-code"
}
},
"status": "active"
}
```
> :warning: The client models will be dispatched using @janiscommerce/model@^8.8.0
### Service Settings ⚠️ **Deprecated**
You should configure the database config in your service for the new clients using the package [Settings](https://www.npmjs.com/package/@janiscommerce/settings) and the `newClientsDatabases` field
#### .janiscommercerc.json
```json
{
"newClientsDatabases": {
"default": { // DB config that the new clients will use
"type": "mongodb",
"database": "janis-{{code}}" // necessary to add dynamic database name. Since 3.0.0
},
"other-database": {
"write": {
"type": "solr",
"database": "core-{{code}}"
},
"read": {
"type": "solr",
"host": "host-read-solr",
"database": "core-{{code}}"
}
}
}
}
```
If we create a `brand-new-client` client with the previous settings, we will get the following client
```json
{
"code": "brand-new-client",
"databases": {
"default": {
"write": {
"type": "mongodb",
"database": "janis-brand-new-client"
}
},
"other-database": {
"write": {
"type": "solr",
"database": "core-brand-new-client"
},
"read": {
"type": "solr",
"database": "core-brand-new-client"
}
}
},
"status": "active"
}
```
### 🔑 Secrets ⚠️ **Deprecated**
The package will get the **secret** using the *JANIS_SERVICE_NAME* environment variable.
If the **secret** was found, the result will be merged with the settings found in the *`janiscommercerc.json`* in the `newClientsDatabases` field.
The Secrets are stored in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager) and obtained with the package [@janiscommerce/aws-secrets-manager](https://www.npmjs.com/package/@janiscommerce/aws-secrets-manager)
<details>
<summary>Complete example in which the settings are obtained in the settings file and merged with the fetched credentials in AWS Secrets Manager.</summary>
In the example will be used a new client **brand-new-client**.
1. Settings in file.
```json
{
"newClientsDatabases": {
"default": {
"type": "mongodb",
"database": "janis-{{code}}"
}
}
}
```
2. Secret fetched.
```json
{
"databases": {
"default": {
"write": {
"host": "mongodb+srv://some-host.mongodb.net",
"user": "secure-user",
"password": "secure-password",
}
}
}
}
```
3. Settings merged after fetching the Secret
```json
{
"default": {
"write": {
"type": "mongodb",
"database": "janis-brand-new-client",
"host": "mongodb+srv://some-host.mongodb.net",
"user": "secure-user",
"password": "secure-password",
}
}
}
```
</details>
### Skip Credential Fetching ⚠️ **Deprecated**
To skip the fetch of the credentials, it can be used the setting `skipFetchCredentials` set as **true**.
```json
{
"newClientsDatabases": {
"default": {
"write": {
"type": "mongodb",
"skipFetchCredentials": true,
"protocol": "mongodb+srv://",
"host": "mongodb+srv://some-host.mongodb.net",
"user": "some-user",
"password": "insecure-password"
}
}
}
}
```
### ClientModel
At `./[MS_PATH]/models/client.js`
```js
'use strict';
const { ModelClient } = require('@janiscommerce/client-creator');
module.exports = ModelClient;
```
:sparkles::new::sparkles: **Additional Fields**
Additional fields is a *getter* that allows the service to customize the clients fields, this is useful when a service needs their own custom data in clients.
> #### :information_source: This will affect Client Create API and also Client Updated Event behavior
> When a client is created or modified, the current client will be obtained from ID service and **only the additional fields that exist in the getter** will be saved in the service along with the basic client fields.
<details>
<summary>Examples using additionalFields()</summary>
**Model configuration**
```js
'use strict';
const { ModelClient } = require('@janiscommerce/client-creator');
module.exports = class MyModelClient extends ModelClient {
static get additionalFields() {
return [
'myAdditionalField',
'anotherAdditionalField'
]
}
};
```
**If a new client is created with these additional fields:**
```json
{
"name": "Some Client",
"code": "some-client",
"myAdditionalField": "some-additional-data",
"anotherAdditionalField": "another-additional-data",
"unusedAdditionalField": "unused-data"
}
```
**The client will be saved in the service with only the specified additional fields:**
```json
{
"name": "Some Client",
"code": "some-client",
"myAdditionalField": "some-additional-data",
"anotherAdditionalField": "another-additional-data"
}
```
</details>
### APICreate
This Api will create new clients received in `clients` parameter.
Parameters:
- clients `string Array`: The clients codes to be created. _optional_
- processClients `boolean`: If received as **true** will compare Service clients with Janis ID clients and create, update or remove clients when needed. _optional_
File location `./[MS_PATH]/api/client/post.js`
#### Basic version
```js
'use strict';
const { APICreate } = require('@janiscommerce/client-creator');
module.exports = APICreate;
```
#### `postSaveHook(clientCodes, clients)`
Receives the clientCodes and clients from the API.
Parameters:
- clientCodes `string Array`: The client created codes.
- clients `object Array`: The clients created objects that were saved.
:information_source: This hook is used when received `clients` or `processClients` (when need to create)
<details>
<summary>Example using ApiCreate postSaveHook()</summary>
```js
'use strict';
const { APICreate } = require('@janiscommerce/client-creator');
module.exports = class ClientCreateAPI extends APICreate {
async postSaveHook(clientCodes, clients) {
await myPostSaveMethod(clientCodes);
clientCodes.forEach(clientCode => {
console.log(`Saved client ${clientCode}, now i'm gonna do something great`);
})
clients.forEach(({ databases, status }) => {
console.log(`This epic client has ${databases.length} databases and its status is ${status}`)
})
}
};
```
</details>
#### `postUpdateHook(clients)`
Hook called after update clients.
:information_source: This hook is used when received `processClients` (when no need to create)
Parameters:
- clients `object`: The recently updated client.
#### `postRemoveHook(clients)`
Hook called after remove clients.
:information_source: This hook is used when received `processClients` (when found clients to remove in service)
Parameters:
- clientsCodes `string Array`: The recently removed client codes.
### ListenerCreated
This listener handles a created event emitted by Janis ID service. It allows to create a new client in the core database and set a new database for him.
File location `./[MS_PATH]/event-listeners/id/client/created.js`
#### Basic version
```js
'use strict';
const { ServerlessHandler } = require('@janiscommerce/event-listener');
const { ListenerCreated } = require('@janiscommerce/client-creator');
module.exports.handler = (...args) => ServerlessHandler.handle(ListenerCreated, ...args);
```
#### `postSaveHook(clientCode, client)`
Receives the clientCode and client from the event.
Parameters:
- clientCode `string`: The client created code of the created client.
- client `object`: The client created object that was saved.
<details>
<summary>Example using ListenerCreated postSaveHook()</summary>
```js
'use strict';
const { ServerlessHandler } = require('@janiscommerce/event-listener');
const { ListenerCreated } = require('@janiscommerce/client-creator');
class ClientCreatedListener extends ListenerCreated {
async postSaveHook(clientCode, client) {
console.log(`Saved client ${clientCode}, now i'm gonna do something great`);
console.log(`Saved client has ${client.databases.length} databases! Whoaaa`)
}
}
module.exports.handler = (...args) => ServerlessHandler.handle(ClientCreatedListener, ...args);
```
</details>
### ListenerUpdated
This listener handles an updated event emitted by Janis ID service. It allows to activate or deactivate a client by changing his status.
File location `./[MS_PATH]/event-listeners/id/client/updated.js`
#### Basic version
```js
'use strict';
const { ServerlessHandler } = require('@janiscommerce/event-listener');
const { ListenerUpdated } = require('@janiscommerce/client-creator');
module.exports.handler = (...args) => ServerlessHandler.handle(ListenerUpdated, ...args);
```
#### `postSaveHook(currentClient)`
Receives the currentClient from the event.
Parameters:
- currentClient `object`: The recently updated client.
<details>
<summary>Example using ListenerUpdated postSaveHook()</summary>
```js
'use strict';
const { ServerlessHandler } = require('@janiscommerce/event-listener');
const { ListenerUpdated } = require('@janiscommerce/client-creator');
class ClientUpdatedListener extends ListenerUpdated {
async postSaveHook(currentClient) {
console.log(`Saved client ${currentClient.name}, now i'm gonna do something great`);
}
}
module.exports.handler = (...args) => ServerlessHandler.handle(ClientUpdatedListener, ...args);
```
</details>
### ListenerRemoved
This listener handles a removed event emitted by Janis ID service. It allows to remove a client from the core clients database and drop his database.
File location `./[MS_PATH]/event-listeners/id/client/removed.js`
#### Basic version
```js
'use strict';
const { ServerlessHandler } = require('@janiscommerce/event-listener');
const { ListenerRemoved } = require('@janiscommerce/client-creator');
module.exports.handler = (...args) => ServerlessHandler.handle(ListenerRemoved, ...args);
```
#### `postRemovedHook(clientCode)`
Receives the removed clientCode from the API.
Parameters:
- clientCode `string`: The client removed code.
<details>
<summary>Example using ListenerRemoved postRemovedHook()</summary>
```js
'use strict';
const { ServerlessHandler } = require('@janiscommerce/event-listener');
const { ListenerRemoved } = require('@janiscommerce/client-creator');
class ClientRemovedListener extends ListenerRemoved {
async postRemovedHook(clientCode) {
console.log(`Saved client ${clientCode}, now i'm gonna do something great`);
}
}
module.exports.handler = (...args) => ServerlessHandler.handle(ClientRemovedListener, ...args);
```
</details>
### Serverless functions
The package exports `clientFunctions`, an array with serverless functions to simplify the usage. It has the hooks for the Create Api and Listeners.
At `./serverless.js`
```js
'use strict';
const { helper } = require('sls-helper'); // eslint-disable-line
const { clientFunctions } = require('@janiscommerce/client-creator');
module.exports = helper({
hooks: [
// other hooks
...clientFunctions
]
});
```
### Schemas
Add schemas for the Client Created, Updated and Removed event listeners and the Create Client API post. Subscribe to events.
At ` ./schemas/client/` add these two files:
- [create.yml](schemas/create.yml)
- [base.yml](schemas/base.yml)
At ` ./schemas/event-listeners/id/client` add this file:
- [created.yml](schemas/created.yml)
- [updated.yml](schemas/updated.yml)
- [removed.yml](schemas/removed.yml)
At ` ./events/src/id/` add this file:
- [client.yml](schemas/client.yml)
### Tests and coverage
The _default_ Api and Listeners (:warning: without customization) not need to be tested.
To avoid coverage leaks, in `./.nycrc`
```json
{
"exclude": [
//... other excluded files
"src/event-listeners/id/client/",
"src/models/client.js",
"src/api/client/post.js"
]
}
```
> :warning: If exists any customization of the files, do not add the file to the .nycrc and add the corresponding tests.