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.24.4</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.
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:
Verify the received inputs to ensure validation can proceed.
Extract the values of the inputs.
Run the validation.
Construct the validation report (also adding custom output values to its context if desired).
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 theTAR
report as you validate or foresee an intermediate GITB-agnostic structure as the result of your validation that you will then convert to the expectedTAR
report. These are all points to consider when designing your validation service. Details on how theTAR
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;
}
}
Note
Default service address: Using the above displayed endpoint mapping, and considering (a) no app context path,
(b) the default port mapping of 8080, and (c) the default CXF root of /services
, the full WSDL address would be:
http://localhost:8080/services/validation?wsdl
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:
A client for the service is constructed based on the WSDL provided through the
handler
attribute.The service’s getModuleDefinition operation is called to determine its input parameters.
The inputs are constructed based on the GITB TDL expressions in the test case (in the example the single
aDocument
input is populated from thedocument
context variable).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 toSTRING
. 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.