Validation services

GITB validation services are used to validate input and produce a validation report. They are arguably the simplest type of test service given their specific use which is reflected in the limited operations such a service needs to implement. Considering their focus on validation and the simplicity of their API, validation services are also easily used as standalone services for one-off validation calls.

Implementing the service

A GITB validation service is a web application that at least exposes a web service implementing the GITB validation service API. The easiest way to get up and running is to use the template validation service available as a Maven Archetype (see Service template).

Once you have answered the prompts you will have a fully functioning GITB validation service implemented using the Spring Boot framework that you can adapt to your specific needs. Alternatively of course you can implement the service from scratch in any way and technology stack you prefer. In this case a very useful resource is the gitb-types library that includes classes for all GITB types, service interfaces and service clients. This is available on Maven Central and can be added as a Maven dependency as follows:

<dependency>
    <groupId>eu.europa.ec.itb</groupId>
    <artifactId>gitb-types-jakarta</artifactId>
    <version>1.22.0</version>
</dependency>

Note

The gitb-types library is also available in a variant with classes using the Javax APIs. See Using the gitb-types library for details.

Check the Service template description for more details on the content and use of the sample validation service. In terms of its initial definition, a validation service needs to be defined as an implementation of the com.gitb.vs.ValidationService interface:

@Component
public class ValidationServiceImpl implements com.gitb.vs.ValidationService {
    ...
}

The following sections cover the service’s operations, whereas as the final step you will also need to register the service endpoint as part of your configuration.

Service operations

Note

Service WSDLs and XSDs: The WSDL and XSD for validation services are listed in the specification reference section.

The following figure illustrates the operations that a validation service needs to implement and their use by the test bed.

../_images/ValidationService.png

Figure 2: Use of the validation service operations

getModuleDefinition

The getModuleDefinition operation is used to return information on how the service is expected to be used. In case the service is specific to a given project and not meant to be published and reused, you can provide an empty implementation as follows:

public GetModuleDefinitionResponse getModuleDefinition(Void parameters) {
    return new GetModuleDefinitionResponse();
}

If you plan to publish a reusable and well-documented service for others to use, it is meaningful to provide a complete implementation. In this case, this method is used to document:

  • The identification metadata of the service.

  • The configuration parameters it expects.

  • The variable inputs that are expected.

The difference between configuration parameters and inputs is more a conceptual point in that configuration parameterises the validation to take place, whereas inputs represent the actual content to validate. In practice configuration parameters are often skipped in favour of inputs that serve both to pass the content to validate as well as any additional properties needed by the validation service.

This operation is the first one to be called when using the service in a standalone manner as it allows the caller to figure out the inputs it expects. The validation service API defines generally how inputs are passed but not how many in this specific case nor the name and value of each one. When used by the test bed this operation is also important as it determines:

  • The types of expected inputs. This enables automatic type conversions when passing the call’s parameters.

  • The mandatory inputs. The test bed checks that all required inputs are accounted for before calling the validate operation to fail quickly without an unnecessary service call.

The following example shows a complete implementation of the getModuleDefinition operation.

public GetModuleDefinitionResponse getModuleDefinition(Void parameters) {
    GetModuleDefinitionResponse response = new GetModuleDefinitionResponse();
    response.setModule(new ValidationModule());
    // Set an identifier for the service.
    response.getModule().setId("MyValidationService");
    // Set "V" as the service's type (for "Validation").
    response.getModule().setOperation("V");
    response.getModule().setMetadata(new Metadata());
    // Set a name for the service (the identifier is reused here).
    response.getModule().getMetadata().setName(response.getModule().getId());
    // Set a version string for the service.
    response.getModule().getMetadata().setVersion("1.0.0");
    response.getModule().setInputs(new TypedParameters());
    // Define the service's input parameters.
    response.getModule().getInputs().getParam().add(createParameter(...));
    response.getModule().getInputs().getParam().add(createParameter(...));
    return response;
}

The metadata set for a validation service (identifier, name, version and operation) are not used in practice. The only important information that needs to be defined are the input parameters. In the above example these are created by calling a custom createParameter() method. See Documenting input and output parameters for full details on how these parameters need to be defined. Note that as of release 1.10.0, you are no longer obliged to define service inputs (i.e. they are optional), although doing so remains a best practice as it allows client-side input verification.

validate

The validate operation is used to carry out the validation that this service is meant to perform. The specific validation logic is entirely domain-specific, however all validate implementations follow a common sequence of steps:

  1. Verify the received inputs to ensure validation can proceed.

  2. Extract the values of the inputs.

  3. Run the validation.

  4. Construct the validation report (also adding custom output values to its context if desired).

  5. Return the result.

These steps are illustrated in the following code example:

public ValidationResponse validate(ValidateRequest parameters) {
    // First extract the parameters and check to see if they are as expected.
    List<AnyContent> input = getInput(parameters);
    if (input.size() != 1) {
        throw new IllegalArgumentException(String.format("This service expects one input to be provided named '%s'", INPUT1_NAME));
    }
    // Retrieve the value to process.
    String inputValue = getInputValue(input);
    // Validate the input and construct the report.
    TAR validationReport = doValidation(inputValue);
    // Return the result.
    ValidationResponse result = new ValidationResponse();
    result.setReport(validationReport);
    return result;
}

The above example illustrates the key steps that are taking place but decouples certain actions into separate methods. These are specifically:

  • The extraction of the input parameter in method getInput(). Multiple input parameters may be present including ones with the same name. See Using inputs on what you should consider when looking up your inputs.

  • The retrieval of the input value(s) to process in method getInputValue(). An input parameter offers a string value that may initially seem to be the one to use. This however could be BASE64 content or a remote URL that points to the actual content. See Interpreting an input value on what you should consider when retrieving an input’s value.

  • The validation and generation of the report in method doValidation(). This method captures the domain-specific validation logic and is a prime candidate to decouple in a separate component. Keep in mind however that the report includes errors and warnings that may need to be generated on-the-fly, which also may include the relevant location in the processed input (if possible). Omitting such details is possible but diminishes the reporting power of your validator considering that it would otherwise only report a “success” or “failure” result. As such, it might be necessary to construct the TAR report as you validate or foresee an intermediate GITB-agnostic structure as the result of your validation that you will then convert to the expected TAR report. These are all points to consider when designing your validation service. Details on how the TAR validation report itself should be populated are provided in Constructing a validation report (TAR).

Configuring the web service endpoint

Apart from fully implementing the expected web service operations, the validation service needs to correctly publish its service endpoint. Specifically:

  • The name of the service must be “ValidationService”.

  • The name of the service port must be “ValidationServicePort”.

  • The namespace must be set to “http://www.gitb.com/vs/v1/”.

Failure to do so will result in the test bed not being able to correctly lookup the endpoint to call. The following example illustrates how this could be done in a Spring implementation using CXF:

@Configuration
public class ValidationServiceConfig {
    @Bean
    public Endpoint validationService(Bus cxfBus, ValidationServiceImpl validationServiceImplementation) {
        EndpointImpl endpoint = new EndpointImpl(cxfBus, validationServiceImplementation);
        endpoint.setServiceName(new QName("http://www.gitb.com/vs/v1/", "ValidationService"));
        endpoint.setEndpointName(new QName("http://www.gitb.com/vs/v1/", "ValidationServicePort"));
        endpoint.publish("/validation");
        return endpoint;
    }
}

Using the service through a test case

Use of a validation service in a test case is achieved with the verify step. The following example illustrates use of a service that validates a single aDocument input parameter.

<verify handler="https://VALIDATION_SERVICE_ADDRESS?wsdl" desc="Validate against remote service">
    <input name="aDocument">$document</input>
</verify>

When the verify step is executed the following actions take place:

  1. A client for the service is constructed based on the WSDL provided through the handler attribute.

  2. The service’s getModuleDefinition operation is called to determine its input parameters.

  3. The inputs are constructed based on the GITB TDL expressions in the test case (in the example the single aDocument input is populated from the document context variable).

  4. The validate operation is called to validate the content and retrieve the report.

Using validator output in test cases

Starting from release 1.11.0, validators used via test cases can also have their output used in subsequent processing. Such output is returned by means of the service’s validation report, and specifically by including output data in the report’s context section. Returning such context data was already possible but resulted only in additional information displayed to complement the validation report, without allowing its subsequent use. This is now possible by means of the output attribute on the verify step.

Defining the output attribute results in the test session context being updated with the data set as the report’s context. This is typically a map but could be anything your service returns. The following snippet illustrates this case:

<!-- Call the service and store its output in a variable named "validatorOutput". -->
<verify output="validatorOutput" handler="https://VALIDATION_SERVICE_ADDRESS?wsdl" desc="Validate against remote service">
  <input name="aDocument">$document</input>
</verify>
<!-- Use the output. The context in this case was a map including an item named "identifier". -->
<log>$validatorOutput{identifier}</log>

Using the service standalone

Validation services can also be called in a standalone manner to perform one-off validations. Cases where such calls are commonly used are during unit testing to ensure that generated content is valid. The following example illustrates such a call:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.gitb.com/vs/v1/" xmlns:v11="http://www.gitb.com/core/v1/">
  <soapenv:Header/>
  <soapenv:Body>
      <v1:ValidateRequest>
        <input name="input1" embeddingMethod="STRING">
          <!-- Provide the input as-is. -->
          <v11:value>a_value</v11:value>
        </input>
        <input name="input2" embeddingMethod="STRING">
            <!-- Provide the input in a CDATA block to avoid XML formatting issues. -->
            <v11:value><![CDATA[Provide content here ...]]></v11:value>
        </input>
      </v1:ValidateRequest>
  </soapenv:Body>
</soapenv:Envelope>

The example above should be for the most part self-evident. Points that merit highlighting are:

  • The possibility to pass inputs as-is or in CDATA blocks (actually this is simply a XML feature).

  • The embeddingMethod that is set to STRING. This tells the validation service how the text value should be interpreted. Possible values are:

    • STRING: The value is used as-is.

    • BASE64: The value is considered as BASE64-encoded bytes.

    • URI: The value is considered to be the content retrieved from a remote URI reference.