@dso-console/hooks
TypeScript icon, indicating that this package has built-in type declarations

7.0.0-rc • Public • Published

Plugin manager

Présentation

Le plugin manager ou communément appelés "hooks" et une interface permettant au serveur d'api (apps/server) de déclencher des hooks "déclencheurs / événements" pour que les plugins puisset piloter et configurer des outils.

Les plugins obligatoires de l'application sont souvent appelés corePlugins

L'objectif est que le server soit totalement agnostique des actions des plugins et même de leur existence. De plus le serveur d'API essayera au démarrage de charger de charger des plugins dans un dossier spécifiques, on les distingue en les appelant externalPlugins

Développement de plugin

Cette section est dédié à tous ceux voulant améliorer les corePlugins ou en crée de nouveaux.

Pour commencer je vous conseille d'aller regarder le (../../plugins/vault)[plugin vault]

Interface

Tout paquet existant devrait avoir un export nommé plugin et de type Plugin

Ce typage vous assurera de bien fournir toutes les clés nécessaire au bon fonctionnement du plugin

index.ts

import type { Plugin } from '@dso-console/hooks'
import infos from './infos.js'
import monitor from './monitor.js'

export const plugin: Plugin = {
  infos,
  subscribedHooks: {},
  monitor,
}

Infos

Ce sont les infos de bases de votre application

infos.ts

import type { ServiceInfos } from '@dso-console/hooks'

const infos: ServiceInfos = {
  name: 'my_plugin', // il serait bien que ça ne change jamais, imaginez que c'est un identifiant unique.
  to: ({ project, organization }) => `${process.env.MON_URL}/${organization}/${project}`,
  title: 'Mon super plugin',
  imgSrc: 'https://un_lien_vers/image_externe.svg/', // préférez le svg
  description: 'La description générale de mon plugin',
}

export default infos

La fonction to peux renvoyer une String ou un objet contenant une clé to et potentiellement d'autres informations ou encore un tableau de cet objet

// Valid
const to1 = () => 'une url'
const to2 = () => { to: 'url', title: 'Un titre', description: 'description', imgSrc: 'url' }
const to3 = () => [
  { to: 'url générale', title: 'Service générale', description: 'description générale' },
  { to: 'url1', title: 'Service 1', description: 'description 1' },
  { to: 'url2', title: 'Service 2', description: 'description 2' },
]
// Invalid
const to3 = () => ['url', 'url1', 'url2']

Monitoring

Pour offrir un (et un seul) service de monitoring sommaire de l'outil que manipulez vous pouvez la classe Monitor et l'initialiser avec une fonction dont vous aurez seul la maitrise. Cette fonction sera éxécuté par un setInterval toutes les 5 min ou selon le temps en miliseconds que vous aurez fourni

monitor.ts

import { type MonitorInfos, MonitorStatus, Monitor } from '@dso-console/shared'

const monitor = async (instance: Monitor): Promise<MonitorInfos> => {
  instance.lastStatus.lastUpdateTimestamp = (new Date()).getTime()
  // Votre fonction ne devrait jamais lever d'exception  
  try {
    // faites des trucs
    // mettez à jour les clés en fonction de votre résultat:
    // instance.lastStatus.message = 'Tout va bien'
    // instance.lastStatus.status = MonitorStatus.OK
  } catch (error) {
    instance.lastStatus.message = 'Error lors la requete'
    instance.lastStatus.status = MonitorStatus.UNKNOW
    // la clé cause n'est pour l'instant jamais retourné à l'utilisateur ni stocké, ça sera pour une prochaine PR
    instance.lastStatus.cause = error
  }
  // c'est bien de le retourner mais on s'en fiche un peu
  return instance.lastStatus
}

export default new Monitor(monitor)

SubscribedHooks

Pour informer le plugin manager sur quels hooks vous voulez éxecutez une fonction vous devez fournir un objet qui aura cette structure

  subscribedHooks: {
    createProject: {
      steps: { 
        pre: createDsoProjectFirst,
        post: createDsoProjectLast,
      },
    },
    archiveProject: {
      steps: { post: archiveDsoProject },
    },
  },

Fonctions (StepCalls)

Pensez à typer vos fonctions comme suit pour TS soit capable de vous notifier si associer un mauvais payload à un hook.

Vos fonctions ne doivent pas non plus lever d'exceptions non gérés.

Et elles doivent toute retourner un status !

Vous pouvez aussi retourner des clés supplémentaires. Ces clés sont accessibles par tous les plugins dans payload.results[nom_du_plugin].

Attention chaque fonction écrase le resultat de la step d'avant

export const createDsoProjectFirst: StepCall<CreateProjectExecArgs> = async (payload) => {
  try {
    // fais des trucs
    // oh j'ai un payload typé !
    return {
      status: {
        result: 'OK'
        message: 'ça s\'est bien passé' // optionnel si OK
      },
      foo: {
        bar: 'complétement facultatif'
      }
    }
  } catch (error) {
    return {
      status: {
        result: 'KO';
        message: 'Ouille !' // Obligatoire pour explique ce qui n'a pas réussi'
      },
    }
  }
}

export const createDsoProjectLast: StepCall<CreateProjectExecArgs> = async (payload) => {
  try {
    // fais des trucs
    // oh j'ai un payload typé !
    return {
      status: {
        result: 'OK'
        message: `${payload.results.my_plugin.foo.bar} a vraiment bien été crée` // optionnel si OK
      },
      une_clé: {
        newProjectName: 'complétement facultatif'
      }
    }
  } catch (error) {
    return {
      status: {
        result: 'KO';
        message: 'Ouille !' // Obligatoire pour explique ce qui n'a pas réussi'
      },
    }
  }
}

Apis

Il n'existe pour l'instant qu'un seul plugin qui exporte une API pour les autres plugins. C'est Vault ! D'autres viendront aider à la séparation des responsabiltés plus tard, stay tuned !

Vous pouvez comme lui déclarer des apis sur des hooks. Pour l'uniformite déclarer que c'est un extends de PluginApi (qui est vide)

api.ts

import { PluginApi } from '@dso-console/hooks'

export class ClusterApi extends PluginApi {} 
  subscribedHooks: {
    createCluster: {
      api: (args) => new ClusterApi(args.label, args.privacy),
      steps: { post: createCluster },
    },
    deleteCluster: {
      api: (args) => new ClusterApi(args.label),
    },
  },

Dépendances entre les plugins

La section précédente est bien sympathique mais en l'état un autre plugin n'a aucune conscience des apis autres plugins.

Disclaimer: Les plugins peuvent se parler entre eux mais attention ils ne s'importent jamais les uns les autres. C'est le Plugin Manager qui est en charge de fournir tous les objets nécessaires et de faire passe plat.

Pour y arriver il va falloir deux étapes

  1. Le plugin qui expose l'api doit faire un declare module
declare module '@dso-console/hooks' {
  interface HookPayloadApis<Args extends DefaultArgs> {
    vault: Args extends CreateClusterExecArgs | DeleteClusterExecArgs
    ? ClusterApi
    : undefined
  }
}
  1. Le module l'utilisant doit importer les types

package.json

{
  "devDepencies": {
    "my_plugin": "1.2.3"
  }
}

src/env.d.ts

/// <reference types="my_plugin/types/index.d.ts" />

Conclusion

N'hésitez pas à ouvrir des issues si ce n'est pas clair et bon développement !

Readme

Keywords

none

Package Sidebar

Install

npm i @dso-console/hooks

Weekly Downloads

3

Version

7.0.0-rc

License

none

Unpacked Size

49.4 kB

Total Files

32

Last publish

Collaborators

  • this-is-tobi
  • nonot