Operation


    Consulter la Javadoc pour plus de précisions sur cette API

    Un peu de vocabulaire :

    • Operation : Action sur un objet du domaine
    • OperationHandler : Fragment de code exécuté avant ou après l’opération
    • OperationHook : OperationHandler exposé comme service REST

    L’abonnement à une opération passe par la création d’un document de classe OperationHandlerRegistration. Les tags référencés par cette classe permettent de configurer l’abonnement :

    Tag Type Description
    OperationHandler string Classe Java de l’OperationHandler ou l’URL d’un OperationHook
    ExecutionPhase choicelist Phase d’exécution de l’opération
    Action choicelist Action de l’opération
    ObjectType choicelist Type d’objet auquel réagir
    Enabled boolean Détermine si l’abonnement est actif ou inactif
    Asynchronous boolean Détermine si l’OperationHandler doit être exécuté de manière asynchrone ou non
    RegistrationOrder integer Ordonnancer les différents abonnements à une même opération
    StopOnException boolean Détermine si l’exécution de l’opération doit être stoppée en cas d’exception (seulement si synchrone)
    Authorization string chaîne d’autorisation basic à fournir au hook (générer en ligne grâce à blitter)

    Flower fournit nativement quelques OperationHandler comme le :

    • com.flower.docs.core.tsp.operation.LogOperationHandler : permet de logger le contexte pour lequel il est exécuté
    • com.flower.docs.core.tsp.operation.DroolsOperationHandler : permet d’appliquer des règles métiers (à travers une table de décision Drools)

    Développement

    En plus de ceux fournis nativement, il est possible de développer son propre OperationHandler.

    package com.xxx.sample;
    
    import org.springframework.stereotype.Component;
    import com.flower.docs.operation.api.OperationContext;
    import com.flower.docs.operation.api.OperationHandler;
    
    @Component
    public class OperationHandlerSample implements OperationHandler
    {
        public void process(OperationContext context)
        {
        }
    }
    

    Pour que cet OperationHandler puisse être utilisé par Flower, il est nécessaire que :

    • la classe soit présente dans le classpath de la JVM
    • que le package du handler (ici com.xxx.sample) soit scanné par Spring


    Une fois ces deux étapes réalisées, l’OperationHandler sera appelé conformément aux abonnements définis.

    DroolsOperationHandler

    Cet OperationHandler s’appuie sur le moteur de règle Drools et une table de décision (fichier MS Excel) afin de détermine les règles à appliquer.

    Une table de décision est composée de deux types de colonnes :

    • les conditions : détermine les cas dans lesquel appliquer une règle
    • les actions : les actions à exécuter pour une règle donnée

    Au sein d’une table de décision, il est possible d’utiliser les méthodes suivantes à partir de l’objet util :

    Tag Description
    getDocumentService() Récupère le service de gestion de document
    getFolderService() Récupère le service de gestion de dossier
    getTaskService() Récupère le service de gestion de tâche
    getVirtualFolderService() Récupère le service de gestion de dossier virtuel
    getService(Component component) Récupère le service de gestion de composant
    getClassService(Component component) Récupère le service de gestion des classes de composant
    getTagClassService() Récupère le service de gestion des classe de tags
    getAclService() Récupère le service de gestion d’ACL
    getFactDAO() Récupère la DAO de gestion de l’historique
    create(Component component) Crée le composant fourni en entrée et retourne celui réellement créé
    update(Component component) Modifie le composant fourni en entrée
    changeClass(Component component, String classId) Modifie la classe du component fourni en entrée et propage uniquement les tags en commun entre la classe initiale et la nouvelle
    getClassId(Component component) Récupère la valeur de la classe du composant sinon vide
    setClassId(Component component, String classId) Définie la valeur de la classe du composant
    getStatus(Component component) Récupère la valeur du statut du composant
    setStatus(Component component, Status status) Définie la valeur du statut du composant
    getTagValues(Component component, String tagName) Récupère la liste de valeurs d’un tag sinon null
    setTagValues(Component component, String tagName, List<String> values) Définie la liste de valeurs d’un tag en modifiant sa valeur s’il existe, sinon en ajoutant un tag
    setTagReadOnly(Component component, String tagName, boolean readonly) Modifie un tag en le mettant en lecture seule ou lecture écriture
    addTagValues(Component component, String tagName, List<String> values) Ajoute une liste de valeurs à un tag existant ou ajoute le tag
    getAnswerId(Task task) Récupère la dernière réponse appliquée sur une tâche
    log(String message) Affiche dans les logs un message préfixé par [Drools] en INFO

    Exemple

    Dépendances Maven

    <dependency>
    	<groupId>com.flower.docs.core</groupId>
    	<artifactId>flower-docs-ws-client</artifactId>
    	<version>2.4.4</version>
    </dependency>
    <dependency>
    	<groupId>com.flower.docs.apis</groupId>
    	<artifactId>flower-docs-operation-api</artifactId>
    	<version>2.4.4</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-web</artifactId>
    	<version>2.1.5.RELEASE</version>
    </dependency>
    

    Hook

    @RestController
    @RequestMapping("/log")
    public class SampleOperationHook extends OperationHook
    {
        private static final Logger LOGGER = LoggerFactory.getLogger(SampleOperationHook.class);
    
        @Override
        public void process(OperationContext context) throws TechnicalException, FunctionalException
        {
            LOGGER.info("Processing context={}", context);
        }
    }
    

    Sécurité

    Accès au hook

    Les OperationHook peuvent être sécurisés et Flower peut les consommer en fournissant une authentification BASIC.

    Pour mettre en place cette authentification, il est nécessaire de définir une configuration de sécurité Spring.


    SecurityConfig.java

    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http.csrf().disable()
                .exceptionHandling()
                .and()
                .formLogin().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().httpBasic()
                .and().authorizeRequests().anyRequest().authenticated();
        }
    }
    

    Ensuite, il suffit de définir l’utilisateur pouvant consommer le service REST dans le fichier application.properties :

    spring.security.user.name=toto
    spring.security.user.password=1234
    

    Pour que Flower fournisse l’authentification définie, il est nécessaire de mettre à jour l’abonnement avec la chaîne d’autorisation basic à fournir (générer en ligne grâce à blitter)

    Configuration

    • accès au Core ws.url

    Gestion des erreurs

    Par défaut, lors de l’exécution d’un OperationHook, le Core Flower log les erreurs renvoyées en parsant le corps de la réponse HTTP. Pour renvoyer des exceptions Flower adaptées en fonction du contexte, il faut ajouter dans un objet instancié par Spring le code suivant :

    @ExceptionHandler(CodeBasedException.class)
    public ResponseEntity<Object> handleCustomException(CodeBasedException ex, WebRequest request)
    {
        HttpHeaders headers = new HttpHeaders();
        headers.add("code", ex.getCode());
        headers.add("message", ex.getMessage());
        return new ResponseEntity<Object>(headers, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    


    Dans le cas d’OperationHook synchrones, il est possible de renvoyer des messages d’erreurs personnalisés à l’utilisateur final. Pour cela, il est nécessaire d’utiliser le code d’erreur F00039 :

    throw ExceptionBuilder.createFunctionalException(F00039, "", "Custom error message");