Skip to content

Commit ada92c7

Browse files
committed
Added mappingPLugin/execute to mappingExecution controller, renamed *types* endpoints to *plugins*, updated changelog
1 parent e63a7de commit ada92c7

File tree

10 files changed

+183
-161
lines changed

10 files changed

+183
-161
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88
### Fixed
9+
* Cloned Git repositories are now properly closed (only relevant for tests)
10+
911
### New Features
12+
* Added support for JWT authentication (enabled via property *mapping-service.authEnabled*)
13+
* Integration of Prometheus monitoring
14+
* Added endpoint /api/v1/mappingExecution/plugins/<PluginID>/execute for direct execution of plugins
15+
* Added JoltPlugin to list of default plugins
16+
* New configuration property *mapping-service.mappingAdminRole* can be used to authenticate access to administrative endpoints
17+
* plugin-core jar released as separate dependency and can be used for implementing own plugins easier
18+
1019
### Changed
20+
* Countless dependency updates
21+
* Changed plugin loading behaviour (default plugins are now part of mapping-service and not shipped as jar files)
22+
* Plugin code (Python-based plugins) is now checked out in a configurable folder (mapping-service.codeLocation)
23+
* Changed base class for Python-based plugins to AbstractPythonMappingPlugin
24+
* Plugin version should now match the Git release tag used by Python-based plugins
25+
* Python-based plugins are now creating an own Venv (stored at the codeLocation) to avoid dependency conflicts
26+
* MappingAdministration endpoints /api/v1/mappingAdministration/types|reloadTypes were renamed to /api/v1/mappingAdministration/plugins|reloadPlugins
27+
* MappingAdministration /api/v1/mappingAdministration/reloadPlugins (GET) and PUT|POST /api/v1/mappingAdministration are now secured and can only be accessed from localhost (if authentication is disabled) or by users with the group role defined by property *mapping-service.mappingAdminRole*
1128

1229
## [1.1.1] - date 2025-01-20
1330
### Fixed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ systemProp.jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"
33
// Name of project
44
rootProject.name = 'mapping-service'
55
// Version of project
6-
version=1.1.2-SNAPSHOT
6+
version=2.0.0-SNAPSHOT
77
action.custom-1=allTests
88
action.custom-1.args=--configure-on-demand -w -x check

settings/application-default.properties

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,11 @@ mapping-service.authEnabled:false
7575
mapping-service.mappingAdminRole:MAPPING_ADMIN
7676

7777
repo.security.enable-csrf=false
78-
repo.security.allowedOriginPattern=*
78+
repo.security.allowedOriginPattern=*
79+
80+
keycloakjwt.jwk-url=https://<KeycloakHostname>/realms/<RealmName>/protocol/openid-connect/certs
81+
keycloakjwt.resource: <KeyCloak Client>
82+
keycloakjwt.jwt-claim: preferred_username
83+
keycloak.realm: <RealmName>
84+
keycloak.auth-server-url: https://<KeycloakHostname>/
85+
keycloak.resource: <RealmName>

src/main/java/edu/kit/datamanager/mappingservice/configuration/OpenApiDefinitions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public OpenAPI customOpenAPI() {
3636
.components(new Components())
3737
.info(new Info().title("Mapping-Service - RESTful API").
3838
description("This webpage describes the RESTful API of the KIT Data Manager Mapping-Service.").
39-
version("1.1.2").
39+
version("2.0.0").
4040
contact(
4141
new Contact().
4242
name("KIT Data Manager Support").

src/main/java/edu/kit/datamanager/mappingservice/configuration/WebSecurityConfig.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545
import org.springframework.web.cors.CorsConfigurationSource;
4646
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
4747
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
48-
49-
import java.util.ArrayList;
5048
import java.util.Arrays;
5149
import java.util.List;
5250
import java.util.Optional;
@@ -89,8 +87,7 @@ record SecuredEndpoint(String method, String pattern) {
8987

9088
//special endpoints only accessible by mapping admin (if auth enabled) or from localhost (if auth disabled)
9189
List<SecuredEndpoint> securedEndpointMatchers = List.of(
92-
new SecuredEndpoint("POST", "/api/v1/mappingAdministration/types/*/execute"),
93-
new SecuredEndpoint("GET", "/api/v1/mappingAdministration/reloadTypes"),
90+
new SecuredEndpoint("GET", "/api/v1/mappingAdministration/reloadPlugins"),
9491
new SecuredEndpoint("PUT", "/api/v1/mappingAdministration"),
9592
new SecuredEndpoint("POST", "/api/v1/mappingAdministration")
9693
);

src/main/java/edu/kit/datamanager/mappingservice/domain/MappingRecord.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class MappingRecord implements EtagSupport, Serializable {
5151
@NotNull(message = "The unique identify of the record.")
5252
private String mappingId;
5353

54-
@NotNull(message = "Type of the mapping, e.g. GEMMA, XSLT, handlebars, ....")
54+
@NotNull(message = "Id of the plugin used for the mapping, e.g. GEMMA_v1.0.0, XSLT_v2.1.0, handlebars_v2.0.1, ....")
5555
private String mappingType;
5656

5757
@NotNull(message = "Title of the mapping.")

src/main/java/edu/kit/datamanager/mappingservice/rest/IMappingAdministrationController.java

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import io.swagger.v3.oas.annotations.media.Schema;
2626
import io.swagger.v3.oas.annotations.responses.ApiResponse;
2727
import io.swagger.v3.oas.annotations.responses.ApiResponses;
28-
import jakarta.servlet.http.HttpServletRequest;
2928
import jakarta.servlet.http.HttpServletResponse;
3029
import org.springdoc.core.converters.models.PageableAsQueryParam;
3130
import org.springframework.core.io.Resource;
@@ -51,7 +50,7 @@
5150
public interface IMappingAdministrationController {
5251

5352
@Operation(summary = "Create a new mapping.", description = "This endpoint allows to create a new mapping and required two parameters: The record metadata, which contains "
54-
+ "the mapping identifier and mapping type, and the mapping document, which defines the rules for the mapping applied by the given mapping type. ",
53+
+ "the mapping identifier and mapping plugin id, and the mapping document, which defines the rules for the mapping applied by the given mapping plugin. ",
5554
responses = {
5655
@ApiResponse(responseCode = "201", description = "CREATED is returned only if the record has been validated, persisted and the mapping document was successfully validated and stored.", content = @Content(schema = @Schema(implementation = MappingRecord.class))),
5756
@ApiResponse(responseCode = "400", description = "BAD_REQUEST is returned if the provided mapping record or the mapping document are invalid."),
@@ -63,7 +62,7 @@ public interface IMappingAdministrationController {
6362
ResponseEntity<MappingRecord> createMapping(
6463
@Parameter(description = "JSON representation of the mapping record.", required = true) @RequestPart(name = "record") final MultipartFile record,
6564
@Parameter(description = "The mapping document associated with the record. "
66-
+ "The format of the document is defined by the mapping type, which is given by the mappingType attribute of the mapping record.", required = true) @RequestPart(name = "document") final MultipartFile document,
65+
+ "The format of the document is defined by the mapping plugin, which is given by the mappingPluginId attribute of the mapping record.", required = true) @RequestPart(name = "document") final MultipartFile document,
6766
final WebRequest request,
6867
final HttpServletResponse response,
6968
final UriComponentsBuilder uriBuilder) throws URISyntaxException;
@@ -95,15 +94,15 @@ ResponseEntity<Resource> getMappingDocumentById(
9594
HttpServletResponse hsr);
9695

9796
@Operation(summary = "Get all mapping records.", description = "List all mapping records in a paginated and/or sorted form. The listing can be "
98-
+ "refined by providing a typeId in order to return only mapping for a certain mapping type. If not typeId is provided, all mapping "
97+
+ "refined by providing a mappingPluginId in order to return only mapping for a certain mapping plugin. If no mappingPluginId is provided, all mapping "
9998
+ "records are returned.",
10099
responses = {
101100
@ApiResponse(responseCode = "200", description = "OK and a list of records, which might be empty.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = MappingRecord.class))))})
102101
@RequestMapping(value = {"/"}, method = {RequestMethod.GET})
103102
@PageableAsQueryParam
104103
@ResponseBody
105104
ResponseEntity<List<MappingRecord>> getMappings(
106-
@Parameter(description = "The type identifier linked to a mapping type.") @RequestParam(value = "typeId") String typeId,
105+
@Parameter(description = "The plugin identifier linked to a mapping plugin.") @RequestParam(value = "mappingPluginId") String mappingPluginId,
107106
Pageable pgbl,
108107
WebRequest wr,
109108
HttpServletResponse hsr,
@@ -123,7 +122,7 @@ ResponseEntity<MappingRecord> updateMapping(
123122
@Parameter(description = "The mapping identifier.", required = true) @PathVariable(value = "mappingId") String mappingId,
124123
@Parameter(description = "JSON representation of the metadata record.") @RequestPart(name = "record") final MultipartFile record,
125124
@Parameter(description = "The mapping document associated with the record. "
126-
+ "The format of the document is defined by the mapping type, which is given by the mappingType attribute of the mapping record.") @RequestPart(name = "document") final MultipartFile document,
125+
+ "The format of the document is defined by the mapping plugin, which is given by the mappingPluginId attribute of the mapping record.") @RequestPart(name = "document") final MultipartFile document,
127126
final WebRequest request,
128127
final HttpServletResponse response,
129128
final UriComponentsBuilder uriBuilder
@@ -146,40 +145,17 @@ ResponseEntity<Void> deleteMapping(
146145
@Operation(summary = "Get all available plugins.",
147146
responses = {
148147
@ApiResponse(responseCode = "200", description = "OK and a list of all plugins will be returned, which might be empty.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = PluginInformation.class))))})
149-
@RequestMapping(value = {"/types"}, method = {RequestMethod.GET})
148+
@RequestMapping(value = {"/plugins"}, method = {RequestMethod.GET})
150149
@ResponseBody
151150
ResponseEntity<List<PluginInformation>> getAvailablePlugins(
152151
WebRequest wr,
153152
HttpServletResponse hsr);
154153

155-
@Operation(summary = "Reload all mapping types.", description = "Reloads all plugins from the plugin directory and updates their dependencies if necessary.",
154+
@Operation(summary = "Reload all mapping plugins.", description = "Reloads all plugins from the plugin directory and updates their dependencies if necessary.",
156155
responses = {
157156
@ApiResponse(responseCode = "204", description = "NO_CONTENT is returned on a successful refresh.")})
158-
@RequestMapping(value = {"/reloadTypes"}, method = {RequestMethod.GET})
157+
@RequestMapping(value = {"/reloadPlugins"}, method = {RequestMethod.GET})
159158
ResponseEntity<String> reloadAvailablePlugins(
160159
WebRequest wr,
161160
HttpServletResponse hsr);
162-
163-
@Operation(summary = "Map a document directly using the provided plugin.", description = "This endpoint allows the mapping of documents via a file upload. "
164-
+ "The identifier of the plugin must be passed to this endpoint as parameters together with the document to be mapped and the mapping rules.", responses = {
165-
@ApiResponse(responseCode = "200", description = "OK is returned if the mapping was successful. "
166-
+ "The result will also be returned in the response."),
167-
@ApiResponse(responseCode = "404", description = "NOT_FOUND is returned if no plugin for typeID could be found."),
168-
@ApiResponse(responseCode = "400", description = "BAD_REQUEST is returned if a parameter is missing or the mapping could not be performed with the provided input. It is "
169-
+ "expected that a mapping plugin accepts a well defined input and produces results for proper inputs. Therefore, only a faulty input "
170-
+ "document should be the reason for a mapper to fail."),
171-
@ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR is returned the mapping returned successfully, but the mapping result "
172-
+ "is not accessible. This is expected to be an error in the mapping implementation and should be fixed in there.")})
173-
@RequestMapping(value = {"/types/{typeID}/execute"}, method = {RequestMethod.POST}, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
174-
@ResponseBody
175-
void runPlugin(
176-
@Parameter(description = "The document to be mapped.", required = true) @RequestPart(name = "document") final MultipartFile document,
177-
@Parameter(description = "The mapping rules document.", required = true) @RequestPart(name = "mapping") final MultipartFile mapping,
178-
@Parameter(description = "The typeID of the plugin to execute.", required = true) @PathVariable(value = "typeID") String typeID,
179-
final HttpServletRequest request,
180-
final HttpServletResponse response,
181-
final UriComponentsBuilder uriBuilder) throws URISyntaxException;
182-
183-
184-
185161
}

src/main/java/edu/kit/datamanager/mappingservice/rest/IMappingExecutionController.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,24 @@ ResponseEntity<JobStatus> getJobStatus(
115115
@ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR if the mapping job has failed.")})
116116
@DeleteMapping(path = "/schedule/{job-id}")
117117
ResponseEntity<Void> deleteJobAndAssociatedData(@PathVariable(name = "job-id") String jobId) throws Throwable;
118+
119+
@Operation(summary = "Map a document directly using the provided plugin.", description = "This endpoint allows the mapping of documents via a file upload. "
120+
+ "The identifier of the plugin must be passed to this endpoint as parameters together with the document to be mapped and the mapping rules.", responses = {
121+
@ApiResponse(responseCode = "200", description = "OK is returned if the mapping was successful. "
122+
+ "The result will also be returned in the response."),
123+
@ApiResponse(responseCode = "404", description = "NOT_FOUND is returned if no plugin for pluginId could be found."),
124+
@ApiResponse(responseCode = "400", description = "BAD_REQUEST is returned if a parameter is missing or the mapping could not be performed with the provided input. It is "
125+
+ "expected that a mapping plugin accepts a well defined input and produces results for proper inputs. Therefore, only a faulty input "
126+
+ "document should be the reason for a mapper to fail."),
127+
@ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR is returned the mapping returned successfully, but the mapping result "
128+
+ "is not accessible. This is expected to be an error in the mapping implementation and should be fixed in there.")})
129+
@RequestMapping(value = {"/plugins/{pluginId}/execute"}, method = {RequestMethod.POST}, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
130+
@ResponseBody
131+
void runPlugin(
132+
@Parameter(description = "The document to be mapped.", required = true) @RequestPart(name = "document") final MultipartFile document,
133+
@Parameter(description = "The mapping rules document.", required = true) @RequestPart(name = "mapping") final MultipartFile mapping,
134+
@Parameter(description = "The pluginId of the plugin to execute.", required = true) @PathVariable(value = "pluginId") String pluginId,
135+
final HttpServletRequest request,
136+
final HttpServletResponse response,
137+
final UriComponentsBuilder uriBuilder) throws URISyntaxException;
118138
}

0 commit comments

Comments
 (0)