Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Abide to the DataOfferCreationRequest.PolicyEnum rule #1055

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md).
#### Patch Changes

- Refactoring: Config as Java Code ([#1051](https://github.com/sovity/edc-ce/pull/1051))
- Fix issues with the Create Data Offer Endpoint ([PR#1055](https://github.com/sovity/edc-ce/pull/1055))

### Deployment Migration Notes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class DataOfferCreationRequest {
@Schema(description = "The asset to create", requiredMode = REQUIRED)
private UiAssetCreateRequest uiAssetCreateRequest;

@Schema(description = "Which policy to apply to this asset.", requiredMode = REQUIRED)
@Schema(description = "Which policy to apply to this asset creation.", requiredMode = REQUIRED)
private PolicyDefinitionChoiceEnum policy;

@Schema(description = "Policy Expression.", requiredMode = NOT_REQUIRED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@

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

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

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

public enum PolicyDefinitionChoiceEnum {
@Schema(description = "Only create the asset", requiredMode = REQUIRED)
DONT_PUBLISH,
@Schema(description = "Create the asset and assigns the always-true policy in the contract definition", requiredMode = REQUIRED)
PUBLISH_UNRESTRICTED,
@Schema(description = "Create the asset, a policy and a contract definition", requiredMode = REQUIRED)
PUBLISH_RESTRICTED
}
3 changes: 3 additions & 0 deletions extensions/wrapper/wrapper/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ dependencies {
annotationProcessor(libs.lombok)
compileOnly(libs.lombok)


implementation(project(":config"))

api(project(":extensions:wrapper:wrapper-api"))
api(project(":extensions:wrapper:wrapper-common-mappers"))
api(project(":utils:catalog-parser"))
Expand All @@ -22,6 +24,7 @@ dependencies {

implementation(project(":extensions:contract-termination"))
implementation(project(":extensions:database-direct-access"))
implementation(project(":extensions:policy-always-true"))
implementation(project(":extensions:sovity-messenger"))
implementation(project(":utils:jooq-database-access"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import de.sovity.edc.ext.wrapper.api.ui.pages.asset.AssetApiService;
import de.sovity.edc.ext.wrapper.api.ui.pages.contract_definitions.ContractDefinitionApiService;
import de.sovity.edc.ext.wrapper.api.ui.pages.policy.PolicyDefinitionApiService;
import de.sovity.edc.extension.policy.AlwaysTruePolicyConstants;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.eclipse.edc.spi.types.domain.asset.Asset;
Expand Down Expand Up @@ -66,45 +67,90 @@ private boolean isIdAvailable(DSLContext dsl, Table<?> table, TableField<?, Stri
}

public IdResponseDto createDataOffer(DSLContext dsl, DataOfferCreationRequest dataOfferCreationRequest) {

val commonId = dataOfferCreationRequest.getUiAssetCreateRequest().getId();

val assetIdExists = checkIfAssetIdAvailable(dsl, commonId).isAvailable();
if (!assetIdExists) {
throw new InvalidRequestException("Asset with id %s already exists".formatted(commonId));
}
return switch (dataOfferCreationRequest.getPolicy()) {
case DONT_PUBLISH -> createButDontPublish(dsl, dataOfferCreationRequest, commonId);
case PUBLISH_UNRESTRICTED -> createAndPublishUnrestricted(dsl, dataOfferCreationRequest, commonId);
case PUBLISH_RESTRICTED -> createAndPublishRestricted(dsl, dataOfferCreationRequest, commonId);
};
}

val policyIdExists = checkIfPolicyIdAvailable(dsl, commonId).isAvailable();
if (!policyIdExists) {
throw new InvalidRequestException("Policy with id %s already exists".formatted(commonId));
}
private @NotNull IdResponseDto createAndPublishUnrestricted(DSLContext dsl, DataOfferCreationRequest dataOfferCreationRequest, String commonId) {
val assetId = commonId;
val contractDefinitionId = commonId;
val policyId = AlwaysTruePolicyConstants.POLICY_DEFINITION_ID;

val contractDefinitionIdExists = checkIfContractDefinitionIdAvailable(dsl, commonId).isAvailable();
if (!contractDefinitionIdExists) {
throw new InvalidRequestException("Contract definition with id %s already exists".formatted(commonId));
}
checkAssetIdAvailable(dsl, assetId);
checkContractDefinitionIdAvailable(dsl, contractDefinitionId);

assetApiService.createAsset(dataOfferCreationRequest.getUiAssetCreateRequest());

return createContractDefinition(assetId, policyId, contractDefinitionId);
}
ununhexium marked this conversation as resolved.
Show resolved Hide resolved

private @NotNull IdResponseDto createAndPublishRestricted(DSLContext dsl, DataOfferCreationRequest dataOfferCreationRequest, String commonId) {
val assetId = commonId;
val policyId = commonId;
val contractDefinitionId = commonId;

checkAssetIdAvailable(dsl, assetId);
checkPolicyIdAvailable(dsl, policyId);
checkContractDefinitionIdAvailable(dsl, contractDefinitionId);

assetApiService.createAsset(dataOfferCreationRequest.getUiAssetCreateRequest());

val maybeNewPolicy = Optional.ofNullable(dataOfferCreationRequest.getUiPolicyExpression());

maybeNewPolicy.ifPresent(
policy -> policyDefinitionApiService.createPolicyDefinitionV2(new PolicyDefinitionCreateDto(commonId, policy)));
policy -> policyDefinitionApiService.createPolicyDefinitionV2(new PolicyDefinitionCreateDto(policyId, policy)));
ununhexium marked this conversation as resolved.
Show resolved Hide resolved

createContractDefinition(assetId, policyId, contractDefinitionId);

return new IdResponseDto(commonId, OffsetDateTime.now());
}

private @NotNull IdResponseDto createButDontPublish(DSLContext dsl, DataOfferCreationRequest dataOfferCreationRequest, String commonId) {
checkAssetIdAvailable(dsl, commonId);
return assetApiService.createAsset(dataOfferCreationRequest.getUiAssetCreateRequest());
}

private void checkContractDefinitionIdAvailable(DSLContext dsl, String commonId) {
val contractDefinitionIdExists = checkIfContractDefinitionIdAvailable(dsl, commonId).isAvailable();
if (!contractDefinitionIdExists) {
throw new InvalidRequestException("Contract definition with id %s already exists".formatted(commonId));
}
}

private void checkPolicyIdAvailable(DSLContext dsl, String commonId) {
val policyIdExists = checkIfPolicyIdAvailable(dsl, commonId).isAvailable();
if (!policyIdExists) {
throw new InvalidRequestException("Policy with id %s already exists".formatted(commonId));
}
}

private void checkAssetIdAvailable(DSLContext dsl, String commonId) {
val assetIdExists = checkIfAssetIdAvailable(dsl, commonId).isAvailable();
if (!assetIdExists) {
throw new InvalidRequestException("Asset with id %s already exists".formatted(commonId));
}
}

private @NotNull IdResponseDto createContractDefinition(String assetId, String policyId, String contractDefinitionId) {
val cd = new ContractDefinitionRequest();
cd.setAssetSelector(List.of(UiCriterion.builder()
.operandLeft(Asset.PROPERTY_ID)
.operator(UiCriterionOperator.EQ)
.operandRight(UiCriterionLiteral.builder()
.type(UiCriterionLiteralType.VALUE)
.value(commonId)
.value(assetId)
.build())
.build()));
cd.setAccessPolicyId(commonId);
cd.setContractPolicyId(commonId);
cd.setContractDefinitionId(commonId);
cd.setAccessPolicyId(policyId);
cd.setContractPolicyId(policyId);
cd.setContractDefinitionId(contractDefinitionId);

contractDefinitionApiService.createContractDefinition(cd);

return new IdResponseDto(commonId, OffsetDateTime.now());
return contractDefinitionApiService.createContractDefinition(cd);
}
}
89 changes: 84 additions & 5 deletions tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ void canMakeAnOnDemandDataSourceAvailable(
}

@Test
void canCreateDataOfferWithoutAnyNewPolicy(
void canCreateDataOfferWithoutAnyNewPolicyNotContractDefinition(
@Provider EdcClient providerClient
) {
// arrange
Expand Down Expand Up @@ -728,9 +728,7 @@ void canCreateDataOfferWithoutAnyNewPolicy(
assertThat(getAllPoliciesExceptTheAlwaysTruePolicy(providerClient)).hasSize(0);

assertThat(providerClient.uiApi().getContractDefinitionPage().getContractDefinitions())
.extracting(ContractDefinitionEntry::getContractDefinitionId)
.first()
.isEqualTo(assetId);
.hasSize(0);
}

@Test
Expand All @@ -755,7 +753,7 @@ void canCreateDataOfferWithNewPolicy(

val dataOfferCreateRequest = new DataOfferCreationRequest(
asset,
DataOfferCreationRequest.PolicyEnum.DONT_PUBLISH,
DataOfferCreationRequest.PolicyEnum.PUBLISH_RESTRICTED,
UiPolicyExpression.builder()
.constraint(UiPolicyConstraint.builder()
.left("foo")
Expand Down Expand Up @@ -921,6 +919,87 @@ void dontCreateAnythingIfTheContractDefinitionAlreadyExists(
.isEqualTo(assetId);
}

@Test
void reuseTheAlwaysTruePolicyWhenPublishingUnrestricted(
E2eScenario scenario,
@Provider EdcClient providerClient
) {
// arrange
val assetId = "assetId";

// act
providerClient.uiApi()
.createDataOffer(DataOfferCreationRequest.builder()
.uiAssetCreateRequest(UiAssetCreateRequest.builder()
.id(assetId)
.dataSource(UiDataSource.builder()
.type(DataSourceType.ON_REQUEST)
.onRequest(UiDataSourceOnRequest.builder()
.contactEmail("foo@example.com")
.contactPreferredEmailSubject("Subject")
.build())
.build())
.build())
.policy(DataOfferCreationRequest.PolicyEnum.PUBLISH_UNRESTRICTED)
.build());

// assert
assertThat(providerClient.uiApi().getAssetPage().getAssets())
// the asset used for the placeholder contract definition
.hasSize(1)
.extracting(UiAsset::getAssetId)
.first()
.isEqualTo(assetId);

assertThat(getAllPoliciesExceptTheAlwaysTruePolicy(providerClient)).hasSize(0);

assertThat(providerClient.uiApi().getContractDefinitionPage().getContractDefinitions())
.hasSize(1)
.filteredOn(it -> it.getContractDefinitionId().equals(assetId))
.extracting(ContractDefinitionEntry::getContractDefinitionId)
.first()
// the already existing one, before the data offer creation attempt
.isEqualTo(assetId);
}

@Test
void onlyCreateTheAssetWhenDontPublish(
E2eScenario scenario,
@Provider EdcClient providerClient
) {
// arrange
val assetId = "assetId";

// act
providerClient.uiApi()
.createDataOffer(DataOfferCreationRequest.builder()
.uiAssetCreateRequest(UiAssetCreateRequest.builder()
.id(assetId)
.dataSource(UiDataSource.builder()
.type(DataSourceType.ON_REQUEST)
.onRequest(UiDataSourceOnRequest.builder()
.contactEmail("foo@example.com")
.contactPreferredEmailSubject("Subject")
.build())
.build())
.build())
.policy(DataOfferCreationRequest.PolicyEnum.DONT_PUBLISH)
.build());

// assert
assertThat(providerClient.uiApi().getAssetPage().getAssets())
// the asset used for the placeholder contract definition
.hasSize(1)
.extracting(UiAsset::getAssetId)
.first()
.isEqualTo(assetId);

assertThat(getAllPoliciesExceptTheAlwaysTruePolicy(providerClient)).hasSize(0);

assertThat(providerClient.uiApi().getContractDefinitionPage().getContractDefinitions())
.hasSize(0);
}

private static @NotNull List<PolicyDefinitionDto> getAllPoliciesExceptTheAlwaysTruePolicy(EdcClient edcClient) {
return edcClient.uiApi().getPolicyDefinitionPage().getPolicies().stream().filter(it -> !it.getPolicyDefinitionId().equals(
AlwaysTruePolicyConstants.POLICY_DEFINITION_ID)).toList();
Expand Down
Loading