Skip to content

Commit

Permalink
feat: processing service (#7)
Browse files Browse the repository at this point in the history
* feat: small refactor + init of processor service

* feat: processing service finished

* feat: processing service implemented

* feat: bugfixes and cleanup of new service

* feat: additional test added

* docs: little remark added

* fix: invalid logging levels

* feat: PR remarks

* feat: small improvements

* fix: broken ci pipeline
  • Loading branch information
jobulcke authored Sep 12, 2024
1 parent 8f86901 commit 5a6cbdd
Show file tree
Hide file tree
Showing 40 changed files with 1,159 additions and 397 deletions.
140 changes: 100 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,74 @@
# VSDS TestBed Shacl Validator

<!-- TOC -->

* [VSDS TestBed Shacl Validator](#vsds-testbed-shacl-validator)
* [Introduction](#introduction)
* [Validation service implementation](#validation-service-implementation)
* [Introduction](#introduction)
* [ReplicationProcesssingService](#replicationprocesssingservice)
* [startReplicating](#startreplicating)
* [haltWhenReplicated](#haltwhenreplicated)
* [destroyPipeline](#destroypipeline)
* [ShaclValidationService](#shaclvalidationservice)
* [How to run in Docker](#how-to-run-in-docker)
* [Prerequisites](#prerequisites)
* [Building and running](#building-and-running)
* [Live reload for development](#live-reload-for-development)
* [Packaging using Docker](#packaging-using-docker)

* [Steps to use the TestBed Shacl Validator](#steps-to-use-the-testbed-shacl-validator)
* [interact](#interact)
* [call](#call)
* [Building and running the project](#building-and-running-the-project)
* [Live reload for development](#live-reload-for-development)
* [Packaging using Docker](#packaging-using-docker)
<!-- TOC -->

## Introduction

This application implements the [GITB test service APIs](https://www.itb.ec.europa.eu/docs/services/latest/) in a
[Spring Boot](https://spring.io/projects/spring-boot) web application that is meant to support
[GITB TDL test cases](https://www.itb.ec.europa.eu/docs/tdl/latest/) running in the Interoperability Test Bed.

### Validation service implementation
## ReplicationProcesssingService

This service is responsible for creating an LDIO pipeline, checking if the LDES Client is still REPLACTING and
destroying the pipeline afterwards. It is available through the endpoint's
WSDL: http://localhost:8080/services/process?wsdl. This processing service has three operations:

#### startReplicating

This operation is responsible for creating the pipeline, which immediately starts the replication process.

Input parameters:

| Name | Description | Required |
|:--------:|---------------------------------|----------|
| ldes-url | the url of the LDES to validate | true |

#### haltWhenReplicated

This operation is responsible for polling and returning the status of the LDES Client

#### destroyPipeline

This operation is resonsible for deleting the LDIO pipeline, as well the GraphDB repository

More information about processing services can be
found [here](https://www.itb.ec.europa.eu/docs/services/latest/processing/)

## ShaclValidationService

The sample validation service validates a text against an (also provided) expected value. The user of the service can
also select whether he/she wants to have a mismatch reported as an error or a warning. Finally, an information message
is also returned in case values match but when ignoring casing.
This service is responsible for executing the SHACL validation and is accessible through the endpoint's
WSDL: http://localhost:8080/services/validation?wsdl

Once running, the validation endpoint's WDSL is available at http://localhost:8080/services/validation?WSDL. See
[here](https://www.itb.ec.europa.eu/docs/services/latest/validation/) for further information on processing service
implementations.
Input parameters:

| Name | Description | Required |
|:-----------:|---------------------------------------------------------------------|----------|
| shacl-shape | the shacl shape that will be used to validate the server against to | true |

This validation services requires in the validation call two parameters:

1. **ldes-url**: the url of the LDES to validate
2. **shacl-shape**: the shacl shape that will be used to validate the server against to

More information about validation services can be
found [here](https://www.itb.ec.europa.eu/docs/services/latest/validation/)

## How to run in Docker

### Prerequisites
Expand Down Expand Up @@ -75,6 +111,8 @@ In this specific tutorial, this would result in the following config:
- SERVER_PORT=8080
```
This service has already been added to the provided `docker-compose.yaml` file.

2. Start up all the services

```shell
Expand All @@ -92,10 +130,24 @@ First of all, make a folder that will contain all required files for the test su
```shell
mkdir validate_ldes_to_shacl_shape
cd validate_ldes_to_shacl_shape
```

Secondly, a scriptlet will be added to the test suite, which contains all the required logic to create the necessary
LDIO pipelines, check the LDES Client status and so on.

```shell
mkdir scriptlets
```

In this directory, place the [`validate-ldes.xml`](./docker/scriplets/validate-ldes.xml) file.

Now, the test cases can be written and added to the test suite. First, a new folder must be created.

```shell
mkdir test_cases
```

Secondly, test case xml file can be added to the `test_cases` folder.
Now, `test_case.xml` file can be added to the newly created folder

```xml
Expand All @@ -111,20 +163,20 @@ Secondly, test case xml file can be added to the `test_cases` folder.
</actors>
<steps stopOnError="true">
<!-- Prompt the user to enter the required data to run the test -->
<interact id="validationData" desc="Setup parameters to validate the LDES">
<request desc="URL of the LDES to validate" name="ldes"/>
<request desc="URL of the LDES to validate" name="ldesUrl"/>
<request desc="Shacl shape that must be used for validating" name="shaclShape" inputType="UPLOAD"/>
<request desc="Amount of seconds between each polling attempt" name="pollingInterval" inputType="TEXT"/>
</interact>
<log>"Starting shacl validation test"</log>
<!-- Step 3: Check relation timestamp consistency. -->
<!-- Notice here how we refer to the address of the validation service using the domain-level "validationServiceAddress" configuration property. -->
<verify output="validatorOutput" id="shaclValidationStep" desc="validate against shacl"
handler="http://testbed-shacl-validator:8080/services/validation?wsdl">
<input name="ldes-url">$validationData{ldes}</input>
<input name="shacl-shape">$validationData{shaclShape}</input>
</verify>
<log>"shacl verification finished"</log>
<assign to="delayDuration">concat($validationData{pollingInterval}, '000')</assign>
<assign to="addresses{processing}">"http://testbed-shacl-validator:8080/services/process?wsdl"</assign>
<assign to="addresses{validation}">"http://testbed-shacl-validator:8080/services/validation?wsdl"</assign>
<call id="validateLdes" path="scriptlets/validate-ldes.xml">
<input name="ldesUrl">$validationData{ldesUrl}</input>
<input name="shaclShape">$validationData{shaclShape}</input>
<input name="delayDuration">$delayDuration</input>
<input name="addresses">$addresses</input>
</call>
</steps>
<output>
Expand All @@ -145,21 +197,27 @@ Some interesting things we see in this test case, are the `interact` block, as w
#### interact

This block is responsible for prompting the user/tester to enter some data that will be used for the test. In this
specific case, it will be the URL of the LDES that must be validated.
specific case, it will be the URL of the LDES that must be validated, a SHACL shape file and a polling interval on which
the LDES Client status must be checked.

| Name of the parameter | Description |
|:---------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ldesUrl | URL of the LDES to be validated |
| SHACL shape | SHACL shape that will be used to validate the LDES |
| pollingInterval | Interval in which the LDES Client status will be checked in seconds. Keep in mind if the LDES to validate contains 1M members, it can take a very long time to replicate. Therefore, it can be better to configure a larger interval (e.g. an hour or even half a day) |

#### verify
#### call

This block is responsible for the verifying the LDES by calling the external service, which is this TestBed Shacl
Validator service. The most important part to notice here is the `handler` attribute, where the external TestBed Shacl
Validator service will be set. In this case, the docker compose service name must be used, followed by the port. After
that, the "fixed" uri is placed, which will call the right validation service, which is in this case
`/services/validation?wsdl`
This block is responsible for executing the scriptlet, which is responsible for creating an LDIO pipeline, checking when
the LDES Client has finished with REPLICATING, performing the SHACL validation and destroying the pipeline afterward.

Another thing that can be noticed here, are the input parameters. These two parameters are required to let the test run.
Another thing that can be noticed here, are the input parameters. These three parameters are required to let the test
run.

1. `ldes-url`: the url of the LDES that must be validated. The value here is fetched from the prompted input
2. `shacl-shape`: the SHACL shape that will be used to validate all the members against to. Here is the value also
1. `ldesUrl`: the url of the LDES that must be validated. The value here is fetched from the prompted input
2. `shaclShape`: the SHACL shape that will be used to validate all the members against to. Here is the value also
fetched from the prompted input
3. `delayDuration`: the amount of seconds on which the LDES Client must be checked

To finish up this step, a test suite itself must be added as well. This can be done by adding `test_suite.xml` to the
`validate_ldes_to_shacl_shape` folder.
Expand Down Expand Up @@ -206,11 +264,13 @@ This is how the file should look like:
Most important to notice here, is that de test case declared in the `test_cases` folder, must be referred from this file
by adding the `test case` tag with as attribute the id that has been assigned to the test case in its file.

4. Compress the `validate_ldes_to_shacl_shape` folder to a zip and upload it to TestBed
4. Add the Test Suite to the TestBed instance

Create a ZIP file contains all the items that are in `validate_ldes_to_shacl_shape` folder. This can be uploaded to
TestBed now. This can be done either by the UI, or via the REST API. If you choose to do it via the REST API, the

If everything is set up, the folder containing everything must be zipped. After that, it can be uploaded to TestBed. This can be done either by the UI, or via the REST API. If you choose to do it via the REST API, the
```shell
curl -F updateSpecification=true -F specification=<SPECIFICATION_API_KEY> -F testSuite=@test_shacl_validator.zip --header "ITB_API_KEY: <DOMAIN_API_KEY>" -X POST http://localhost:9000/api/rest/testsuite/deploy;
curl -F updateSpecification=true -F specification=<SPECIFICATION_API_KEY> -F testSuite=@test_shacl_validator.zip --header "ITB_API_KEY: <COMMUNITY_API_KEY>" -X POST http://localhost:9000/api/rest/testsuite/deploy;
```

5. Run the test via the TestBed UI
Expand Down
50 changes: 50 additions & 0 deletions docker/scriplets/validate-ldes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<scriptlet id="validate-ldes" xmlns="http://www.gitb.com/tdl/v1/">
<params>
<var name="ldesUrl" type="string"/>
<var name="shaclShape" type="string"/>
<var name="delayDuration" type="number">
<value>60000</value>
</var>
<var name="addresses" type="map" />
</params>
<steps stopOnError="true">
<process output="startupReport" desc="Start Replication" id="startReplicatingProcess"
handler="$addresses{processing}">
<operation>startReplicating</operation>
<input name="ldes-url">$ldesUrl</input>
</process>
<log>"Waiting for the LDES Client status to be available"</log>
<process desc="Wait until client is available" handler="DelayProcessor">
<operation>delay</operation>
<input>5000</input>
</process>

<log>"Start checking LDES Client status"</log>
<assign to="replicationOutput"/>
<repuntil desc="Check if replication has ended">
<do>
<process desc="Wait" handler="DelayProcessor">
<operation>delay</operation>
<input>$delayDuration</input>
</process>
<process output="replicationOutput" handler="$addresses{processing}">
<operation>haltWhenReplicated</operation>
</process>
<log level="DEBUG">$replicationOutput{STATUS}</log>
</do>
<cond>$replicationOutput{STATUS} = 'REPLICATING'</cond>
</repuntil>
<log>"Starting shacl validation"</log>

<verify output="validatorOutput" id="shaclValidationStep" desc="validate against shacl"
handler="$addresses{validation}">
<input name="shacl-shape">$shaclShape</input>
</verify>
<log>"shacl verification finished"</log>
<process desc="Delete pipeline" handler="$addresses{processing}" operation="destroyPipeline"/>
</steps>
<output name="validatorOutput">
$validatorOutput
</output>
</scriptlet>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package be.vlaanderen.informatievlaanderen.ldes.gitb;

import be.vlaanderen.informatievlaanderen.ldes.gitb.services.replication.ProcessExecutors;
import be.vlaanderen.informatievlaanderen.ldes.gitb.valueobjects.ParameterDefinition;
import be.vlaanderen.informatievlaanderen.ldes.gitb.valueobjects.ProcessParameters;
import be.vlaanderen.informatievlaanderen.ldes.gitb.valueobjects.ProcessResult;
import com.gitb.core.ConfigurationParameters;
import com.gitb.core.Metadata;
import com.gitb.core.TypedParameters;
import com.gitb.ps.Void;
import com.gitb.ps.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class ReplicationProcessingService implements ProcessingService {
private static final String SERVICE_NAME = "ReplicationProcessingService";
private static final Logger log = LoggerFactory.getLogger(ReplicationProcessingService.class);
private final ProcessExecutors processExecutors;

public ReplicationProcessingService(ProcessExecutors processExecutors) {
this.processExecutors = processExecutors;
}

@Override
public GetModuleDefinitionResponse getModuleDefinition(Void parameters) {
final ProcessingModule processingModule = new ProcessingModule();
processingModule.setId(SERVICE_NAME);

final Metadata metadata = new Metadata();
metadata.setName(SERVICE_NAME);
processingModule.setMetadata(metadata);

processingModule.setConfigs(new ConfigurationParameters());

processExecutors.getProcessExecutors().stream()
.map(processExecutor -> {
final var processingOperation = new ProcessingOperation();
final var typedParameters = new TypedParameters();
processingOperation.setName(processExecutor.getName());
typedParameters.getParam().addAll(processExecutor
.getParameterDefinitions()
.stream()
.map(ParameterDefinition::convertToTypedParameter)
.toList());
processingOperation.setInputs(typedParameters);
return processingOperation;
})
.forEach(processingModule.getOperation()::add);

final GetModuleDefinitionResponse getModuleDefinitionResponse = new GetModuleDefinitionResponse();
getModuleDefinitionResponse.setModule(processingModule);
return getModuleDefinitionResponse;
}

@Override
public ProcessResponse process(ProcessRequest parameters) {
log.info("Received 'process' command with '{}' operation from test bed for session [{}]", parameters.getOperation(), parameters.getSessionId());
return processExecutors.getProcessExecutor(parameters.getOperation())
.map(processExecutor -> processExecutor.execute(new ProcessParameters(parameters.getSessionId(), parameters.getInput())))
.orElseGet(() -> ProcessResult.invalidOperation(parameters.getOperation()))
.convertToResponse();
}

@Override
public BeginTransactionResponse beginTransaction(BeginTransactionRequest parameters) {
return new BeginTransactionResponse();
}

@Override
public Void endTransaction(BasicRequest parameters) {
return new Void();
}


}
Loading

0 comments on commit 5a6cbdd

Please sign in to comment.