@mnemotix/weever-core

2.1.1 • Public • Published

Weever

[[TOC]]

Tour d'horizon

Weever est un outil de documentation articulé autour d'une chaîne de traitement sémantique.

Dit autrement, on peut la voir comme une application Web permettant une manipulation simplifiée d'une base de donnée RDF basée sur l'ontologie générique de Mnémotix.

Dit autrement, Weever est un site Web à travers duquel il est possible de documenter la progression d'un ou plusieurs projets au travers d'événements temporels liés a un carnet d'adresse et a un système de fichier :

weever-preview.png

Petit plus, il est possible de partager publiquement tout ou partie d'un projet. Un explorateur de données ouvertes est joint à l'application.

explorer-preview.png

A propos de ce dépôt

Ce dépôt contient le cœur de Weever, toute la logique permettant de démarrer une instance de l'application. Notez qu'elle reste une bibliothèque au sens Javascript, ou dit autrement une dépendance NPM. Il n'est donc pas possible de la démarrer en mode standalone.

Par contre, étant un moteur générique, il est possible de l'inclure dans un nouveau projet pour démarrer une nouvelle instance ad hoc. Cette nouvelle instance pourra :

  • être customisée graphiquement avec un thème de couleur à la carte.
  • être améliorée dans ses fonctionnalités grâce à un système d'injection de composants.

Une instance de démo existe pour montrer les fonctionnalités de base.

Identifiants de test : jean.demo@gmil.co/test

Manuel pour l'utilisateur

Pour les non-développeurs, le manuel d'utilisateur est disponible sur le wiki de l'instance de Démo.

Manuel pour le développeur

Le plus concret pour savoir comment structurer une instance Weever est de regarder dans le dépôt de Weever de démo.

Dépendances pré-requises

Weever est basé sur une liste de dépendances obligatoires dans sa partie backend et frontend.

Environnement logiciel

Weever est conçu pour interfacer un certain nombre de logiciels tiers.

  • Une base de données RDF GraphDB
  • Un moteur de recherche ElasticSearch dans lequel GraphDB indexe ses données via un certain nombre de connecteurs.
  • Un serveur d'authentification Keycloak
  • Un serveur de stockage de fichier en cloud Minio
  • Un serveur de téléversement optimisé de fichiers TusD
  • Un serveur de génération d'imagette Thumbor

Si cette liste peut paraître imposante, toute cette stack applicative (à part GraphDB) est facile à mettre en place via un fichier docker-compose moyennant le réglage de quelques variables d'environnements abordé dans une section suivante.

Back-End

La partie Back-End de Weever est piloté par la bibliothèque @synaptix/server anciennement nommée synaptix.js. Cette librairie est une boîte noire interfacée avec un bus sémantique Synaptix et permet de générer une API GraphQL assurant 99% de la gestion de donnée.

Cette API GraphQL permet de :

  • Requêter la base de données et l'index de l'application (Query au sens GraphQL)
  • Mettre à jour la base de données. (Mutation au sens GraphQL)
  • Gérer son compte utilisateur (connexion, déconnexion, création de compte, oubli de mot de passe). (Mutation au sens GraphQL)

Chaque requête GraphQL est contextualisée par un jeton de session (JWT) et un header HTTP précisant la langue de l'interface.

Le 1% restant est un petit ensemble de middlewares ExpressJS (l'API GraphQL étant un middleware particulier) permettant de renvoyer différentes choses comme des fichiers de langues ou des fichiers normalisés.

La liste de ces middlewares sont les fichiers dans le dossier /src/server/middlewares qui commencent par serve***

La documentation de @synaptix/server est ici.

Front-End

L'interface graphique de Weever s'appuie sur la librarie @synaptix/ui elle-même basée sur la libraire MUI.

@synaptix/ui offre des composants clé-en-main et connectés au modèle de données générique Mnémotix.

Note: La documentation de SynaptixUI va venir bientôt...

L'interfaçage de données avec l'API GrapQL s'appuie sur la librairie @synaptix/client (anciennement nommée synaptix-client-toolkit) elle-même basée sur la librairie @apollo/client.

D'autres librairie utilisant des contextes React sont déclarées en peerDependencies afin d'éviter des doublons et donc des erreurs. Notamment :

  • dayjs pour la gestion de date.
  • formik pour la gestion des formulaires.
  • react-i18next pour la gestion des labels i18n.
  • react-router pour la gestion des routes.

Package.json

En résumé, votre fichier package.json devra comporter les dépendances suivantes :

{
  "@apollo/client": "^3.4.8",
  "@emotion/react": "^11.9.0",
  "@emotion/styled": "^11.8.1",
  "@mnemotix/synaptix-client-toolkit": "^2.5.5",
  "@mnemotix/synaptix.js": "^4.12.1",
  "@mui/material": "^5.6.4",
  "@mui/styles": "^5.6.2",
  "@mui/system": "^5.6.4",
  "@synaptix/ui": ">=1.0.0",
  "dayjs": "^1.10.7",
  "formik": "2.2.9",
  "graphql": "15.7.2",
  "react": "17.0.2",
  "react-dom": "17.0.2",
  "react-i18next": "11.16.2",
  "react-router": "5.2.0",
  "react-router-dom": "5.3.0"
}

Note: La liste se trouve dans le champ peerDependencies du fichier /package.json

Environnement de développement

Weever est une application connectée à un écosystème logiciel (voir section Environnement logiciel), avant de démarrer une instance de développement, il faut s'assurer d'avoir réglé un certain nombre de variables d'environnements.

Ces variables sont à mettre dans un fichier .env à la racine du projet.

RDFSTORE_ROOT_URI=              # URI du endpoint GraphDB
RDFSTORE_REPOSITORY_NAME=       # Nom de la base recevant les données RDF
RDFSTORE_USER=                  # Identifiant  de l'utilisateur GraphDB ayant au RDFSTORE_REPOSITORY_NAME avec accès en R/W 
RDFSTORE_PWD=                   # Mot de passe de l'utilisateur GraphDB 

ES_MASTER_URI=                  # URI du endpoint "master" ES  
ES_CLUSTER_USER=                # Identifiant  de l'utilisateur ES
ES_CLUSTER_PWD=                 # Mot de passe de l'utilisateur ES
INDEX_VERSION=NATIVE            # Laisser NATIVE pour bypasser RabbitMQ et utiliser le client ES natif développé en NodeJS
INDEX_PREFIX_TYPES_WITH=        # Préfix [optionel] utilisé pour tous les indices ES

OAUTH_BASE_URL=                 # URI du endpoint Keycloak
OAUTH_ADMIN_USERNAME=           # Identifiant d'un utilisateur ayant des droits d'administrateur
OAUTH_ADMIN_PASSWORD=           # Mot de passe d'un utilisateur ayant des droits d'administrateur
OAUTH_REALM=                    # Identifiant du Realm 
OAUTH_REALM_CLIENT_ID=api       # Identifiant  du client OpenID, laisser "api" 
OAUTH_REALM_CLIENT_SECRET=      # Mot de passe du client OpenID

Pour prendre en compte ces variables, vous pouvez copier-coller le répertoire /launcher dans votre projet. Ce répertoire contient (entre autre) deux sous répertoires /conf/docker/local et /conf/docker/remote contenant des configuration docker-compose pour démarrer une stack de développement. Le premier (local) démarre une stack entièrement locale, le second démarre une stack se connectant sur un serveur GraphDB, un moteur ES, et un SSO keycloak distants.

L'éxécutable /launcher/console.sh permet dans lancer les deux stacks.

REMOTE_MODE=(1|0) ./launcher/console.sh start

Notez qu'il faut lancer cette commande à la première installation

./launcher/console.sh install

Installation de données préliminaires

Un certain nombre de choses ne sont pas encore automatisées et nécéssite une intervention manuelle.

Créer une nouvelle base GraphDB et charger les ontologies

Dans le Workbench GraphDB, créer un nouveau "repository" qui prendra le même nom que la variable d'environnement RDFSTORE_REPOSITORY_NAME ainsi que qu'un utilisateur associé en Read/Write dont les identifiants auront les mêmes valeurs que les variables RDFSTORE_USER et RDFSTORE_PWD.

Attention, dans le formulaire de création de répo, ne pas oublier de sélectionner le "Ruleset Mnémotix" rdfs-plus.mnx.pie

screen-repo-creation.png

Une fois le répo créé, charger les ontologies disponibles dans le dossier /documentation/data/graphdb.

Ontologies standardisées :

  • foaf.ttl
  • geonames.ttl
  • prov.ttl
  • sioc.ttl
  • skos.ttl

Ontolologies Mnémotix :

  • mnx.ttl
  • mnx-project.ttl
  • mnx-skos.ttl
Instantier les connecteurs ES

Lancer la commande :

yarn data-index -a

Elle aura pour effet de créer un connecteur GraphDB -> ES par type RDF utilisé par Weever. Un connecteur verse ses données dans un index ES préfixé pa valeur de la variable d'environnement INDEX_PREFIX_TYPES_WITH

Créer un Realm Keycloak

Compléter le template disponible /documentation/templates/realm.json et charger le dans l'interface de création de Realm Keycloak.

Mettre sur pied une instance Weever

Weever Core expose un certain nombre d'éléments pour démarrer une instance Weever rapidement.

Préparation

Weever Core expose l'utilitaire /src/server/launch.js pour lancer le backend de Weever.

L'exemple de lancement le plus simple possible se trouve dans le fichier entrypoint de l'instance Weever Démo.

import { launch } from "@mnemotix/weever-core/server";
import Package from "../../package.json";
import environmentDefinition from "./config/environment";
import webpackConfig from "../../webpack.config";

launch({
  Package,
  environmentDefinition,
  locales: {},
  webpackConfig
});
  • Package fait référence au fichier package.json importé dans un objet JS
  • environmentDefinition est un fichier surchargeant (et ajoutant) la définition des variables d'environnement par défaut et disponibles dans le fichier /src/server/config/environment.js (la description des variables est définie dans le fichier).

Note: Il est possible de surcharger les variables directement dans le fichier .env.

  • locales est un objet JS au format i18n permettant de surcharger les fichiers de langues par défaut et disponibles dans le fichier /src/locales/index.js

Note: Le premier niveau de clés cet objet est la liste des langues disponible. Pour l'instant fr|en.

  • webpackConfig est un objet JS au format Webpack explicité ci-dessous. Webpack est le bundler retenu pour générer (et optimiser) les fichiers JS/CSS/Assets à envoyer au navigateur Web pour démarrer le Front-End de Weever

L'exemple le plus minimal est celui-ci :

const path = require("path");
const { generateWebpackConfig } = require("@mnemotix/weever-core/server");

module.exports = generateWebpackConfig({
  distPath: path.resolve(__dirname, "dist"),
  entries: [
    {
      name: "weever",
      entry: path.resolve(__dirname, "./src/client/launchWeever.js"),
      html: {
        template: path.resolve(__dirname, "./src/client/index.tpl.html"),
        filename: "index.html",
      },
    }
  ],
});

Weever Core expose la méthode utilitaire generateWebpackConfig qui attend le paramètre distPath (répertoire dans lequel générer les fichiers compilés et assemblés) et entries (les points d'entrées du code Front-End JS).

L'exemple ci-dessus va générer dans le répertoire /dist les assets (JS/CSS/Images/Font/autres) compilés et assemblés à partir du point d'entrée JS ./src/client/launchWeever.js injecté dans le template HTML ./src/client/index.tpl.html.

Le template HTML minimal ./src/client/index.tpl.html à créer est celui-ci :

<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
  </head>

  <body>
    <div id="react-root"/>
  </body>
</html>

La seule balise importante à ne pas omettre est <div id="react-root"/>. C'est la balise d'injection du code ReactJS définissant le code HTML Front-End de l'instance Weever.

Quant au point d'entrée JS de Weever ./src/client/launchWeever.js à définir, le code minimal est celui-ci :

import {launchWeever} from "@mnemotix/weever-core/launchWeever";
import {theme} from "./theme";
import possibleTypes from "./gql/possibleTypes.json";

launchWeever({
  possibleTypes,
  theme,
  extensions: []
});
  • possibleTypes est un object JS nécéssaire au client GraphQL Apollo pour résoudre les fragments GQL. Ce fichier doit être généré à chaque changement de définition du schéma GraphQL. Pour cela deux options :
    • Si votre modèle ne spécifie pas celui de base. Copier-coller le fichier /documentation/possibleTypes.json dans ./src/client/gql/possibleTypes.json fera l'affaire.
    • Sinon, appellez la commande yarn gql-fragments [chemin de votre définition de modèle] qui le fera pour vous.
  • theme est un objet JS définissant le thème graphique de l'application au format MUI.
  • extensions est la liste des extensions pour surcharger le code FrontEnd de l'application décrit dans Weeever Core. Voir la section Extensions dédiée.

Lancement

Une fois la préparation terminée. Deux méthodes de lancement.

babel-node ./src/server/entrypoint.js
  • Utiliser la librairie JS @babel/register et créer un fichier de transpilation intermédiaire à la racine du projet entrypoint.js
require("@babel/register");
require("./src/server/entrypoint");

Puis lancer la commande :

node ./entrypoint.js

Note : Tous les fichiers décrits dans la section Lancement et Préparation sont disponibles dans le dépôt de l'instance Weever de démo avec plus d'options et de détails (notamment l'intégration de Koncept dans les points d'entrée du fichier de configuration de Webpack)

Étendre Weever

Extensions

Comme évoqué dans la section précédente l'utilitaire @mnemotix/weever-core/launchWeever peut attendre le paramètre extensions.

C'est une liste d'instance d'objet Extension.

Un exemple étant le plus parlant, on peut reprendre l'exemple ci-dessus en ajoutant une extension :

import {ROUTES} from "@mnemotix/weever-core";
import {Extension} from "@synaptix/ui";
import {Route} from "react-router-dom";
import loadable from "@loadable/component";
import {launchWeever} from "@mnemotix/weever-core/launchWeever";
import {theme} from "./theme";
import possibleTypes from "./gql/possibleTypes.json";

// Routes are code splitted to decrease the size of initial laaded JS bundle file

const InjectedFragment = loadable(() => import("./components/InjectedFragment"));
const OverridedComponent = loadable(() => import("./components/OverridedComponent"));
const AddedComponent = loadable(() => import("./components/AddedComponent"));

const extension = new Extension({
  name: "MyCoolExtension",
  generateTopRoutes: ({isContributor, isAdmin, isEditor} = {}) => (
    <>
      <Route exact path={ROUTES["route_to_override"]} component={OverridedComponent} />
      
      <If condition={isContributor}>
        <Route exact path={"/new_route"} component={AddedComponent} />
      </If>
    </>
  ),
  fragments: {
    "Project.Heading.AfterTags": InjectedComponent,
  },
  settings: {
    "ProjectsListVisibleColumnNames": ["image", "title", "tags", "status", "updatedAt", "accessPolicy"]
  }
});

launchWeever({
  possibleTypes,
  theme,
  extensions: [extension]
});

Dans cet exemple, trois types d'extensions sont utilisées.

Surchage de haut niveau

La surchage complète et/ou ajout d'une route de haut niveau se fait dans le paramètre generateTopRoutes qui prend une fonction et attend une liste de routes à ajouter au début des routes déjà existantes. React Router s'arrêtant sur la première route matchant l'URL demandée, si deux routes avec le même chemin sont définies, la première l'emporte.

Surchage d'une portion de composant

La Surchage d'une portion de composant se fait dans le paramètre fragments qui est un object de clés/valeurs. Les clés étant les identifiants des points d'injection dans l'application et les valeurs, le composant React à monter.

Dans le code de Weever Core vous pouvez trouver des choses comme dans src/client/components/routes/Project/Project.js :

useExtensionFragment("Project.Heading.AfterTags", {projectId: "..."});

Il s'agit d'un point d'injection possible défini par l'identifiant Project.Heading.AfterTags.

Dans l'exemple ci-dessus, le composant InjectedComponent est monté sous la forme <InjectedComponent projectId="..."/> au niveau de ce point d'injection.

Le hook correspondant pour récupérer ce composant est exposé dans @synaptix/ui et s'appelle useExtensionFragment :

Surchage d'une configuration particulière

D'autres portions de l'application sont pilotées grâce à des configurations simples. Dans l'exemple plus haut la clé ProjectsListVisibleColumnNames concerne les colonnes visibles par défaut de la liste des projets.

Le hook correspondant pour récupérer cette valeur est exposé dans @synaptix/ui et s'appelle useExtensionSettings :

useExtensionSettings("ProjectsListVisibleColumnNames")

Readme

Keywords

none

Package Sidebar

Install

npm i @mnemotix/weever-core

Weekly Downloads

92

Version

2.1.1

License

Apache-2.0

Unpacked Size

162 MB

Total Files

5958

Last publish

Collaborators

  • ndelaforge
  • mrogelja
  • qrichaud.mnemotix