Library node
Resumen
Sección titulada «Resumen»El nodo Library es un tipo especial de nodo Action usado para definir bibliotecas de código reutilizables que otros nodos pueden usar en cualquier punto del flujo de trabajo. Los nodos Library favorecen la reutilización de código, el mantenimiento y la consistencia, al centralizar la funcionalidad común en un único lugar en vez de duplicar código entre varios nodos.
Un nodo Library se crea usando un nodo Action y contiene un script que define una clase u objeto con métodos y propiedades. La biblioteca se carga después en otros nodos mediante ScriptUtils.evaluateFromNode("library_name"), dejando su funcionalidad disponible durante toda la ejecución del flujo de trabajo.
Propiedades del nodo
Sección titulada «Propiedades del nodo»| Propiedad | Tipo | Descripción |
|---|---|---|
| Name | Texto | El nombre que identifica este nodo Library. Otros nodos usarán este nombre para cargar la biblioteca (por ejemplo, “library_global_base”, “library_request_constants”, “library_utils”). Use nombres descriptivos que indiquen claramente el propósito de la biblioteca. |
| Id | Número | Identificador único asignado al nodo. Este valor se genera automáticamente por el sistema e identifica de forma única al nodo dentro de la definición de proceso. |
| Description | Área de texto | Campo opcional para documentar el propósito de la biblioteca, las funciones que ofrece y ejemplos de uso. Es especialmente importante en las bibliotecas, para ayudar a otros desarrolladores a entender qué funcionalidad está disponible. |
| Source position | Desplegable | No aplica. Los nodos Library no tienen posiciones de origen ni destino, ya que no forman parte del flujo de ejecución del flujo de trabajo. Existen para aportar funcionalidad a otros nodos. |
| Target position | Desplegable | No aplica. Los nodos Library no tienen posiciones de origen ni destino. |
| Script definition | Editor de scripts | Contiene el código de la biblioteca que define clases, métodos y propiedades. El script debe devolver la clase u objeto que representa la biblioteca. Se puede acceder al editor de dos formas: - Área de texto: editar directamente en el campo de texto - Editor emergente: haga clic en el icono sobre el área de texto para abrir una ventana de edición más grande El editor admite autocompletado. Pulse Ctrl+Espacio mientras escribe para ver las opciones disponibles y sugerencias de código. |
Crear una biblioteca
Sección titulada «Crear una biblioteca»Una biblioteca se define creando una clase con métodos estáticos que aportan funcionalidad reutilizable. El script debe terminar con una sentencia return que devuelva la definición de la clase.
Ejemplo: biblioteca base
Sección titulada «Ejemplo: biblioteca base»El siguiente ejemplo muestra una biblioteca base completa llamada library_global_base que aporta funciones de utilidad comunes:
import com.openkm.sdk4j.impl.OKMWebservices;import com.openkm.sdk4j.bean.*;import com.openkm.okmflow.util.*;import com.openkm.okmflow.bean.*;import com.openkm.util.*;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.DeserializationFeature;import java.time.LocalDateTime;import java.time.Duration;
// Base library with common functions that can be used by all librariesclass BaseLibrary {
static void logInfo(String component, String message) { FileLogger.info(component, message); }
static void logError(String component, String message, Exception e = null) { FileLogger.error(component, message + "\n" + StackTraceUtils.toString(e)); }
static OKMWebservices getWebservices() { logInfo("BaseLibrary", "getWebservices"); return WebservicesHelper.getInstance(); }
static Object getContextValue(Map<String,Object> context, String key) { logInfo("BaseLibrary", "getContextValue"); return context.get(key); }
static void setContextValue(Map<String,Object> context, String key, Object value) { logInfo("BaseLibrary", "setContextValue"); context.put(key, value); }
// Get workflow initiator ID static String getInitiatorId(Map<String,Object> context) { logInfo("BaseLibrary", "getInitiatorId"); Actor actor = context.get("initiator"); return actor.getId(); }
// Get workflow initiator ID static long getProcessInstanceId(Map<String,Object> context) { logInfo("BaseLibrary", "getProcessInstanceId"); def procIns = context.get("processInstance"); return procIns.getId(); }
static String convertToJson(Object obj) { try { ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException e) { logError("BaseLibrary", "Error converting object to JSON", e); throw e; } }
static <T> T convertFromJson(String jsonString, Class<T> targetClass) { try { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper.readValue(jsonString, targetClass); } catch (JsonProcessingException e) { logError("BaseLibrary", "Error converting JSON to object of type: " + targetClass.getSimpleName(), e); throw e; } catch (IllegalArgumentException e) { logError("BaseLibrary", "Invalid JSON string or target class", e); throw e; } } static boolean hasDurationElapsed(String durationString, LocalDateTime startTime) { try { // Parse the ISO 8601 duration string Duration expectedDuration = Duration.parse(durationString);
// Get current time LocalDateTime currentTime = LocalDateTime.now();
// Calculate actual elapsed time since start time Duration elapsedDuration = Duration.between(startTime, currentTime);
// Check if elapsed time is greater than or equal to expected duration return elapsedDuration.compareTo(expectedDuration) >= 0;
} catch (Exception e) { logError("BaseLibrary", e.getMessage(), e); throw e; } }}
return BaseLibraryEsta biblioteca aporta varias funciones de utilidad:
- Funciones de registro:
logInfo()ylogError()para un registro consistente en todo el flujo de trabajo - Acceso a WebServices:
getWebservices()para obtener el cliente de la API de OpenKM - Gestión del contexto:
getContextValue()ysetContextValue()para trabajar con variables del flujo de trabajo - Información del flujo de trabajo:
getInitiatorId()ygetProcessInstanceId()para acceder a metadatos del flujo de trabajo - Utilidades JSON:
convertToJson()yconvertFromJson()para serialización JSON - Comprobación de duración:
hasDurationElapsed()para comprobar si ha transcurrido un periodo de tiempo
Cargar y usar una biblioteca
Sección titulada «Cargar y usar una biblioteca»Para usar una biblioteca en otro nodo (Action, Decision, asignación de Task, etc.), cárguela mediante ScriptUtils.evaluateFromNode("library_name") con el nombre del nodo Library:
import com.openkm.sdk4j.impl.OKMWebservices;import com.openkm.sdk4j.bean.*;import com.openkm.okmflow.util.*;import com.openkm.okmflow.bean.*;import com.openkm.util.*;import com.openkm.bean.form.*;
// Load libraryClass baseLibrary = ScriptUtils.evaluateFromNode("library_global_base");def initiatorId = baseLibrary.getInitiatorId(context);En este ejemplo:
- La biblioteca se carga mediante
ScriptUtils.evaluateFromNode("library_global_base") - La clase devuelta se guarda en la variable
baseLibrary - Se pueden llamar métodos de la biblioteca usando
baseLibrary.methodName()
Patrones habituales de biblioteca
Sección titulada «Patrones habituales de biblioteca»Biblioteca de constantes
Sección titulada «Biblioteca de constantes»Las bibliotecas pueden usarse para definir constantes empleadas en todo el flujo de trabajo:
class Constants { static final String CONTEXT_UUID = "uuid" static final String CONTEXT_INITIATOR = "initiator" static final String STATUS_PENDING = "PENDING" static final String STATUS_APPROVED = "APPROVED" static final String STATUS_REJECTED = "REJECTED"}
return ConstantsBiblioteca de utilidades
Sección titulada «Biblioteca de utilidades»Funciones de utilidad comunes para manipulación de cadenas, manejo de fechas o lógica de negocio:
class Utilities { static String formatCurrency(double amount) { return String.format("$%.2f", amount) }
static boolean isValidEmail(String email) { return email.matches(/^[A-Za-z0-9+_.-]+@(.+)$/) }
static String generateReference() { return "REF-" + System.currentTimeMillis() }}
return UtilitiesBiblioteca de lógica de negocio
Sección titulada «Biblioteca de lógica de negocio»Reglas de negocio complejas usadas en varios puntos de decisión:
class BusinessRules { static boolean requiresManagerApproval(double amount) { return amount > 1000.0 }
static boolean requiresDirectorApproval(double amount) { return amount > 10000.0 }
static String determineApprovalLevel(double amount) { if (requiresDirectorApproval(amount)) { return "DIRECTOR" } else if (requiresManagerApproval(amount)) { return "MANAGER" } else { return "SUPERVISOR" } }}
return BusinessRulesVentajas de usar bibliotecas
Sección titulada «Ventajas de usar bibliotecas»- Reutilización de código: escriba la funcionalidad común una vez y úsela en varios nodos
- Mantenimiento: actualice la lógica en un único lugar en vez de en varios nodos
- Consistencia: garantice que se aplica la misma lógica de forma consistente en todo el flujo de trabajo
- Capacidad de prueba: las funciones de biblioteca pueden probarse de forma independiente
- Legibilidad: los scripts de los nodos del flujo de trabajo quedan más limpios y enfocados
- Modularidad: separe responsabilidades en bibliotecas especializadas
Buenas prácticas
Sección titulada «Buenas prácticas»- Use nombres de biblioteca descriptivos que indiquen su propósito (por ejemplo,
library_global_base,library_invoice_utils) - Anteponga
library_a los nombres de biblioteca para distinguirlos de los nodos Action normales - Documente cada función de biblioteca con comentarios que expliquen los parámetros y los valores de retorno
- Mantenga las bibliotecas enfocadas: cree bibliotecas separadas para distintas áreas funcionales
- Use métodos estáticos para funciones de utilidad que no necesiten estado de instancia
- Incluya gestión de errores y registro en las funciones de biblioteca
- Pruebe a fondo las funciones de biblioteca antes de usarlas en flujos de trabajo de producción
- Versione sus bibliotecas al hacer cambios significativos
- Documente las dependencias entre bibliotecas si una biblioteca usa otra
- Mantenga el código de las bibliotecas eficiente: estas funciones pueden llamarse muchas veces durante la ejecución del flujo de trabajo