npm package: @knetik/micro-core
Description: This package defines an application structure for deploying a traditional node project with a multi tenant workflow.
Latest version: 3.1.45

# Knetik Micro Core

This package defines an application structure for deploying a traditional node
project with a multi tenant workflow.

Micro Core is built on a multi tenant premise. On project Init the Core app is
setup with all the required dependencies and adaptors but no connections are
made. When a customer connects, a connection is made, an instance of Core is 
created scoped to that user's environment and returned as App. Use `App` to 
access user scoped data and clients. Use `Core` to access all non multi tenant 
functionality.

### Setup

1. Setup an npm project as usual.

~~~
mkdir myAppName
cd myAppName
npm/yarn init
~~~

2. Add the micro-core package

~~~
npm i --save @knetik/micro-core
# or
yarn add @knetik/micro-core
~~~

3. Add adaptors - https://www.npmjs.com/search?q=%40knetik/micro

~~~
npm i --save @knetik/micro-mongoose @knetik/micro-knetikcloud
# or
yarn add @knetik/micro-mongoose @knetik/micro-knetikcloud
~~~

4. Genrate the App skeleton

~~~
node ./node_modules/.bin/micro-new
(Windows powershell) yarn run micro-new
~~~

### Development

There are README files throughout the application. A bit of explanation and an
example go a long way.

**enter the REPL**

micro-core provides a REPL to assist in the development process. Enter the REPL with:

```
node ./node_modules/.bin/micro-console
```

Once the REPL returns a promt:

- Enter `Core` and hit return. Yeah, that's where the magic happens.  
- Try `Core.connect(<an_app_id>, [oauth_token]).then(App => console.log(App))`

The REPL gives you full access to your entrie application right in your terminal. 
[more on REPLs](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)

**to boot the http server**

```
node ./node_modules/.bin/micro-serve
```

The `app_id` is resolved from the first subdomian segment of requesting domain
or from the configured `APP_ID_KEY` request header. Which defaults to 
`x-knetikcloud-appid`

### Testing

Still working on a way to include jasmine as a dev dependency from the package.
For now just install the required packages.

~~~
npm i --save-dev @knetik/micro-test
# or
yarn add --dev @knetik/micro-test
~~~

- run `node ./node_modules/.bin/micro-test` or `jasmine` from the project root, 
  specs go in the spec directory. :)

### Deploy

Use `serverless` to deploy to lambda.

- install the serverless cli - `npm install -g serverless`
- Update the sls.yml files in .dist with the correct info
- copy the correct sls.yml file to the project root - `cp ./dist/sls.development.yml ./serverless.yml`
- run `sls deploy` to deploy
- running `sls info` returns the API Gateway url. Visit your newly deployed application in the browser.

## App Functionality

### Configs

Configs are stored in `config/environments/{env}.json`. On App init variables
are merged into process.env.

### Logger

There are 2 primary loggers in a micro-core application. The Core.Logger and the
App.Logger. App.Logger is scoped to the client and attached to the client's app
instance on Core.connect.


### Load Paths

Any directory added inside `app` is auto loaded. Modules inside the app sub 
directories require an exported function with a single `App` parameter. The 
return value of the function will is made available with the `get()` method. 
`App.get('{directory_name}.{module_name}')`

`get()` accepts snake_case or PascalCase dot separated paths.

'services.example_service' or 'Services.ExampleService'

- [module example](https://github.com/knetikmedia/micro-multi/blob/master/packages/multi-core/services/example_service.js)
- [usage example](https://github.com/knetikmedia/micro-multi/blob/master/packages/multi-core/app/routes/example.js#L30)

### Initializers

Initializers are stored in `config/initializers` and are loaded after Core.init 
and before Core.serve. Now is the time to setup custom libraries and other 
project specific configurations. Tools that are not Multi Tenant should be setup here.
Maybe AWS libs or a Google SDK. Attaching initialized services to Core is encouraged.

### Credentials Store

On init a CredentialsStore object is assigned to the Core application. This is a simple key value store exposing three methods.

~~~
Core.CredentialsStore.set(key, value) // => promise(value)
Core.CredentialsStore.get(key) // => promise(value)
Core.CredentialsStore.remove(key) // => promise(boolean)
~~~

The default is an in memory store. But configuration is made available for using
other providers.  [EXAMPLE](https://github.com/knetikmedia/micro-multi/tree/master/packages/micro-credentials_store-s3)

On App.connect a credential store is assigned to the returned app instance and is scoped to the app_id

~~~
Core.connect(app_id)
.then(App => {
  App.CredentialsStore.set(key, value) // => promise(value)
  App.CredentialsStore.get(key) // => promise(value)
  App.CredentialsStore.remove(key) // => promise(boolean)
})
~~~

### Core Adaptors

A core adaptor is defined by any lib that hooks into the Core init process. 
Core Adaptors are npm packages that export an Adaptor class exposing a single static 
function

- init<required>: Setup any library prerequisites that are the same on any client.

Include a core adaptor in your micro-core app by:

- Adding the adaptor package to your project
  - Available adaptors: https://www.npmjs.com/search?q=%40knetik%2Fmicro
- Then add it to the config .json file in the CORE_ADAPTORS array.

### Adaptors

An adaptor is defined by any lib that hooks into the Core init process. Some may 
support multi tenancy I.E.  Postgres, Mongoose, Redis, Knetikcloud. Adaptors are 
npm packages that export an Adaptor class exposing 4 static functions

- init<required>: Setup any library prerequsites that are the same on any client. Multi 
  tenant Adaptors must make a call to
  `App.AdaptorInitializer.add(adaptor_name, this /* adaptor class */)`
- configDefaults: defines the required config variables that are imported when
  micro-new is run. 
- connect: Required for multi tenant adaptors only. Should receive an App instance
  as a parameter, create a connection to the wrapped library scoped to the
  app instance, return the connection or an instance of the adaptor holding the 
  connection.
- addInitializers: Is run during Core.init after initializer load and before 
  express listen. It accepts a single Core parameter and no return value is 
  expected. Do anything you want with Core at this point.

Include an adaptor in your micro-core app by:

- Adding the adaptor package to your project
  - Available adaptors: https://www.npmjs.com/search?q=%40knetik%2Fmicro
- Then add it to the config .json file in the adaptors array.
- TODO: Adaptor configs are not merged after micro-new has been run