Intégration de l'OIP Core V3
A la lecture de cette documentation, si vous remarquez des oublis ou des sections à détaillées, n'hésitez pas à contacter l'équipe OIP
Si vous avez choisi de faire la migration vers l'OIP Core v3 en conservant votre code base actuelle (basée sur une version de l'OIP starter v2), alors vous êtes au bon endroit.
Cette documentation vous guidera dans chaque étape de votre migration pour rendre votre projet compatible avec l'OIP Core v3 et ses évolutions.
Même si vous n'allez pas utiliser le nouveau starter, il est important de lire les bonnes pratiques présentes dans la documentation
La branche v2-oip-core-v3 du repository fasst-oip-starter vous offre un exemple du résultat de l'implémentation de la migration de ce guide.
Les principales étapes de la migration
- Mise à jour des dépendances
- Mise à jour de l'environnement
- Migration du modèle de données OIP v2 vers OIP v3
- Migration du fichier de configuration OIP
- Migration des imports depuis @fasstech/oip-core
- Migration des fichiers de configuration des vues et des transitions des vues
- Migration de l'initialisation de l'OIP Core
- Migration des types graphQL côté backend pour supporter le nouveau schema du process OIP v3 et les changements des controllers OIP v3
- Migration coté frontend des queries et mutations graphQL liées au process, aux transitions et aux tâches et mise à jour de ProcessHandlers
- Migrations dans les composants des vues des appels aux handlers exposés par ProcessHandlers pour la gestion des transitions et des tâches
Mise à jour des dépendances
Dans le package.json de votre projet veuillez mettre à jour les deps :
- @fasstech/oip-core
- @fasstech/oip-tools
Pour chacune de ces dépendances veuillez installer la dernière version 3.x disponible
Pour lister les versions disponibles d'un package npm :
Veuillez installer également la dep @fasstech/oip-transition-manager
Environnement
Pour un support de l'OIP Core v3 voici les variables d'environnement à ajouter :
L'URL de OIP_MONGO_DB_URL est la même que la variable existante MONGO_DB_URL. Vous pouvez refactor votre code base pour n'utiliser que OIP_MONGO_DB_URL si vous le souhaitez.
CIPHER_IV et CIPHER_KEY sont des valeurs à définir propre à chaque OAV. cf documentation
La clé OIP_TOOLS_CLIENT_SECRET doit être demandée à l'équipe OIP.
Modèle de données
Propriétés supprimées
- Contact#email => remplacé par l'entité native Email
- Contact#isPEP => remplacé par AML#isPoliticallyExposed
- Contact#mobilePhoneIndicative => remplacé par l'entité native Phone
- Contact#mobilePhoneNumber => remplacé par l'entité native Phone
- Contact#phoneIndicative
- Contact#phoneNumber => remplacé par l'entité native Phone
- Customer#email => remplacé par l'entité native Email
- Customer#hasRelationPoliticallyExposed => remplacé par AML#isRelationPoliticallyExposed
- Customer#isMadelin
- Customer#isPoliticallyExposed => remplacé par AML#isPoliticallyExposed
- Customer#isTns => remplacé par l'entité native TNS
- Customer#mobilePhoneIndicative => remplacé par l'entité native Phone
- Customer#mobilePhoneNumber => remplacé par l'entité native Phone
- Customer#phoneIndicative
- Customer#phoneNumber => remplacé par l'entité native Phone
- Employee#firstname => remplacé par la propriété firstName
- Employee#lastname => remplacé par la propriété lastName
- Grantee#attachedSocialSecurity => remplacé par l'entité native SocialSecurity
- Grantee#isCCS
- GroupPension#optionalDeathCoverage
- Offer#type
- Scenario#annualPayment
- SeverancePayHypothesis#type
- TNS#hasRelationPoliticallyExposed => remplacé par AML#isRelationPoliticallyExposed
- TNS#isPoliticallyExposed => remplacé par AML#isPoliticallyExposed
OIP Config
Général
- Supprimer l'import des constantes viewIds et actionIds depuis le fichier views/ids.mjs (ce fichier n'est plus généré)
- Supprimer viewIds et actionIds du path views.config de l'objet de configuration
Déclaration des propriétés
- Remplacer schema par properties : toutes les entités, natives et personnalisées, utilisent la propriété properties pour définir les propriétés d'une entité.
- Supprimer generateAccessors: true.
- Supprimer les propriétés utilisant schemaOIP ; remplacer par une relation sur une entité (existante ou nouvellement créée).
Déclaration des types des propriétés
- Boolean => 'boolean'
- Date => 'date'
- Number => 'number'
- Object => 'object'
- String => 'string'
- [{ type: String }] => 'string[]'
- enum: MyEnum => enum: Object.values(MyEnum)
- { type: Array, items: { type: String, enum: MyEnum } } => { type: 'string[]', enum: MyEnum }
- Array => identifier dans le code le type sous-jacent : 'MyType[]'
V2
MyEntity: {
schema: {
customProp: { type: String }
}
}V3
MyEntity: {
properties: {
customProp: { type: 'string' }
}
}Déclaration des relations
Toutes les entités, natives et personnalisées, utilisent la propriété relations pour définir les relations entre entités ; les propriétés parents et multiple n'existent plus.
La déclaration d'une relation se fait uniquement depuis l'entité parente.
V2
MyEntity: {
parents: ['project'],
multiple: {
name: 'myentities'
}
}V3
Project: {
relations: {
MyEntity: ['multiple', 'myentities']
}
}Définition des tâches
Pour chaque tâche, la signature de la fonction de callback doit être modifiée.
Une tâche peut prendre en paramètre :
- metadata
- taskHandler expose deux méthodes :
- updateState permettant de mettre à jour le statut de la tâche
- get permettant de récupérer la tâche
OIP Core
Déclaration des imports
- getter / setter : @fasstech/oip-core/entities/Xyz
- enums : @fasstech/oip-core/entities/Xyz/enums
- enums générériques : @fasstech/oip-core/entities/enums
- process : @fasstech/oip-core/process
- validator : @fasstech/oip-core/entities/helpers/validator
- router : @fasstech/oip-core/routes
import { transitionRouter as router } from '@fasstech/oip-core/process'; // V2
// est remplacé par
import router from '@fasstech/oip-core/routes'; // V3- task : @fasstech/oip-core/task
import { runTaskCtrl as runTask } from '@fasstech/oip-core/controllers'; // V2
// est remplacé par
import { runTask } from '@fasstech/oip-core/task'; // V3- CustomProperties : @fasstech/oip-core/entities
- Entities, start : @fasstech/oip-core
Fonctions de lecture pour les entités
- get: T | null
- getOrCreate: T
- findAll: T[]
- findAllOrCreate: T[] // ne peut être vide
Les entités sont désormais considérées nativement comme des tableaux ; ceci entraine des modifications dans leur récupération.
Une entité non existante ne sera plus créée automatiquement lors d'un get à la place, il est conseillé d'utiliser getOrCreate
// V2
await Entities('Address').get('Customer');
// V3
await Entities('Address').getOrCreate('Customer');Pour récupérer une entité spécifique dans un tableau d'entité, il n'est plus possible d'utiliser l'id de l'entité, il faut désormais utiliser un filtre.
// V2
await Entities('Adress').get('Customer', addressId)
// V3
await Entities('Adress').get(
'Customer',
{ filter: ({ id }) => id === addressId }
);Filtre
La syntaxe pour filtrer des entités a changé. En V3, la syntaxe est :
// Entité Project
Entities('Project').get({ id: getProjectId(project) })
// Autres entités
Entities('Company').get(
project,
{ filter: ({ id }) => id === getCompanyId(company) }
);Modification de la configuration des vues
Modifications des fichiers de configuration YAML
L'option action n'est plus nécessaire dans les fichiers de configuration YAML ; cette notion est désormais définie dans les fichiers de configuration des transitions des vues.
Ajout des fichiers de configuration des transitions
Les fichiers transition.mjs et workflow.mjs doivent être créés à la racine du dossier contenant les fichiers de configuration des vues (viewsConfig).
Le fichier transition.mjs permet de définir les transitions entre les vues du projet. Chaque transition définie une vue source (srcViewId), une vue cible (tgtViewId) et une action ; un handler peut également être associé si la transition est conditionnelle.
export default [
{
srcViewId: 'id_de_la_vue_courante',
tgtViewId: 'id_de_la_vue_cible',
action: 'next', // 'next' ou 'prev'
conditionLabel: 'nom_de_la_condition', // par convention, le nommage suit le format `${srcViewId}_${action}${tgtViewId}`
conditionOrder: 1 // ordre d'exécution de la condition
}
]Le fichier workflow.mjs permet de définir le point d'entrée du parcours ainsi que la liste de toutes les vues.
export default {
rootView: 'home',
views: ['home', 'summary']
};Modification du script du build des vues
Suite à la mise à jour de la dep @fasstech/oip-tools en version 3, le script buildViews.sh à la racine de votre projet nécessite l'ajout de l'option --gql-client relay à l'appel de oip-tools views build
CLI_TIMEOUT=8
echo "[CLI] RUN BUILD VIEWS\n"
oip-tools views build --config ./viewsConfig --gql-client relay --extFileBackend mjs
echo "[SLEEP] $CLI_TIMEOUT seconds\n"
sleep $CLI_TIMEOUT
echo "[YARN] getSchema\n"
yarn getSchema
echo "[YARN] relay\n"
yarn relay
echo "[YARN] lint fix error\n"
yarn lint > /dev/nullLes fichiers qui ne sont plus générés en v3
Due à la nouvelle logique de transition apportée par l'OIP Core v3, le build des vues ne génère plus les dossiers et fichiers suivants :
- fichier views/ids.mjs
- fichier views/transitions.mjs
- les dossiers views/[my-view]/transitions
- le fichier views/_common/client/graphql/queries/TaskUpdateSubscription.js (est remplacé par views/_common/client/graphql/subscriptions/TaskSubscription.js)
- le fichier views/_common/client/hooks/useTaskUpdateSubscription.js (est remplacé par views/_common/client/hooks/useSubscription.js)
Une fois que vous avez terminé la migration de la configuration de vos vues, veuillez supprimer ces dossiers / fichiers
La nouvelle logique de transition
Voir les documentations pour davantage de détails :
- Transitions (OIP Core)
- Transitions (OIP par l'exemple)
- Transition (OIP Starter)
Migration du fichier server/app.mjs
Fichier server/app.mjs
- Supprimer l'import du fichier views/transitions.mjs (ce fichier n'est plus généré lors du build des vues)
- L'initialisation de l'OIP Core v3 doit être modifer :
// init OIP Core v3
await OIP.start('./oip.config.mjs');
await OIP.startTransitionManager();Migration des types graphQL côté server
L'OIP Core v3 apporte des changements au niveaux de ses controllers responsables de la gestion des transitions et des tâches. Par ailleurs le schéma de l'objet process accessible depuis le context graphQL de chaque resolver a été simplifié.
Par conséquent il est nécessaire de mettre à jour les types ainsi que les resolvers graphQL relatifs.
Process
Migration du type graphQL Process fichier server/graphql/type/ProcessType.mjs
L'objet process résolu par l'OIP Core v3 qui est accessible depuis le context de chaque resolver graphQL a été simplifié. Il ne contient que 2 champs : projectId et viewId
Exemple de la définition du type graphQL Process pour un support OIP Core v3 :
import { gql } from 'apollo-server-express';
const ProcessType = {
typeDefs: gql`
type Process {
projectId: ID
viewId: ID
}
`
};
export default ProcessType;Task
Migration du type graphQL Task fichier server/graphql/type/TaskType.mjs
Exemple de la définition du type graphQL Task pour un support OIP Core v3 :
import { prop } from 'ramda';
import { gql } from 'apollo-server-express';
const TaskType = {
typeDefs: gql`
scalar TaskMetadata
type TaskState {
status: String
statusCode: Int
}
type TaskHistory {
updatedAt: String
status: String
statusCode: Int
metadata: TaskMetadata
error: String
}
type Task {
id: ID
taskName: String
projectId: ID
metadata: TaskMetadata
error: String
state: TaskState
history: [TaskHistory]
}
`,
resolvers: {
Task: {
id: prop('_id')
}
}
};
export default TaskType;Query
Migration du type graphQL Query fichier server/graphql/type/QueryType.mjs
La définition de ces queries est requise :
- process permet de récupérer le process actuel
- task permet de récupérer les informations d'une tâche
Exemple de la définition de type Query pour un support OIP Core v3 :
import { ProcessResolver } from '../resolver/index.mjs';
import { gql } from 'apollo-server-express';
const QueryType = {
typeDefs: gql`
interface QueryResponse {
ok: Boolean!
error: String
}
type TaskQueryResponse implements QueryResponse {
ok: Boolean!
error: String
task: Task
}
type Query {
process: Process
task(taskName: String!, linkedToProject: Boolean): TaskQueryResponse
}
`,
resolvers: {
Query: {
process: (parent, _, context) => ProcessResolver(context).get(),
task: (parent, { taskName, linkedToProject }, context) => ProcessResolver(context).getTask({ taskName, linkedToProject })
}
}
};
export default QueryType;Mutation
Migration du type graphQL Mutation fichier server/graphql/type/MutationType.mjs
La définition de ces mutations est requise :
- action permet de déclancher une transition
- runTask permet de déclancher l'éxecution d'une tâche
- killCronTask permet de kill une tâche CRON
Exemple de la définition de type Mutation pour un support OIP Core v3 :
import { ProcessResolver } from '../resolver/index.mjs';
import { gql } from 'apollo-server-express';
const MutationType = {
typeDefs: gql`
scalar ActionContext
enum Action {
PREV
NEXT
}
interface MutationResponse {
ok: Boolean!
error: String
}
type ActionMutationResponse implements MutationResponse {
ok: Boolean!
error: String
process: Process!
}
type TaskMutationResponse implements MutationResponse {
ok: Boolean!
error: String
taskName: String
}
type Mutation {
action(action: Action!, context: ActionContext): ActionMutationResponse
runTask(taskName: String!, linkedToProject: Boolean, metadata: TaskMetadata): TaskMutationResponse
killCronTask(taskName: String!): TaskMutationResponse
}
`,
resolvers: {
Mutation: {
action: (parent, { action, context }, ctx) => ProcessResolver(ctx).action({ action, context }),
runTask: (parent, { taskName, linkedToProject, metadata }, context) => ProcessResolver(context).runTask({ taskName, linkedToProject, metadata }),
killCronTask: (parent, { taskName }, context) => ProcessResolver(context).killCronTask({ taskName })
}
}
};
export default MutationType;Subscription
Migration du type graphQL Subscription fichier server/graphql/type/SubscriptionType.mjs
La définition de la souscription taskSubscription qui vous permet de souscrire à la mise à jour d'une tâche est requise :
Exemple de la définition de type Subscription pour un support OIP Core v3 :
import { gql } from 'apollo-server-express';
import { withFilter } from 'graphql-subscriptions';
import { pubsub } from '../initGraphql.mjs';
const SubscriptionType = {
typeDefs: gql`
type Subscription {
taskSubscription(taskName: String!, projectId: String): TaskStateSubPayload
}
`,
resolvers: {
Subscription: {
taskSubscription: {
subscribe: withFilter(() => pubsub.asyncIterator('TASK_STATE_UPDATE'), (payload, variables) => {
if (variables.projectId) {
return payload.taskSubscription.taskName === variables.taskName &&
payload.taskSubscription.projectId === variables.projectId;
}
return payload.taskSubscription.taskName === variables.taskName;
})
}
}
}
};
export default SubscriptionType;ProcessResolver
Une fois les types graphQL mis à jour il vous faut éditer le resolver relatif ProcessResolver fichier server/graphql/resolver/ProcessResolver.mjs car les controllers exposés par l'OIP Core v3 ont évolués.
Les paramètres et le retour des méthodes qui sont appellées pour résoudre les queries et mutations doit correspondre aux définitions GraphQL relatives.
Exemple pour un support OIP Core v3 :
import { logger } from '@fasstech/logger';
import { KO, OK } from './helpers.mjs';
// import des controllers des transitions
import { next, prev } from '@fasstech/oip-core/process';
// import des controllers des tâches
import {
getTask as getTaskCtrl,
runTask as runTaskCtrl,
killTask as killTaskCtrl
} from '@fasstech/oip-core/task';
const ProcessResolver = ({
process,
sessionId
}) => (() => {
// récupération de l'id du projet actuel depuis le context graphQL
const { projectId } = process;
// get process
const get = () => {
logger.info(`[ProcessResolver][get] info: sessionId=${sessionId} projectId=${projectId}`);
return process;
};
// get task
const getTask = async ({ taskName, linkedToProject }) => {
logger.info(`[ProcessResolver][getTask] info: ${taskName}`);
try {
const task = await getTaskCtrl({
taskName,
projectId: linkedToProject ? projectId : null
});
return OK({ task });
} catch (err) {
return KO(err.message);
}
};
// action (trigger transition)
const action = async ({ action, context }) => {
logger.info(`[ProcessResolver][action] info: ${action}`);
const actions = {
'NEXT': () => next(projectId, context),
'PREV': () => prev(projectId, context)
};
try {
const newViewId = await actions[action]();
return OK({
process: {
projectId,
viewId: newViewId
}
});
} catch (err) {
return KO(err, { process });
}
};
// run task
const runTask = async ({ taskName, linkedToProject, metadata }) => {
logger.info(`[ProcessResolver][runTask] info: ${taskName}`);
try {
// not wait for the complete execution of the task, it can take time
runTaskCtrl({
taskName,
metadata,
projectId: linkedToProject ? projectId : null
});
return OK({ taskName });
} catch (err) {
return KO(err.message);
}
};
// kill task
const killCronTask = async ({ taskName }) => {
logger.info(`[ProcessResolver][killCronTask] info: ${taskName}`);
try {
await killTaskCtrl({ taskName });
return OK({ taskName });
} catch (err) {
return KO(err.message);
}
};
return {
get,
getTask,
action,
runTask,
killCronTask
};
})();
export default ProcessResolver;Migration des handlers côté client
Après avoir éditer le schéma graphQL et les resolvers côté back il vous faut appliquer les changements côté client dans le dossier app
Fichiers à migrer :
- les queries gql, dossier _graphql/queries
- les mutations gql, dossier _graphql/mutations
- le fichier Process.jsx
- le fichier withProcess.jsx
Fichiers à supprimer :
Vous pouvez supprimer le dossier app/_graphql/subscriptions, ce dossier est inutile. Un hook useSubscription est généré lors du build des vues (fichier views/_common/client/hooks/useSubscription.js) et vous permet d'initialiser une subscription gql depuis vos vues.
Migration des queries
Dossier app/_graphql/queries
La migration des queries se résume à mettre à jour les variables et les champs demandés en fonction de la définition des types gql relatifs.
Queries à migrer :
- process
- task (à vérifier selon votre code base)
Process
Exemple de migration du fichier de la query du process :
import { graphql } from 'react-relay';
const QProcessQuery = graphql`
query QProcessQuery {
process {
projectId
viewId
}
}
`;
const QProcess = {
query: QProcessQuery
};
export default QProcess;Task
Exemple de migration du fichier de la query d'une tâche :
import { graphql } from 'react-relay';
const TaskQuery = graphql`
query TaskQuery($taskName: String!, $linkedToProject: Boolean) {
task(taskName: $taskName, linkedToProject: $linkedToProject) {
ok
error
task {
id
taskName
projectId
metadata
error
state {
status
statusCode
}
history {
status
statusCode
updatedAt
metadata
error
}
}
}
}
`;
const Task = {
query: TaskQuery
};
export default Task;Migration des mutations
Dossier app/_graphql/mutations
La migration des mutations se résume à mettre à jour les variables et les champs demandés en fonction de la définition des types gql relatifs.
Mutations à migrer :
- action
- runTask
- killCronTask (à vérifier selon votre code base)
Action
Exemple de migration du fichier de la mutation action (transition) :
import {
commitMutation,
graphql
} from 'react-relay';
const mutation = graphql`
mutation ActionMutation($action: Action!, $context: ActionContext) {
action(action: $action, context: $context) {
ok
error
process {
projectId
viewId
}
}
}
`;
export default (environment, { action, context }, done) => {
const variables = { action, context };
commitMutation(
environment,
{
mutation,
variables,
updater: (store) => { },
optimisticUpdater: () => {
},
onCompleted: (response) => {
const { ok, error, process } = response.action;
done(ok, error, process);
},
onError: err => console.error(err)
}
);
};RunTask
Exemple du migration du fichier de la mutation runTask :
import {
commitMutation,
graphql
} from 'react-relay';
const mutation = graphql`
mutation RunTaskMutation($taskName: String!, $linkedToProject: Boolean, $metadata: TaskMetadata) {
runTask(taskName: $taskName, linkedToProject: $linkedToProject, metadata: $metadata) {
ok
error
taskName
}
}
`;
export default (environment, { taskName, linkedToProject, metadata }, done) => {
const variables = {
taskName,
linkedToProject,
metadata
};
commitMutation(
environment,
{
mutation,
variables,
updater: (store) => { },
optimisticUpdater: () => {
},
onCompleted: (response) => {
const { ok, error, taskName } = response.runTask;
done(ok, error, taskName);
},
onError: err => console.error(err)
}
);
};KillCronTask
Exemple de migration du fichier de la mutation killCronTask :
import {
commitMutation,
graphql
} from 'react-relay';
const mutation = graphql`
mutation KillCronTaskMutation($taskName: String!) {
killCronTask(taskName: $taskName) {
ok
error
taskName
}
}
`;
export default (environment, { taskName }, done) => {
const variables = { taskName };
commitMutation(
environment,
{
mutation,
variables,
updater: (store) => {},
optimisticUpdater: () => {},
onCompleted: (response) => {
const { ok, error, taskName } = response.killCronTask;
done(ok, error, taskName);
},
onError: err => console.error(err)
}
);
};Migration de Process.jsx
Les handlers de transitions et de la gestion des tâches sont définis dans le fichier app/Process.jsx
Ces handlers permettent la gestion des transitions et de tâches depuis les composants de vos vues wrapper par le HOC withProcess
Ces handlers appellent les queries et mutations relatives au process, aux tasks et aux transitions que vous venez de migrer.
La mirgation de ce fichier se résume donc à rendre compatible les handlers définis suite aux modifications apportées aux queries et mutations gql.
Exemple de migration du fichier Process.jsx :
import { ActionMutation, RunTaskMutation, KillCronTaskMutation } from '@@appMutations';
import { QProcess, Task } from '@@appQueries';
import Moment from 'moment';
import { isNil } from 'ramda';
import { useEffect, useRef, useState } from 'react';
import { fetchQuery, useRelayEnvironment } from 'react-relay';
Moment.locale('fr');
const Process = ({ children }) => {
const environment = useRelayEnvironment();
const [process, setProcess] = useState(null);
const processConfigurationRef = useRef();
const getProcess = async () => {
const response = await fetchQuery(environment, QProcess.query).toPromise();
return response.process;
};
const getTask = async ({ taskName, linkedToProject }) => {
const response = await fetchQuery(environment, Task.query, {
taskName,
linkedToProject,
}).toPromise();
return response.task;
};
useEffect(() => {
/* get process configuration */
async function fetchConfig() {
const response = await fetch('/process/configuration', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
processConfigurationRef.current = await response.json();
const process = await getProcess();
setProcess(process);
}
fetchConfig();
}, []);
if (isNil(process)) return null;
/**
* Trigger a transition
* @param {'NEXT'|'PREV'} action
* @param {object} context - any transition context (optional)
*/
const onAction = ({ action, context }) => {
ActionMutation(environment, { action, context }, (ok, error, process) => {
setProcess(process);
});
};
/**
* Trigger next transition
* @param {object} context - any transition context (optional)
*/
const onNext = ({ context } = {}) => {
ActionMutation(environment, { action: 'NEXT', context }, (ok, error, process) => {
setProcess(process);
});
};
/**
* Trigger prev transition
* @param {object} context - any transition context (optional)
*/
const onPrev = ({ context } = {}) => {
ActionMutation(environment, { action: 'PREV', context }, (ok, error, process) => {
setProcess(process);
});
};
/**
* Trigger task execution
* @param {string} taskName - related task name
* @param {boolean} linkedToProject - if the task is linked to current project or not (optional, true by default)
* @param {object} metadata - any metadata (optional)
*/
const onRunTask = ({ taskName, linkedToProject = true, metadata }) => {
RunTaskMutation(environment, { linkedToProject, taskName, metadata }, (ok, error, taskName) => {
if (ok && !error) {
console.log(`[RUN TASK] ${taskName}`);
return;
}
console.error(`[RUN TASK] ${taskName} error: ${error}`);
});
};
/**
* Kill a CRON task
* @param {string} taskName - related task name
*/
const onKillCronTask = ({ taskName }) => {
KillCronTaskMutation(environment, { taskName }, (ok, error, taskName) => {
if (ok && !error) {
console.log(`[KILL TASK] ${taskName} killed`);
return;
}
console.error(`[KILL TASK] ${taskName} error: ${error}`);
});
};
/**
* Get a task
* @param {string} taskName - related task name
* @param linkedToProject - if the task is linked to current project or not (optional, true by default)
* @return {Promise<Task|null>}
*/
const onGetTask = async ({ taskName, linkedToProject = true }) => {
const { ok, error, task } = await getTask({
taskName,
linkedToProject,
});
return ok && !error ? task : null;
};
/**
* Get task status
* @param {string} taskName - related task name
* @param linkedToProject - if the task is linked to current project or not (optional, true by default)
* @return {Promise<string|null>}
*/
const onGetTaskStatus = async ({ taskName, linkedToProject = true }) => {
const task = await getTask({ taskName, linkedToProject });
return task?.state.status;
};
/**
* Get task status code
* @param {string} taskName - related task name
* @param linkedToProject - if the task is linked to current project or not (optional, true by default)
* @return {Promise<number|null>}
*/
const onGetTaskStatusCode = async ({ taskName, linkedToProject = true }) => {
const task = await getTask({ taskName, linkedToProject });
return task?.state.statusCode;
};
return children({
context: process,
processConfiguration: processConfigurationRef.current,
processHandlers: {
onAction,
onNext,
onPrev,
onRunTask,
onGetTask,
onKillCronTask,
onGetTaskStatus,
onGetTaskStatusCode,
},
});
};
export default Process;Dans cet exemple, les handlers de transition sont définis comme suit :
- onAction pour déclencher une transition, paramètre action, valeur acceptée NEXT ou PREV
- onNext pour déclencher la transition vers la vue suivante
- onPrev pour déclencher la transition vers la vue précédente
Ces handlers prennent en paramètre optionnel un objet context qui représente le context de la transition (cf documentation sur les transitions v3)
Handler pour déclencher l'exécution d'une tâche : onRunTask
Paramètres :
- taskName le nom de la tâche
- linkedToProject si la tâche est liée au projet actuel ou pas (optionnel, true par défaut)
- metadata objet metadata (optionnel)
Les handlers pour récupérer les informations d'une tâche :
- onGetTask récupère les informations d'une tâche
- onGetTaskStatus récupère le statut d'une tâche
- onGetTaskStatusCode récupère le code du statut d'une tâche
Paramètres :
- taskName le nom de la tâche
- linkedToProject si la tâche est liée au projet actuel ou pas (optionnel, true par défaut)
Handler pour kill une tâche CRON : onKillCronTask, prend en paramètre taskName
Migration du fichier withProcess.jsx
Le HOC app/withProcess.jsx permet au composant passé en paramètre de recevoir les props suivantes :
- context objet qui contient les informations du process actuel
- processConfiguration tableau des vues disponibles
- processHandlers objet qui contient les handlers de transition et des tâches
Quelques modifications sont à apportées à ce fichier :
- suppression de l'import au fichier views/ids.mjs car ce fichier n'est plus généré par la dep @fasstech/oip-tools due à la nouvelle logique de transition de l'OIP Core v3
- suppression de l'export des constantes actionIds et viewIds
- mise à jour des types des propriétés relatives au context et à ProcessHandlers en conséquence des changements du fichier Process.jsx
Exemple de migration du fichier withProcess.jsx :
import React from 'react';
import PropTypes from 'prop-types';
import Moment from 'moment';
Moment.locale('fr');
const ProcessContext = React.createContext();
export default function withProcess (Component) {
return function ProcessComponent (props) {
return (
<ProcessContext.Consumer>
{({
context,
processConfiguration,
processHandlers
}) => <Component
{...props}
context={context}
processConfiguration={processConfiguration}
processHandlers={processHandlers}
/>}
</ProcessContext.Consumer>
);
};
}
const ContextPropTypes = PropTypes.shape({
viewId: PropTypes.string,
projectId: PropTypes.string
});
const ProcessConfigurationPropTypes = PropTypes.arrayOf(
PropTypes.shape({
path: PropTypes.string,
viewId: PropTypes.string
})
);
const ProcessHandlersPropTypes = PropTypes.shape({
onAction: PropTypes.func,
onNext: PropTypes.func,
onPrev: PropTypes.func,
onRunTask: PropTypes.func,
onGetTask: PropTypes.func,
onKillCronTask: PropTypes.func,
onGetTaskStatus: PropTypes.func,
onGetTaskStatusCode: PropTypes.func
});
export {
ContextPropTypes,
ProcessConfigurationPropTypes,
ProcessHandlersPropTypes,
ProcessContext
};Migration des composants des vues
Toujours côté client, à la suite de la migration des fichiers des chapitres précédents, il faut maintenant appliquer les changements dans les composants de vos vues. Dossiers views/[my-view]/client
Par rapport à une code base basée sur un starter v2 et si vous avez suivit la même implémentation comme expliqué dans les chapitres précédents, il vous faudra changer :
- les imports depuis @@app/withProcess
- les appels aux handlers de transitions et des tâches (prop processHandlers)
Les imports depuis withProcess
Au niveau des imports depuis @@app/withProcess les constantes actionIds et viewIds n'existent plus due à la nouvelle logique de transition de OIP Core v3 :
import withProcess, { actionIds, viewIds } from '@@app/withProcess';
const Home = ({ context, processHandlers }) => {
...
return (
<div className='t-home'>
<h2>{viewIds.HOME}</h2>
...
</div>
);
};
export default withProcess(Home);Les appels aux handlers de transition
Les changements par rapport à une code base v2 :
- l'handler onBack devient onPrev
- les paramètres possibles de onAction onNext onPrev ont évolués
Exemple de migration d'un appel à onAction pour une action NEXT :
import withProcess, { actionIds, viewIds } from '@@app/withProcess';
const Home = ({ context, processHandlers }) => {
const $onClick = (actionId) => () => {
processHandlers.onAction(viewIds.HOME, actionId);
};
return (
<div className='t-home'>
<h2>{viewIds.HOME}</h2>
...
<button className='f-button' onClick={$onClick(actionIds.HOME_NEXT)}>
NEXT
</button>
</div>
);
};
export default withProcess(Home);Les appels aux handlers des tâches
Les changements par rapport à une code base v2 :
- les paramètre de l'handler onRunTask ont changés
- l'handler getTask devient onGetTask et ses paramètres ont changés
- l'handler getTaskStatus devient onGetTaskStatus et ses paramètres ont changés
- l'handler getTaskStatusCode devient onGetTaskStatusCode et ses paramètres ont changés
- l'handler onKillCronTask ne change pas
Exemple de migration des appels aux handlers onRunTask et getTask :
import withProcess from '@@app/withProcess';
import { useState } from 'react';
const Home = ({ context, processHandlers }) => {
const [magicTask, setMagicTask] = useState({});
const { projectId, viewId } = context;
// RUN TASK
const onRunTask = () => {
processHandlers.onRunTask({
taskName: 'magicTask',
// si oui ou non la tâche à exécuter sera liée
// au projet actuel ou pas
// (paramètre optionnel, true par défaut)
linkedToProject: true,
});
};
// GET TASK INFO
const onGetTask = () => {
processHandlers
.onGetTask({
taskName: 'magicTask',
// si oui ou non la tâche à récupérer est liée
// au projet actuel ou pas
// (paramètre optionnel, true par défaut)
linkedToProject: true,
})
.then((task) => {
setMagicTask(task || {});
})
.catch((err) => {
console.error(err);
setMagicTask({});
});
};
return (
<div className='t-home'>
<h2>{viewId.toUpperCase()}</h2>
<div>
<p>Magic task:</p>
<pre>{JSON.stringify(magicTask, null, 2)}</pre>
</div>
<div>
<button className='f-button' onClick={onRunTask}>
RUN TASK
</button>
<button className='f-button ml-2' onClick={onGetTask}>
GET INFO
</button>
</div>
...
</div>
);
};
export default withProcess(Home);La logique de migration pour les handlers getTaskStatus et getTaskStatusCode est la même que pour l'handler getTask
Souscription au statut d'une tâche
Exemple de souscription gql depuis un composant d'une vue au statut d'une tâche :
import withProcess from '@@app/withProcess';
// import relatif à la souscription d'une tâche
import { TaskSubscription } from '@@views/_common/graphql/subscriptions/TaskSubscription';
import { useSubscription } from '@@viewCommonHooks/useSubscription';
const Demo = ({ context, processHandlers }) => {
// init task subscription
const { outputValue, errorValue } = useSubscription({
taskName: 'magicTask',
projectId: context.projectId
}, TaskSubscription);
// run task
const $onRunTask = () => {
processHandlers.onRunTask({ taskName: 'magicTask' });
};
return (
<div className="t-demo">
<h2>{context.viewId.toUpperCase()}</h2>
...
<div>
<pre>{JSON.stringify(outputValue || {}, null, 2)}</pre>
<pre>{JSON.stringify(errorValue || {}, null, 2)}</pre>
<button className="f-button" onClick={$onRunTask}>RUN TASK</button>
</div>
</div>
);
};
export default withProcess(Demo);