Registro de pasos de complemento de servidor
Id del esquema: plugin-step@1
Esquema legible por máquina: plugin-step.schema.json
El contenedor de registro canónico de un paso de complemento de servidor (sección 6.6): lógica del lado del servidor que se ejecuta cuando se crea, actualiza o elimina un registro de una entidad. El contenedor indica cuándo y cómo se ejecuta la lógica; una implementación en BS Lang incluye además el script en línea, que se valida con la gramática de BS Lang además de con este esquema. Un registro se valida de forma idéntica tanto si procede de un diseñador como de una llamada a la API, de un asistente de IA o de una importación de solución.
Forma
| Campo | Tipo | Obligatorio | Notas |
|---|---|---|---|
logicalName | string | sí | Con el prefijo del editor, en snake_case en minúsculas. |
displayName | string | sí | Nombre legible del paso. |
publisherPrefix | string | sí | El prefijo del editor propietario, que debe servir de espacio de nombres a logicalName; no el de la tabla observada. Una tabla estándar como account no lleva prefijo a propósito, y validar con ella hacía imposible dar nombre a un paso sobre una de esas tablas. |
entity | string | sí | La entidad cuyas escrituras desencadenan el paso. |
message | string | sí | Create, Update o Delete. |
stage | string | sí | PreValidation, PreOperation o PostOperation. |
executionMode | string | sí | Synchronous (en línea, dentro de la escritura) o Asynchronous (en cola a través de la bandeja de salida y ejecutado después de confirmarse la escritura). Un paso asíncrono debe ser PostOperation: se ejecuta cuando el registro ya está guardado, así que no le queda nada que rechazar. |
rank | integer | sí | Orden de ejecución entre los pasos de la misma entidad + mensaje + fase; el menor se ejecuta primero. |
isEnabled | boolean | sí | Un paso deshabilitado se almacena pero nunca se ejecuta. |
implementation | object | sí | Exactamente una implementación (vea más abajo). |
Implementación
| Campo | Tipo | Obligatorio | Notas |
|---|---|---|---|
type | string | sí | BsLangScript o DotNetAssembly. |
script | string | cuando es BsLangScript | El código fuente de BS Lang. |
assemblyReference | string | cuando es DotNetAssembly | La clave de almacenamiento del ensamblado cargado. |
typeName | string | cuando es DotNetAssembly | El tipo de complemento que se instancia. |
Fases
- PreValidation: se ejecuta antes de la operación; comprobaciones generales de forma y de seguridad.
- PreOperation: se ejecuta justo antes de la escritura; puede cambiar valores de campos del registro o rechazar la operación entera.
- PostOperation: se ejecuta justo después de la escritura; para cambios en cascada o dependientes.
Un paso rechaza una operación llamando a throwValidationError("reason"), lo que detiene la
escritura y devuelve el motivo al llamador.
Ejemplo
{
"logicalName": "par_invoice_reject_negative",
"displayName": "Reject negative invoice total",
"entity": "par_invoice",
"message": "Create",
"stage": "PreOperation",
"executionMode": "Synchronous",
"rank": 1,
"isEnabled": true,
"implementation": {
"type": "BsLangScript",
"script": "if (getValue(\"par_total\") < 0) { throwValidationError(\"Total cannot be negative.\"); }"
}
}
Funciones de host para scripts de BS Lang
Un paso BsLangScript lee y cambia el registro mediante estas funciones de host:
| Función | Finalidad |
|---|---|
getValue("field") | El valor actual de un campo del registro que se está escribiendo. |
setValue("field", value) | Cambia el valor de un campo (solo se tiene en cuenta en PreOperation). |
getPreValue("field") | El valor anterior del campo (en actualizaciones y eliminaciones). |
throwValidationError("message") | Rechaza la operación y la revierte. |
message() / stage() / entityName() | La operación, la fase y la entidad actuales. |
userId() / depth() | El usuario que la desencadenó y la profundidad de recursión. |
Acceder a otras tablas
Las funciones anteriores solo ven el registro que se está escribiendo. Las siguientes acceden al resto del entorno, y todas ellas se ejecutan como el usuario que desencadenó el paso: un script no es una forma de saltarse un permiso, y un paso cuyo llamador no puede leer una tabla no obtiene nada de ella.
| Función | Finalidad |
|---|---|
getRecord("table", id) | Un registro como objeto, o vacío si no existe. Incluye .id. |
queryRecords("table", "field", value) | Las filas en las que un campo es igual a un valor, como una lista de objetos. Limitado a 200. |
runQuery({ … }) | Ejecuta un documento parsware-query@1 completo: filtros and/or anidados con diez operadores, combinaciones, ordenaciones, paginación o un aggregate que devuelve grupos. El mismo lenguaje de consulta que genera Búsqueda avanzada y que almacena una consulta guardada. Filas limitadas a 200; un agregado no. Una columna combinada se lee con su alias (row.acct.par_industry), y el 'from' de un vínculo debe ir entre comillas porque from es una palabra clave de BS Lang. |
runSavedQuery("name") | Ejecuta una consulta guardada por su nombre lógico. La complejidad reside en un diseñador y viaja en la solución, así que el script solo nombra la pregunta. |
countRecords("table", "field", value) | Cuántos hay, sin obtenerlos: un COUNT real, así que es correcto por encima del límite de 200 filas que acota queryRecords. |
createRecord("table", { field: value, … }) | Crea un registro y devuelve su id. |
updateRecord("table", id, { field: value, … }) | Cambia los campos indicados, y solo esos. |
deleteRecord("table", id) | Elimina un registro. |
Una escritura hecha de esta forma ejecuta las reglas habituales, así que un paso registrado en
la tabla que se escribe se ejecutará a su vez. Ese anidamiento está limitado a una profundidad de
ocho y depth() indica a un script dónde se encuentra, pero el límite es una red de
seguridad, no un diseño. Un paso que escribe en la tabla que observa se desencadenará a sí mismo,
y la solución es una condición o una tabla distinta.
Importar lógica compartida
El script de un paso puede importar con import un script de solución por su nombre,
exactamente igual que una regla de negocio de cliente, de modo que una regla que aplican varios
pasos se escribe una sola vez:
import 'par_approval_rules';
if (getValue('par_total') > par_approval_rules.limit()) { … }
Funcionan los dos estilos de comillas. El módulo debe ser un script de solución del mismo entorno; cualquier otra cosa falla al guardar el paso, no al ejecutarlo.