Invocación de funciones externas utilizando expresiones

Se pueden encontrar expresiones en varios lugares de un script para definir el comportamiento de los bucles, las condiciones, etc. Vea el apéndice de sintaxis de expresiones en la guía Authoring Scripts using Intelligent Evidence Gathering (IEG) para realizar consultas.

Estas expresiones pueden hacer referencia a respuestas, pueden combinarlas utilizando diversos operadores e incluso pueden llamar a las funciones (excepto cuando se utilizan en clústeres condicionales dinámicos porque estas expresiones se evalúan en el navegador).

Las funciones descritas anteriormente se denominan de Funciones personalizadas y se definen utilizando código Java™. En función de la utilización, pueden ser de dos tipos:

Ejemplos reales que pueden necesitar la invocación de funciones externas son la validación de un código postal de EE.UU. que un usuario ha proporcionado y la población de un campo de estado basado en un código postal proporcionado. Ahora vamos a demostrar esos dos usos diferentes.

El esquema de DS se tendrá que ampliar para añadir los 2 atributos siguientes a la entidad de persona, como se indica a continuación:

Figura 1. Atributos de persona adicionales en el esquema de DS
<xsd:attribute name="state" type="IEG_STRING"/>
<xsd:attribute name="zipCode" type="IEG_STRING"/>

Primero intentemos validar un código postal en un estado (es una implementación simplista): un código postal debe tener una longitud de cinco dígitos y los tres primeros dígitos indicarán el estado.

La página de detalles personales mencionada anteriormente y la página de resumen correspondiente se pueden modificar con 2 preguntas obligatorias adicionales: estado y código postal:

Figura 2. Preguntas de estado y código postal en la definición de script
<question id="state" mandatory="true">
    <label id="State.Label">
        Estado:
    </label>
    <help-text id="State.HelpText">
        El estado en que vive
    </help-text>
</question>
<question id="zipCode" mandatory="true">
    <label id="ZipCode.Label">
        Código postal:
    </label>
    <help-text id="ZipCode.HelpText">
        Su código postal
    </help-text>
</question>

Entonces la función personalizada que realizar la validación se debe crear como una clase Java en el paquete curam.rules.functions:

Figura 3. Función personalizada para validar el código postal
...
public class CustomFunctionValidateZipCode extends CustomFunctor {

  public Adaptor getAdaptorValue(final RulesParameters rp)
  throws AppException, InformationalException {

    final List<Adaptor> parameters = getParameters();
    final String zipCode =
      ((StringAdaptor) parameters.get(0)).getStringValue(rp);
    final String state =
      ((StringAdaptor) parameters.get(1)).getStringValue(rp);
    boolean valid = false;

    if (zipCode.length() == 5) {
      final String prefix = zipCode.substring(0, 3);
      //buscar los prefijos de estado
      if (prefix.equals("100")
        && state.equalsIgnoreCase("Nueva York")) {
        valid = true;
      }
      if (prefix.equals("900")
        && state.equalsIgnoreCase("California")) {
        valid = true;
      }
    }

    return AdaptorFactory.getBooleanAdaptor(Boolean.valueOf(valid));
  }

}

Los metadatos siguientes para la función personalizada se deben insertar en <sucomponents>/rulesets/functions/CustomFunctionMetaData.xml:

Figura 4. Metadatos de función personalizada
<CustomFunctor name="CustomFunctionValidateZipCode">
  <parameters>
    <parameter>
      curam.util.rules.functor.Adaptor$StringAdaptor
    </parameter>
    <parameter>
      curam.util.rules.functor.Adaptor$StringAdaptor
    </parameter>
  </parameters>
  <returns>curam.util.rules.functor.Adaptor$BooleanAdaptor</returns>
</CustomFunctor>

Consulte la Guía de codificación de reglas de Cúram para obtener más detalles sobre la definición de las funciones personalizadas.

En nuestro ejemplo, la función personalizada ValidateZipCode no accede a una base de datos externa para buscar el estado correspondiente. Idealmente, debe realizar esa búsqueda y, a continuación, comprobar el estado devuelto con el estado que se ha entrado. Para simplificarlo, más arriba sólo se han codificado dos prefijos de código postal.

Entonces la validación se insertará en la página de detalles personales:

Figura 5. Validación de código postal en la definición de script
<validation
  expression="ValidateZipCode(Person.zipCode, Person.state)">
    <message id="InvalidZipCode">
        El código postal no es válido.
    </message>
</validation>

Cuando el usuario pulsa Siguiente, las respuestas a las preguntas de código postal y estado se pasan a la función personalizada, que devolverá "true" si las respuestas son válidas. Entonces se visualizará la página siguiente.

Si la función personalizada devuelve "false", se visualiza el mensaje especificado en la validación en la parte superior de la página de detalles de la persona, bloqueando el acceso a la página siguiente hasta que se someten respuestas válidas.

La función personalizada no tiene ningún efecto secundario porque no modifica nada. Sólo realiza una operación basada en los parámetros y devuelve un resultado.

También sería posible eliminar el distintivo obligatorio en las dos nuevas preguntas y validar las respuestas sólo si han sido proporcionado ambas. Entonces será necesario cambiar la expresión de validación por lo siguiente utilizando la función personalizada lista para utilizarse isNotNull que comprueba si el parámetro proporcionado es nulo:

Figura 6. Expresión de validación alternativa
"not(isNotNull(Person.zipCode) and isNotNull(Person.state))
    or ValidateZipCode(Person.zipCode, Person.state)"

De forma alternativa, es posible llenar la pregunta de estado dado el código postal. Para ello, la página de detalles de la persona sólo solicitará el código postal (con el distintivo obligatorio) y la página de resumen mostrará el estado y el código postal.

Se debe definir la siguiente función personalizada:

Figura 7. Función personalizada para llenar el estado
...
public class CustomFunctionpopulateState extends CustomFunctor {

  public Adaptor getAdaptorValue(final RulesParameters rp)
  throws AppException, InformationalException {

    final IEG2Context ieg2Context = (IEG2Context) rp;
    final long rootEntityID = ieg2Context.getRootEntityID();
    String schemaName; 
    //schemaName tiene que codificarse o recuperarse fuera de IEG
    Datastore ds = null;
    try {
      ds =
        DatastoreFactory.newInstance().openDatastore(
            schemaName);
    } catch (NoSuchSchemaException e) {
      throw new AppException(IEG.ID_SCHEMA_NOT_FOUND);
    }

    Entity applicationEntity = ds.readEntity(rootEntityID);

    Entity personEntity =
      applicationEntity.getChildEntities(
        ds.getEntityType("Person"))[0];
    String zipCode = personEntity.getAttribute("zipCode");
    String state = "Unknown";
    final String prefix = zipCode.substring(0, 3);
    //buscar los prefijos de estado
    if (prefix.equals("100")) {
      state = "Nueva York";
    }
    if (prefix.equals("900")) {
      state = "California";
    }
    personEntity.setAttribute("state", state);
    personEntity.update();
    return AdaptorFactory.getBooleanAdaptor(new Boolean(true));
  }

}

Y los metadatos:

Figura 8. Metadatos de función personalizada
<CustomFunctor name="CustomFunctionpopulateState">
  <returns>curam.util.rules.functor.Adaptor$BooleanAdaptor</returns>
</CustomFunctor>

Entre la página de detalles de la persona y la página de resumen, se debe insertar un elemento de llamada para llamar a esta función personalizada, como se indica a continuación:

Figura 9. Llamada para llenar el estado en la definición de script
<callout id="populateAddress" expression="populateState()"/>

Esta vez, la función personalizada modificará el DS llenando el estado en la entidad de persona. El contexto contiene el ID de entidad raíz y el ID de ejecución, lo que facilita la actualización del DS. Si la llamada se encuentra en un bucle, el contexto también contiene el ID de entidad actual.