Skip to content

Commit

Permalink
Merge branch 'feature/vocabulary-api-extensions' into 'develop'
Browse files Browse the repository at this point in the history
vocabulary api management

See merge request upm-inesdata/inesdata-connector!13
  • Loading branch information
ralconada-gmv committed May 15, 2024
2 parents aba4ab2 + c4450cc commit 538de9c
Show file tree
Hide file tree
Showing 35 changed files with 1,511 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The connector is prepared to be executed inside a container. The following steps

- Create a local Docker image:
```
docker build --tag inesdata/connector:0.1 --build-arg CONNECTOR_JAR=./launchers/connector/build/libs/connector-app.jar -f docker/Dockerfile .
docker build --tag inesdata/connector:0.2.0 --build-arg CONNECTOR_JAR=./launchers/connector/build/libs/connector-app.jar -f docker/Dockerfile .
```

## Database
Expand Down
19 changes: 19 additions & 0 deletions extensions/vocabulary-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Vocabulary API

Provides a management API for work with vocabularies. This API expands the functionality of the control-plane management API to be able to handle Vocabulary entities.


## Vocabulary entity

An example of a Vocabulary entity is shown below.

```
{
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/"
},
"@id": "vocabularyId",
"name": "Vocabulary name",
"jsonSchema": "{ \"title\": \"vocabulary\", \"type\": \"object\", \"properties\": { \"name\": { \"type\": \"string\", \"title\": \"Name\" }, \"keyword\": { \"type\": \"array\", \"title\": \"Keywords\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"name\" ] }"
}
```
24 changes: 24 additions & 0 deletions extensions/vocabulary-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
plugins {
`java-library`
id("com.gmv.inesdata.edc-application")
}

dependencies {
api(project(":spi:vocabulary-spi"))
api(libs.edc.spi.core)
implementation(libs.edc.spi.transform)
implementation(libs.edc.web.spi)

implementation(libs.edc.connector.core)
implementation(libs.edc.api.core)
implementation(libs.edc.lib.util)
implementation(libs.edc.lib.transform)
implementation(libs.edc.dsp.api.configuration)
implementation(libs.edc.api.management.config)
implementation(libs.edc.transaction.spi)
implementation(libs.edc.lib.validator)
implementation(libs.edc.validator.spi)
implementation(libs.swagger.annotations.jakarta)
runtimeOnly(libs.edc.spi.jsonld)
runtimeOnly(libs.edc.json.ld.lib)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.upm.inesdata.vocabulary;

import jakarta.json.Json;
import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.health.HealthCheckResult;
import org.eclipse.edc.spi.system.health.HealthCheckService;
import org.eclipse.edc.spi.types.TypeManager;
import org.eclipse.edc.transaction.spi.TransactionContext;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
import org.eclipse.edc.web.spi.WebService;
import org.upm.inesdata.spi.vocabulary.VocabularyIndex;
import org.upm.inesdata.spi.vocabulary.VocabularyService;
import org.upm.inesdata.vocabulary.controller.VocabularyApiController;
import org.upm.inesdata.vocabulary.service.VocabularyServiceImpl;
import org.upm.inesdata.vocabulary.storage.InMemoryVocabularyIndex;
import org.upm.inesdata.vocabulary.transformer.JsonObjectFromVocabularyTransformer;
import org.upm.inesdata.vocabulary.transformer.JsonObjectToVocabularyTransformer;
import org.upm.inesdata.vocabulary.validator.VocabularyValidator;

import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD;
import static org.upm.inesdata.spi.vocabulary.domain.Vocabulary.EDC_VOCABULARY_TYPE;

import java.util.Map;

/**
* Extension that provides an API for managing vocabularies
*/
@Extension(value = VocabularyApiExtension.NAME)
public class VocabularyApiExtension implements ServiceExtension {

public static final String NAME = "Vocabulary API Extension";
private InMemoryVocabularyIndex defaultVocabularyIndex;

@Inject
private WebService webService;

@Inject
private VocabularyIndex vocabularyIndex;

@Inject(required = false)
private HealthCheckService healthCheckService;

@Inject
private ManagementApiConfiguration config;

@Inject
private TypeManager typeManager;

@Inject
private TransactionContext transactionContext;

@Inject
private TypeTransformerRegistry transformerRegistry;

@Inject
private JsonObjectValidatorRegistry validator;

@Override
public String name() {
return NAME;
}

/**
* Provides a default vocabularyService implementation
*/
@Provider(isDefault = true)
public VocabularyService vocabularyService() {
return new VocabularyServiceImpl(vocabularyIndex, transactionContext);
}

/**
* Provides a default in memory vocabularyIndex
*/
@Provider(isDefault = true)
public VocabularyIndex defaultVocabularyIndex() {
return getVocabularyIndex();
}

/**
* Initializes the service
*/
@Override
public void initialize(ServiceExtensionContext context) {
var monitor = context.getMonitor();

var managementApiTransformerRegistry = transformerRegistry.forContext("management-api");

var factory = Json.createBuilderFactory(Map.of());
var jsonLdMapper = typeManager.getMapper(JSON_LD);
managementApiTransformerRegistry.register(new JsonObjectFromVocabularyTransformer(factory, jsonLdMapper));
managementApiTransformerRegistry.register(new JsonObjectToVocabularyTransformer());

validator.register(EDC_VOCABULARY_TYPE, VocabularyValidator.instance());
var vocabularyApiController = new VocabularyApiController(this.vocabularyService(), managementApiTransformerRegistry, monitor, validator);
webService.registerResource(config.getContextAlias(), vocabularyApiController);

// contribute to the liveness probe
if (healthCheckService != null) {
var successResult = HealthCheckResult.Builder.newInstance().component("FCC Query API").build();
healthCheckService.addReadinessProvider(() -> successResult);
healthCheckService.addLivenessProvider(() -> successResult);
}
}

/**
* Creates a InMemoryVocabularyIndex if not exists
*/
private InMemoryVocabularyIndex getVocabularyIndex() {
if (defaultVocabularyIndex == null) {
defaultVocabularyIndex = new InMemoryVocabularyIndex();
}
return defaultVocabularyIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.upm.inesdata.vocabulary.controller;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import org.eclipse.edc.api.model.ApiCoreSchema;
import org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractOffer;

import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
import static org.upm.inesdata.spi.vocabulary.domain.Vocabulary.EDC_VOCABULARY_TYPE;

/**
* Controller for managing {@link Vocabulary} objects.
*/
@OpenAPIDefinition(
info = @Info(description = "Manages the connector vocabularies.",
title = "Vocabulary API", version = "1"))
@Tag(name = "Vocabulary")
public interface VocabularyApi {

/**
* Get all the vocabularies stored in the vocabularies index. No filters are required due
* to the limited number of vocabularies that each data space will manage.
*
* @return list of vocabularies
*/
@Operation(description = "Obtains all vocabularies",
responses = {
@ApiResponse(responseCode = "200", description = "A list of vocabularies",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ContractOffer.class))))
}
)
JsonArray getVocabularies();

/**
* Retrieves the {@link Vocabulary} with the given ID
*
* @param id id of the vocabulary
* @return JsonObject with the vocabulary information
*/
@Operation(description = "Gets a vocabulary with the given ID",
responses = {
@ApiResponse(responseCode = "200", description = "The vocabulary",
content = @Content(schema = @Schema(implementation = VocabularyOutputSchema.class))),
@ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))),
@ApiResponse(responseCode = "404", description = "A vocabulary with the given ID does not exist",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))
}
)
JsonObject getVocabulary(String id);

/**
* Creates a new vocabulary
*
* @param vocabulary the vocabulary
* @return JsonObject with the created vocabulary
*/
@Operation(description = "Creates a new vocabulary",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = VocabularyOutputSchema.class))),
responses = {
@ApiResponse(responseCode = "200", description = "Vocabulary was created successfully",
content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))),
@ApiResponse(responseCode = "400", description = "Request body was malformed",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))),
@ApiResponse(responseCode = "409", description = "Could not create vocabulary, because a vocabulary with that ID already exists",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) }
)
JsonObject createVocabulary(JsonObject vocabylary);

/**
* Updates a vocabulary
*
* @param vocabulary the vocabulary to be updated
* @return JsonObject with the updated vocabulary
*/
@Operation(description = "Updates a vocabulary with the given ID if it exists. If the vocabulary is not found, no further action is taken.",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = VocabularyOutputSchema.class))),
responses = {
@ApiResponse(responseCode = "204", description = "Vocabulary was updated successfully"),
@ApiResponse(responseCode = "404", description = "Vocabulary could not be updated, because it does not exist."),
@ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))),
})
void updateVocabulary(JsonObject vocabulary);

/**
* Removes the {@link Vocabulary} with the given ID
*
* @param id id of the vocabulary
* @return JsonObject with the updated vocabulary
*/
@Operation(description = "Removes a vocabulary with the given ID if possible",
responses = {
@ApiResponse(responseCode = "204", description = "Vocabulary was deleted successfully"),
@ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))),
@ApiResponse(responseCode = "404", description = "Vocabulary could not be removed, because it does not exist.",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))
})
void removeVocabulary(String id);

/**
* Vocabulary output
*/
@ArraySchema()
@Schema(name = "VocabularyOutput", example = VocabularyOutputSchema.VOCABULARY_OUTPUT_EXAMPLE)
record VocabularyOutputSchema(
@Schema(name = ID)
String id,
@Schema(name = TYPE, example = EDC_VOCABULARY_TYPE)
String name,
String jsonSchema
) {
public static final String VOCABULARY_OUTPUT_EXAMPLE = """
{
"@id": "vocabularyId",
"name": "vocabulary name",
"jsonSchema": "{ \\"title\\": \\"vocabulary\\", \\"type\\": \\"object\\", \\"properties\\": { \\"name\\": { \\"type\\": \\"string\\", \\"title\\": \\"Name\\" }, \\"dct:keyword\\": { \\"type\\": \\"array\\", \\"title\\": \\"Keywords\\", \\"items\\": { \\"type\\": \\"string\\" } } }, \\"required\\": [ \\"name\\" ], \\"@context\\": { \\"dct\\": \\"http:\\/\\/purl.org\\/dc\\/terms\\/\" } }"
}
""";
}

}
Loading

0 comments on commit 538de9c

Please sign in to comment.