npm package: @mnemotix/cortex-core
Description: Cortex Core -----------
Author: Mathieu Rogelja
Latest version: 0.0.14

Cortex Core
-----------

This is the main bundle that gathering all convenient toolkits to :

 - Fastly start a new Cortex-based application.
 - Create a new sharable Cortex bundle to share with every Cortex-based application.


## Principle

The main idea to remember is that a Cortex-based application is no more than a bunch of bundles, costumized or not, cleverly 
placed in a React application and linked each other thanks to the excellent concept of dynamic routing from React Router V4.

## Cortex-based application concepts

### Skeleton

```
client/
    entrypoint.js  --> The client entrypoint for webpack
    Application.js --> Extends a Cortex Application.
    routes.js      --> A list of Cortex Routes
    schema/
        schema.json -->  Autogenerated JSON schema (https://json-schema.org/) 
    components/
      ...          --> All application related components
config/
    environment.js --> List of environment variables
resources/
    ...            --> All images/icons/fonts stuff.
locales/
    fr/
        labels.json --> A i18next label file.
    ...
server/
    entrypoint.js   --> The server entrypoint to start with node.
    schema/
        schemaBuilder.js --> The datamodel schema definition (that generates the two following schema files)
        schema.graphql   --> Autogenerated GraphQL schema
        schema.json      --> Autogenerated JSON introspection schema (useful for Idea GraphQL plugin)
    datastores/
        Session.js       --> Extends a SynaptixSession    
styling/
    semantic.less        --> Copy of semantic-ui-less/semantic.less (Needed to the compiler)
    theme.config         --> SemanticUI theme config definition
    theme/
      ... All less variables and less overrides files.
webpack.config.js
webpack.production.config.js   
package.json
```
### Server side initialization steps.

#### 1. Defining and building a schema

Defining a schema consists on expose a `server/schema/schemaBuilder.js` defined as following :

```javascript
import {SchemaBuilder} from '@mnemotix/cortex-core';

export default new SchemaBuilder({
  typeDefs:  [] /* An array of GraphQL types (See Synaptix.js) */,
  resolvers: {} /* An object of GraphQL resolvers (See Synaptix.js) */,
  modelDefinitions: [] /* A list of ModelDefinitionAbstract (See Synaptix.js) */
});
```

For example if you want to use the FOAF ontology, Synaptix.js gives all resources defining the builder like so:

```javascript
import {SchemaBuilder} from '@mnemotix/cortex-core';
import {Ontologies, mergeResolvers} from '@mnemotix/synaptix.js';

export default new SchemaBuilder({
  typeDefs: [].concat(Ontologies.foaf.schema.Types, Ontologies.foaf.schema.Mutations),
  resolvers: mergeResolvers(Ontologies.foaf.resolvers.Types, Ontologies.foaf.resolvers.Mutations),
  modelDefinitions: Ontologies.foaf.ModelDefinitions
});
```

Of course, this is highly customizable. You can define your own ontology. Please take a look on the standard ontologies defined in Synaptix.js to implement one.

The resulting schema files will be automatically generated a launch time (see entrypoint section). 

- `server/schema/schema.graphql` is the default GraphQL schema used by GraphQL server ans Relay compiler.
- `server/schema/schema.json` is the introspection schema useful for IntelliJ Idea GraphQL Plugin
- `client/schema/schema.json` is the JSON Schema (https://json-schema.org) usefull for generating forms (see [@mnemotix/cortex-form](../cortex-form) bundle)

#### 2. Create an entrypoint

The next step consists on defining a server entrypoint in `server/entrypoint.js`.

##### The launchApplication() magic function.

In this entrypoint you just have to call plug-n-play function to start the app.

```javascript
import {launchApplication} from '@mnemotix/cortex-core';

launchApplication({
    Package, // This is the object related to package.json (needs a json-loader on Webpack)
    webpackDevConfig, //This is the Webpack config file.
    schemaBuilder, // This is the schema builder of the application
    bundles, //This list of bundles path (so as import/require path) used in the application. 
    generateGraphQLEndpoints, //GraphQL endpoints (detailed below)
    generateNetworkLayer //This is the network layer used to communicate with Synaptix middleware bus.
}).then(() => {
  /* app launched */
});
```

If you look to this function, you will see that it :

- Print a beautiful banner.
- Initialize and concatenate all environment variables defined in `[bundle]/config/environment.js`. [See environment section](#environment-variables)
- Initiate a connection to the Synaptix middleware bus
- Setup an ExpressJS server.
- Setup a Socket.IO websocket server binded to ExpressJS server.
- Setup all ExpressJS server middlewares located in `[bundle]/server/middlewares/index.js`. [See middleware section](#expressjs-middlewares).
- Setup a default SSO login/register endpoint. [See graphQL endpoints section](#graphql-endpoints)
- Setup GraphQL endpoints
- Initialize the Synaptix middleware adaptor. [See Synaptix adaptor section](#synaptix-adaptor).

##### Synaptix Adaptor

To interact with Synaptix middleware, the application needs to define three classes:

###### The Synaptix Adaptor

Located in `server/datastores/Adapter.js`

```javascript
import {SynaptixAdapter} from '@mnemotix/cortex-core';

export default class Adapter extends SynaptixAdapter {
  init(){
    /* Called a startup. Usefull for example to instantiate application specific context connections */
  }
}
```

**Note that** if no initialisation is needed you can directly use the Cortex-Core `SynaptixAdapter`.

###### The Synaptix Session 

Located in `server/datastores/Session.js` this class is instantiated for each GraphQL request (and more generally for all client user request).

This is in that class that you can add application specific middleware resolutions.

```javascript
import {SynaptixSession} from '@mnemotix/cortex-core';

export default class Session extends SynaptixSession {
  createForExampleSpecificObject(){
    /* Retrieve here the services to communicate with Synatix middleware. See Synaptix.js.*/
  }
}
```

##### Instantiation of a network layer

The network layer is the lowest layer to communicate with Synaptix Middleware bus.

For example to connect the bus using AMQP, use a `NetworkLayerAMQP` class.

```javascript
  import {NetworkLayerAMQP} from '@mnemotix/synaptix.js';
  import {SynaptixAdapter} from '@mnemotix/cortex-core';
  import {Session} from './datastores/synaptix/Session'; //See preceding section

  const amqpURL = `amqp://${process.env.RABBITMQ_LOGIN}:${process.env.RABBITMQ_PASSWORD}@${process.env.RABBITMQ_HOST}:${process.env.RABBITMQ_PORT}`;
  const networkLayer = new NetworkLayerAMQP(amqpURL, process.env.RABBITMQ_EXCHANGE_NAME);
  
  const datastoreAdapter = new SynaptixAdapter(schemaBuilder.generateModelDefinitionsRegister(), networkLayer, Session);
```

#### GraphQL Endpoints

An application can expose different GraphQL endpoints (Ex: one for the application, and one for a public API).

```javascript
let graphQLEndpoints = [
    {
      endpointURI: '/graphql',  // The exposed URL
      graphQLSchema: schemaBuilder.generateExecutableSchema({ // This method of SchemaBuilder is a quick way to generate an executable GraphQL schema and the related physical files.
        printGraphQLSchemaPath: path.join(__dirname, 'schema/schema.graphql'),          // Points to [application]/server/schema/schema.graphql 
        printJSONSchemaPath: path.join(__dirname, 'schema/schema.json'),                // Points to [application]/server/schema/schema.json     
        printJSONSchemaDotOrgPath: path.join(__dirname, '../client/schema/schema.json') // Points to [application]/client/schema/schema.json     
      }),
      datastoreAdapter // The instance of SynaptixAdaptor (See preceding section)
    }
  ];
```

#### All together

For example, your entrypoint could look like this :

```javascript
import path from 'path';
import Package from '../../package.json';
import webpackDevConfig from '../webpack.config';
import {SynaptixAdapter, launchApplication} from '@mnemotix/cortex-core';
import {NetworkLayerAMQP} from '@mnemotix/synaptix.js';

import Session from "./datastores/Session";
import schemaBuilder from "./schema/schemaBuilder";

const bundles = [
  path.resolve(__dirname, '../'),
  "@mnemotix/cortex-core",
  "@mnemotix/cortex-addressbook",
  "@mnemotix/cortex-profile",
  "@mnemotix/cortex-uploader",
  "@mnemotix/cortex-finder"
];


launchApplication({
    Package,
    schemaBuilder,
    webpackDevConfig,
    bundles,
    generateNetworkLayer: () => {
      const amqpURL = `amqp://${process.env.RABBITMQ_LOGIN}:${process.env.RABBITMQ_PASSWORD}@${process.env.RABBITMQ_HOST}:${process.env.RABBITMQ_PORT}`;
      return new NetworkLayerAMQP(amqpURL, process.env.RABBITMQ_EXCHANGE_NAME);
    },
    generateGraphQLEndpoints: (networkLayer) => {
      const datastoreAdapter = new SynaptixAdapter(schemaBuilder.generateModelDefinitionsRegister(), networkLayer, Session);
    
      return [
        {
          endpointURI: '/graphql',
          graphQLSchema: schemaBuilder.generateExecutableSchema({
            printGraphQLSchemaPath: path.join(__dirname, 'schema/schema.graphql'), /* Optionnal to print schema.graphql */
            printJSONSchemaPath: path.join(__dirname, 'schema/schema.json'),        /* Optionnal to print schema.json    */
            printJSONSchemaDotOrgPath: path.join(__dirname, '../client/schema/schema.json')        /* Optionnal to print schema.json    */
          }),
          datastoreAdapter
        }
      ];
    }
    })
.then()
.catch(error => console.log(error));
```

### Client