Skip to content

Commit

Permalink
feat: workflow for creating an offering (#351)
Browse files Browse the repository at this point in the history
* feat(wrapper): create OfferingResource

* test(wrapper): Add API test for offering endpoint

WIP: Test for empty string input

* refactor(wrapper): return response for createOfferingEndpoint

* refactor(wrapper): delete unnecessary variable

* [WIP] feat(wrapper): model dtos

* feat(wrapper): replace incomplete and add missing model schemas in open api specification

* feat(wrapper): create OfferingResource

* test(wrapper): Add API test for offering endpoint

WIP: Test for empty string input

* refactor(wrapper): return response for createOfferingEndpoint

* refactor(wrapper): delete unnecessary variable

* [WIP] feat(wrapper): model dtos

* feat(wrapper): replace incomplete and add missing model schemas in open api specification

* feat(wrapper): Implement PolicyDTO and MappingService

* feat(wrapper): Implement PolicyDTO and MappingService

* feat(wrapper): Implement first PolicyMappingService test

* docs(wrapper): Checkstyle conform JavaDoc

* fix(wrapper): OfferingService tests

* test: update PolicyMappingServiceTest

* test: rename OfferingResourceTest

* test: add OfferingServiceTest

* chore: checkstyle

* refactor(wrapper): PR #351 suggestions

WIP

* fix(wrapper): Tests

* refactor(wrapper): Replace loops with java streams

* refactor(wrapper): Move offering endpoint to use-case api

* refactor(wrapper): Move validation to service

* chore(model): add ExpressionDto

* docs(common-api): JavaDoc and OpenAPI schema

* fix(wrapper): offering tests

* chore(policy): throw exception when permission missing

* refactor(common-api): policy model

* refactor(wrapper): consistent naming

* fix(wrapper): import order

---------

Co-authored-by: sbiehs <[email protected]>
Co-authored-by: Tim Dahlmanns <[email protected]>
Co-authored-by: Richard Treier <[email protected]>
  • Loading branch information
4 people authored Jun 26, 2023
1 parent fc2212d commit e754e0d
Show file tree
Hide file tree
Showing 21 changed files with 1,195 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
package de.sovity.edc.ext.wrapper.api.common.model;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.OffsetDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

import java.time.OffsetDateTime;
import java.util.Map;

@Getter
@Setter
@ToString
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package de.sovity.edc.ext.wrapper.api.common.model;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
* Opinionated DTO of an EDC Constraint for permissions.
*
* @author [email protected]
*/
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Schema(description =
"Type-Safe OpenAPI generator friendly Constraint DTO that supports an opinionated"
+ " subset of the original EDC Constraint Entity.")
public class AtomicConstraintDto {

@Schema(description = "Left part of the constraint.",
requiredMode = Schema.RequiredMode.REQUIRED)
private String leftExpression;
@Schema(description = "Operator to connect both parts of the constraint.",
requiredMode = Schema.RequiredMode.REQUIRED)
private OperatorDto operator;
@Schema(description = "Right part of the constraint.",
requiredMode = Schema.RequiredMode.REQUIRED)
private String rightExpression;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.sovity.edc.ext.wrapper.api.common.model;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* Expression constraints for policies.
*
* @author [email protected]
*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ExpressionDto {

@Schema(description = """
Expression types:
* `EMPTY` - No constraints for the policy
* `ATOMIC_CONSTRAINT` - A single constraint for the policy
* `AND` - Several constraints, all of which must be respected
* `OR` - Several constraints, of which at least one must be respected
* `XOR` - Several constraints, of which exactly one must be respected
"""
)
private Type type;
private AtomicConstraintDto atomicConstraint;
private List<ExpressionDto> and;
private List<ExpressionDto> or;
private List<ExpressionDto> xor;

/**
* Sum type enum.
*/
public enum Type {
EMPTY, ATOMIC_CONSTRAINT, AND, OR, XOR
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package de.sovity.edc.ext.wrapper.api.common.model;

import io.swagger.v3.oas.annotations.media.Schema;

/**
* The set of supported expression operators. Not all operators may be supported for particular
* expression types.
* Copied from EDC policy-model.
*
* @author [email protected]
*/
@Schema(description = "Operator for constraints")
public enum OperatorDto {
/**
* Operator expressing equality of two operands.
*/
EQ,
/**
* Operator expressing inequality of two operands.
*/
NEQ,
/**
* Operator expressing left operand is greater than right operand.
*/
GT,
/**
* Operator expressing left operand is greater or equal than to the right operand.
*/
GEQ,
/**
* Operator expressing left operand is lesser than to the right operand.
*/
LT,
/**
* Operator expressing left operand is lesser or equal than to the right operand.
*/
LEQ,
/**
* Operator expressing left operand is contained in the right operand.
*/
IN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package de.sovity.edc.ext.wrapper.api.common.model;

import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* Subset of the possible permissions in the EDC.
*
* @author [email protected]
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class PermissionDto {

@Schema(description = "Possible constraints for the permission",
requiredMode = RequiredMode.REQUIRED)
private ExpressionDto constraints;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,34 @@
package de.sovity.edc.ext.wrapper.api.common.model;

import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
* Opinionated subset of the EDC policy.
*
* @author [email protected]
*/
@Getter
@Setter
@ToString
@AllArgsConstructor
@Builder(toBuilder = true)
@RequiredArgsConstructor
@Schema(description = "Type-Safe OpenAPI generator friendly Policy DTO that supports an opinionated" +
" subset of the original EDC Policy Entity.")
@Schema(description = "Type-Safe OpenAPI generator friendly Policy DTO that supports an opinionated"
+ " subset of the original EDC Policy Entity.")
public class PolicyDto {
@Schema(description = "Legacy JSON as built by the Management API. Will be replaced " +
"in the future by a type-safe variant without polymorphisms that can be used " +
"for our generated clients.", requiredMode = Schema.RequiredMode.REQUIRED)
private Object legacyPolicy;

@Schema(description = "Legacy JSON as built by the Management API. Will be replaced "
+ "in the future by a type-safe variant without polymorphisms that can be used "
+ "for our generated clients.", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String legacyPolicy;

@Schema(description = "Permission for this policy", requiredMode = RequiredMode.NOT_REQUIRED)
private PermissionDto permission;
}
3 changes: 2 additions & 1 deletion extensions/wrapper/wrapper/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation(project(":extensions:wrapper:wrapper-common-api"))
implementation(project(":extensions:wrapper:wrapper-ee-api"))
api("${edcGroup}:contract-definition-api:${edcVersion}")
api("${edcGroup}:asset-api:${edcVersion}")
api("${edcGroup}:control-plane-spi:${edcVersion}")
api("${edcGroup}:core-spi:${edcVersion}")
api("${edcGroup}:policy-definition-api:${edcVersion}")
Expand All @@ -42,6 +43,7 @@ dependencies {
testImplementation("io.rest-assured:rest-assured:${restAssured}")
testImplementation("org.assertj:assertj-core:${assertj}")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testImplementation("org.mockito:mockito-core:5.3.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.3")
}

Expand All @@ -59,7 +61,6 @@ tasks.withType<io.swagger.v3.plugins.gradle.tasks.ResolveTask> {
resourcePackages = setOf("de.sovity.edc.ext.wrapper.api")
}


task<org.openapitools.generator.gradle.plugin.tasks.GenerateTask>("openApiGenerateTypeScriptClient") {
dependsOn("resolve")
generatorName.set("typescript-fetch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package de.sovity.edc.ext.wrapper;

import org.eclipse.edc.api.transformer.DtoTransformerRegistry;
import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration;
import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore;
import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore;
Expand Down Expand Up @@ -51,6 +52,8 @@ public class WrapperExtension implements ServiceExtension {
private ContractNegotiationStore contractNegotiationStore;
@Inject
private TransferProcessService transferProcessService;
@Inject
private DtoTransformerRegistry dtoTransformerRegistry;

@Override
public String name() {
Expand All @@ -67,7 +70,8 @@ public void initialize(ServiceExtensionContext context) {
transferProcessStore,
contractAgreementService,
contractNegotiationStore,
transferProcessService
transferProcessService,
dtoTransformerRegistry
);

wrapperExtensionContext.jaxRsResources().forEach(resource ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@
import de.sovity.edc.ext.wrapper.api.ui.services.TransferProcessStateService;
import de.sovity.edc.ext.wrapper.api.usecase.UseCaseResource;
import de.sovity.edc.ext.wrapper.api.usecase.services.KpiApiService;
import de.sovity.edc.ext.wrapper.api.usecase.services.OfferingService;
import de.sovity.edc.ext.wrapper.api.usecase.services.PolicyMappingService;
import de.sovity.edc.ext.wrapper.api.usecase.services.SupportedPolicyApiService;
import lombok.NoArgsConstructor;
import org.eclipse.edc.api.transformer.DtoTransformerRegistry;
import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore;
import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore;
import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore;
Expand All @@ -39,13 +42,14 @@
/**
* Manual Dependency Injection.
* <p>
* We want to develop as Java Backend Development is done, but we have
* no CDI / DI Framework to rely on.
* We want to develop as Java Backend Development is done, but we have no CDI / DI Framework to rely
* on.
* <p>
* EDC {@link Inject} only works in {@link WrapperExtension}.
*/
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class WrapperExtensionContextBuilder {

public static WrapperExtensionContext buildContext(
AssetIndex assetIndex,
ContractDefinitionStore contractDefinitionStore,
Expand All @@ -54,11 +58,13 @@ public static WrapperExtensionContext buildContext(
TransferProcessStore transferProcessStore,
ContractAgreementService contractAgreementService,
ContractNegotiationStore contractNegotiationStore,
TransferProcessService transferProcessService
TransferProcessService transferProcessService,
DtoTransformerRegistry dtoTransformerRegistry
) {
// UI API
var transferProcessStateService = new TransferProcessStateService();
var contractAgreementPageCardBuilder = new ContractAgreementPageCardBuilder(transferProcessStateService);
var contractAgreementPageCardBuilder = new ContractAgreementPageCardBuilder(
transferProcessStateService);
var contractAgreementDataFetcher = new ContractAgreementDataFetcher(
contractAgreementService,
contractNegotiationStore,
Expand All @@ -79,7 +85,11 @@ public static WrapperExtensionContext buildContext(
contractAgreementService
);
var supportedPolicyApiService = new SupportedPolicyApiService(policyEngine);
var useCaseResource = new UseCaseResource(kpiApiService, supportedPolicyApiService);
var policyMappingService = new PolicyMappingService();
var offeringService = new OfferingService(assetIndex, policyDefinitionStore,
contractDefinitionStore, dtoTransformerRegistry, policyMappingService);
var useCaseResource = new UseCaseResource(kpiApiService, supportedPolicyApiService,
offeringService);

// Collect all JAX-RS resources
return new WrapperExtensionContext(List.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,30 @@

package de.sovity.edc.ext.wrapper.api.ui.services;

import static de.sovity.edc.ext.wrapper.utils.EdcDateUtils.utcMillisToOffsetDateTime;
import static de.sovity.edc.ext.wrapper.utils.EdcDateUtils.utcSecondsToOffsetDateTime;
import static de.sovity.edc.ext.wrapper.utils.MapUtils.mapValues;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.sovity.edc.ext.wrapper.api.common.model.AssetDto;
import de.sovity.edc.ext.wrapper.api.common.model.PolicyDto;
import de.sovity.edc.ext.wrapper.api.ui.model.ContractAgreementCard;
import de.sovity.edc.ext.wrapper.api.ui.model.ContractAgreementDirection;
import de.sovity.edc.ext.wrapper.api.ui.model.ContractAgreementTransferProcess;
import java.util.Comparator;
import java.util.List;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation;
import org.eclipse.edc.connector.transfer.spi.types.TransferProcess;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.spi.types.domain.asset.Asset;
import org.jetbrains.annotations.NotNull;

import java.util.Comparator;
import java.util.List;

import static de.sovity.edc.ext.wrapper.utils.EdcDateUtils.utcMillisToOffsetDateTime;
import static de.sovity.edc.ext.wrapper.utils.EdcDateUtils.utcSecondsToOffsetDateTime;
import static de.sovity.edc.ext.wrapper.utils.MapUtils.mapValues;

@Slf4j
@RequiredArgsConstructor
public class ContractAgreementPageCardBuilder {
private final TransferProcessStateService transferProcessStateService;
Expand Down Expand Up @@ -67,23 +70,33 @@ private List<ContractAgreementTransferProcess> buildTransferProcesses(
) {
return transferProcessEntities.stream()
.map(this::buildContractAgreementTransfer)
.sorted(Comparator.comparing(ContractAgreementTransferProcess::getLastUpdatedDate).reversed())
.sorted(Comparator.comparing(ContractAgreementTransferProcess::getLastUpdatedDate)
.reversed())
.toList();
}

@NotNull
private ContractAgreementTransferProcess buildContractAgreementTransfer(TransferProcess transferProcessEntity) {
private ContractAgreementTransferProcess buildContractAgreementTransfer(
TransferProcess transferProcessEntity) {
var transferProcess = new ContractAgreementTransferProcess();
transferProcess.setTransferProcessId(transferProcessEntity.getId());
transferProcess.setLastUpdatedDate(utcMillisToOffsetDateTime(transferProcessEntity.getUpdatedAt()));
transferProcess.setState(transferProcessStateService.buildTransferProcessState(transferProcessEntity.getState()));
transferProcess.setLastUpdatedDate(
utcMillisToOffsetDateTime(transferProcessEntity.getUpdatedAt()));
transferProcess.setState(transferProcessStateService.buildTransferProcessState(
transferProcessEntity.getState()));
transferProcess.setErrorMessage(transferProcessEntity.getErrorDetail());
return transferProcess;
}

@NotNull
private PolicyDto buildPolicyDto(@NonNull Policy policy) {
return new PolicyDto(policy);
var mapper = new ObjectMapper();
try {
return PolicyDto.builder().legacyPolicy(mapper.writeValueAsString(policy)).build();
} catch (JsonProcessingException ex) {
log.error("Could not serialize policy: {}", ex.getMessage(), ex);
return PolicyDto.builder().build();
}
}

@NotNull
Expand Down
Loading

0 comments on commit e754e0d

Please sign in to comment.