From 0a115b68c83df859e051ffe3f9750d64765d49c7 Mon Sep 17 00:00:00 2001 From: Michel Barret Date: Wed, 15 Jan 2025 10:33:00 +0100 Subject: [PATCH] feat: rework export service APIM-8200 --- gravitee-apim-bom/pom.xml | 2 +- gravitee-apim-console-webui/yarn.lock | 2 +- .../model/v4/flow/AbstractFlow.java | 13 +- .../management/api/MembershipRepository.java | 2 + .../management/JdbcMembershipRepository.java | 19 +- .../management/MongoMembershipRepository.java | 14 ++ .../ApiExportDomainService.java | 13 +- .../import_definition/ApiDescriptor.java | 192 ++++++++++++++++++ .../import_definition/GraviteeDefinition.java | 118 +++++++++-- .../import_definition/PlanDescriptor.java | 84 ++++++++ .../crud_service/MembershipCrudService.java | 2 + .../crud_service/MetadataCrudService.java | 2 + .../plan/crud_service/PlanCrudService.java | 3 + .../use_case/ScoreApiRequestUseCase.java | 41 ++-- .../crud_service/WorkflowCrudService.java | 2 + .../adapter/GraviteeDefinitionAdapter.java | 86 +++++++- .../apim/infra/adapter/MemberAdapter.java | 3 +- .../documentation/PageCrudServiceImpl.java | 5 + .../membership/MembershipCrudServiceImpl.java | 15 ++ .../metadata/MetadataCrudServiceImpl.java | 13 ++ .../plan/PlanCrudServiceImpl.java | 13 ++ .../workflow/WorkflowCrudServiceImpl.java | 15 ++ .../api/ApiExportDomainServiceImpl.java | 175 ++++++++++++++-- .../java/fixtures/core/model/ApiFixtures.java | 5 +- .../model/GraviteeDefinitionFixtures.java | 15 +- .../MembershipCrudServiceInMemory.java | 9 + .../inmemory/MetadataCrudServiceInMemory.java | 6 + .../inmemory/PlanCrudServiceInMemory.java | 6 + .../inmemory/WorkflowCrudServiceInMemory.java | 9 + .../use_case/ScoreApiRequestUseCaseTest.java | 89 +++++--- .../api/ApiExportDomainServiceImplTest.java | 74 ++++--- 31 files changed, 904 insertions(+), 143 deletions(-) create mode 100644 gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/ApiDescriptor.java create mode 100644 gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/PlanDescriptor.java diff --git a/gravitee-apim-bom/pom.xml b/gravitee-apim-bom/pom.xml index a607903120b..38327c13916 100644 --- a/gravitee-apim-bom/pom.xml +++ b/gravitee-apim-bom/pom.xml @@ -736,4 +736,4 @@ - \ No newline at end of file + diff --git a/gravitee-apim-console-webui/yarn.lock b/gravitee-apim-console-webui/yarn.lock index 86f3af7402c..772385c203e 100644 --- a/gravitee-apim-console-webui/yarn.lock +++ b/gravitee-apim-console-webui/yarn.lock @@ -23385,7 +23385,7 @@ __metadata: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin": version: 5.5.4 - resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=5adc0c" + resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=b45daf" bin: tsc: bin/tsc tsserver: bin/tsserver diff --git a/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/v4/flow/AbstractFlow.java b/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/v4/flow/AbstractFlow.java index 88665e1b969..efa865f542b 100644 --- a/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/v4/flow/AbstractFlow.java +++ b/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/v4/flow/AbstractFlow.java @@ -20,10 +20,10 @@ import io.gravitee.definition.model.v4.flow.step.Step; import jakarta.validation.constraints.NotEmpty; import java.io.Serializable; +import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -55,9 +55,12 @@ public abstract class AbstractFlow implements Serializable { @JsonIgnore protected List computePlugins(List step) { - return Optional + return Stream .ofNullable(step) - .map(r -> r.stream().filter(Step::isEnabled).map(Step::getPlugins).flatMap(List::stream).collect(Collectors.toList())) - .orElse(List.of()); + .flatMap(Collection::stream) + .filter(Step::isEnabled) + .map(Step::getPlugins) + .flatMap(List::stream) + .toList(); } } diff --git a/gravitee-apim-repository/gravitee-apim-repository-api/src/main/java/io/gravitee/repository/management/api/MembershipRepository.java b/gravitee-apim-repository/gravitee-apim-repository-api/src/main/java/io/gravitee/repository/management/api/MembershipRepository.java index d2adf4a2aef..a94b4ad5685 100644 --- a/gravitee-apim-repository/gravitee-apim-repository-api/src/main/java/io/gravitee/repository/management/api/MembershipRepository.java +++ b/gravitee-apim-repository/gravitee-apim-repository-api/src/main/java/io/gravitee/repository/management/api/MembershipRepository.java @@ -41,6 +41,8 @@ public interface MembershipRepository extends FindAllRepository { */ List deleteByReferenceIdAndReferenceType(String referenceId, MembershipReferenceType referenceType) throws TechnicalException; + List findByReferenceIdAndReferenceType(String referenceId, MembershipReferenceType referenceType) throws TechnicalException; + /** * find membership by id. * @param membershipId the membership id diff --git a/gravitee-apim-repository/gravitee-apim-repository-jdbc/src/main/java/io/gravitee/repository/jdbc/management/JdbcMembershipRepository.java b/gravitee-apim-repository/gravitee-apim-repository-jdbc/src/main/java/io/gravitee/repository/jdbc/management/JdbcMembershipRepository.java index 8e8703a7b00..d4657a10cb2 100644 --- a/gravitee-apim-repository/gravitee-apim-repository-jdbc/src/main/java/io/gravitee/repository/jdbc/management/JdbcMembershipRepository.java +++ b/gravitee-apim-repository/gravitee-apim-repository-jdbc/src/main/java/io/gravitee/repository/jdbc/management/JdbcMembershipRepository.java @@ -77,7 +77,7 @@ public List deleteByReferenceIdAndReferenceType(String referenceId, Memb LOGGER.debug("JdbcMembershipRepository.deleteByReferenceIdAndReferenceType({}, {})", referenceId, referenceType); try { List rows = jdbcTemplate.queryForList( - "select id from " + this.tableName + " where reference_type = ?" + " and reference_id = ?", + "select id from " + this.tableName + " where reference_type = ? and reference_id = ?", String.class, referenceType.name(), referenceId @@ -85,7 +85,7 @@ public List deleteByReferenceIdAndReferenceType(String referenceId, Memb if (!rows.isEmpty()) { jdbcTemplate.update( - "delete from " + this.tableName + " where reference_type = ?" + " and reference_id = ?", + "delete from " + this.tableName + " where reference_type = ? and reference_id = ?", referenceType.name(), referenceId ); @@ -98,6 +98,21 @@ public List deleteByReferenceIdAndReferenceType(String referenceId, Memb } } + @Override + public List findByReferenceIdAndReferenceType(String referenceId, MembershipReferenceType referenceType) + throws TechnicalException { + LOGGER.debug("JdbcMembershipRepository.deleteByReferenceIdAndReferenceType({}, {})", referenceId, referenceType); + try { + return jdbcTemplate.query( + getOrm().getSelectAllSql() + " where reference_type = " + referenceType.name() + " and reference_id = " + referenceId, + getOrm().getRowMapper() + ); + } catch (final Exception ex) { + LOGGER.error("Failed to find memberships for refId: {}/{}", referenceId, referenceType, ex); + throw new TechnicalException("Failed to find memberships by reference", ex); + } + } + @Override public Set findByIds(Set membershipIds) throws TechnicalException { LOGGER.debug("JdbcMembershipRepository.findByIds({})", membershipIds); diff --git a/gravitee-apim-repository/gravitee-apim-repository-mongodb/src/main/java/io/gravitee/repository/mongodb/management/MongoMembershipRepository.java b/gravitee-apim-repository/gravitee-apim-repository-mongodb/src/main/java/io/gravitee/repository/mongodb/management/MongoMembershipRepository.java index cefbf27cf12..49e7fc1b34e 100644 --- a/gravitee-apim-repository/gravitee-apim-repository-mongodb/src/main/java/io/gravitee/repository/mongodb/management/MongoMembershipRepository.java +++ b/gravitee-apim-repository/gravitee-apim-repository-mongodb/src/main/java/io/gravitee/repository/mongodb/management/MongoMembershipRepository.java @@ -15,6 +15,8 @@ */ package io.gravitee.repository.mongodb.management; +import static io.gravitee.repository.mongodb.utils.CollectionUtils.stream; + import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.MembershipRepository; import io.gravitee.repository.management.model.Membership; @@ -117,6 +119,18 @@ public List deleteByReferenceIdAndReferenceType(String referenceId, Memb } } + @Override + public List findByReferenceIdAndReferenceType(String referenceId, MembershipReferenceType referenceType) + throws TechnicalException { + logger.debug("Find memberships by reference [{}/{}]", referenceId, referenceType); + try { + return stream(internalMembershipRepo.findByMemberIdAndMemberType(referenceId, referenceType.name())).map(this::map).toList(); + } catch (Exception ex) { + logger.error("Failed to find memberships by ref: {}/{}", referenceId, referenceType, ex); + throw new TechnicalException("Failed to find memberships by ref"); + } + } + @Override public Optional findById(String membershipId) throws TechnicalException { logger.debug("Find membership by ID [{}]", membershipId); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ApiExportDomainService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ApiExportDomainService.java index 5f548d10581..fd74a44823b 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ApiExportDomainService.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ApiExportDomainService.java @@ -16,9 +16,18 @@ package io.gravitee.apim.core.api.domain_service; import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; -import io.gravitee.apim.core.api.model.import_definition.ImportDefinition; import io.gravitee.apim.core.audit.model.AuditInfo; +import java.util.Collection; public interface ApiExportDomainService { - GraviteeDefinition export(String apiId, AuditInfo auditInfo); + enum Excludable { + GROUPS, + PLANS, + MEMBERS, + PAGES_MEDIA, + METADATA, + SHARED_CONFIGURATION, + } + + GraviteeDefinition export(String apiId, AuditInfo auditInfo, Collection excludeAdditionalData); } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/ApiDescriptor.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/ApiDescriptor.java new file mode 100644 index 00000000000..922326276ba --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/ApiDescriptor.java @@ -0,0 +1,192 @@ +/* + * Copyright © 2015 The Gravitee team (http://gravitee.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.gravitee.apim.core.api.model.import_definition; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.gravitee.common.component.Lifecycle; +import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.ResponseTemplate; +import io.gravitee.definition.model.v4.ApiType; +import io.gravitee.definition.model.v4.analytics.Analytics; +import io.gravitee.definition.model.v4.endpointgroup.EndpointGroup; +import io.gravitee.definition.model.v4.flow.Flow; +import io.gravitee.definition.model.v4.flow.execution.FlowExecution; +import io.gravitee.definition.model.v4.listener.Listener; +import io.gravitee.definition.model.v4.nativeapi.NativeEndpointGroup; +import io.gravitee.definition.model.v4.nativeapi.NativeFlow; +import io.gravitee.definition.model.v4.nativeapi.NativeListener; +import io.gravitee.definition.model.v4.property.Property; +import io.gravitee.definition.model.v4.resource.Resource; +import io.gravitee.definition.model.v4.service.ApiServices; +import io.gravitee.rest.api.model.PrimaryOwnerEntity; +import io.gravitee.rest.api.model.Visibility; +import io.gravitee.rest.api.model.WorkflowState; +import io.gravitee.rest.api.model.api.ApiLifecycleState; +import io.gravitee.rest.api.model.context.OriginContext; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Set; +import lombok.Builder; + +public sealed interface ApiDescriptor { + String id(); + String crossId(); + String name(); + String apiVersion(); + DefinitionVersion definitionVersion(); + ApiType type(); + String description(); + Instant deployedAt(); + Instant createdAt(); + Instant updatedAt(); + boolean disableMembershipNotifications(); + Map metadata(); + Set groups(); + Lifecycle.State state(); + Visibility visibility(); + List labels(); + ApiLifecycleState lifecycleState(); + Set tags(); + PrimaryOwnerEntity primaryOwner(); + Set categories(); + OriginContext originContext(); + WorkflowState workflowState(); + String picture(); + String background(); + + @Builder + record ApiDescriptorV4( + String id, + String crossId, + String name, + String apiVersion, + ApiType type, + String description, + Instant deployedAt, + Instant createdAt, + Instant updatedAt, + boolean disableMembershipNotifications, + Map metadata, + Set groups, + Lifecycle.State state, + Visibility visibility, + List labels, + ApiLifecycleState lifecycleState, + Set tags, + PrimaryOwnerEntity primaryOwner, + Set categories, + OriginContext originContext, + WorkflowState workflowState, + String picture, + String background, + List listeners, + List endpointGroups, + Analytics analytics, + FlowExecution flowExecution, + List flows, + Map> responseTemplates, + List properties, + List resources, + ApiServices services, + boolean failover + ) + implements ApiDescriptor { + @JsonProperty("definitionVersion") + @Override + public DefinitionVersion definitionVersion() { + return DefinitionVersion.V4; + } + } + + @Builder + record ApiDescriptorNative( + String id, + String crossId, + String name, + String apiVersion, + String description, + Instant deployedAt, + Instant createdAt, + Instant updatedAt, + boolean disableMembershipNotifications, + Map metadata, + Set groups, + Lifecycle.State state, + Visibility visibility, + List labels, + ApiLifecycleState lifecycleState, + Set tags, + PrimaryOwnerEntity primaryOwner, + Set categories, + OriginContext originContext, + WorkflowState workflowState, + String picture, + String background, + List listeners, + List endpointGroups, + List flows, + List properties, + List resources + ) + implements ApiDescriptor { + @JsonProperty("definitionVersion") + @Override + public DefinitionVersion definitionVersion() { + return DefinitionVersion.V4; + } + + @JsonProperty("type") + @Override + public ApiType type() { + return ApiType.NATIVE; + } + } + + @Builder + record ApiDescriptorFederated( + String id, + String crossId, + String name, + String apiVersion, + ApiType type, + String description, + Instant deployedAt, + Instant createdAt, + Instant updatedAt, + boolean disableMembershipNotifications, + Map metadata, + Set groups, + Lifecycle.State state, + Visibility visibility, + List labels, + ApiLifecycleState lifecycleState, + Set tags, + PrimaryOwnerEntity primaryOwner, + Set categories, + OriginContext originContext, + WorkflowState workflowState, + String picture, + String background + ) + implements ApiDescriptor { + @JsonProperty("definitionVersion") + @Override + public DefinitionVersion definitionVersion() { + return DefinitionVersion.FEDERATED; + } + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/GraviteeDefinition.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/GraviteeDefinition.java index c3c0da449dc..322938632e5 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/GraviteeDefinition.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/GraviteeDefinition.java @@ -16,37 +16,113 @@ package io.gravitee.apim.core.api.model.import_definition; import io.gravitee.apim.core.api.model.NewApiMetadata; -import io.gravitee.apim.core.documentation.model.Page; import io.gravitee.apim.core.media.model.Media; -import io.gravitee.apim.core.plan.model.PlanWithFlows; +import io.gravitee.common.util.Version; +import io.gravitee.common.utils.TimeProvider; +import java.time.Instant; +import java.util.Collection; import java.util.List; import java.util.Set; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Represents the definition of an exported API. */ -@AllArgsConstructor -@NoArgsConstructor -@Data -@Builder(toBuilder = true) -public class GraviteeDefinition { +public sealed interface GraviteeDefinition { + Export export(); + ApiDescriptor api(); + Set members(); + Set metadata(); + List pages(); + Collection plans(); + List apiMedia(); + String apiPicture(); + String apiBackground(); - private ApiExport api; + @Builder(toBuilder = true) + record V4( + Export export, + ApiDescriptor.ApiDescriptorV4 api, + Set members, + Set metadata, + List pages, + Collection plans, + List apiMedia, + String apiPicture, + String apiBackground + ) + implements GraviteeDefinition { + public V4( + ApiDescriptor.ApiDescriptorV4 api, + Set members, + Set metadata, + List pages, + Collection plans, + List apiMedia, + String apiPicture, + String apiBackground + ) { + this(new Export(), api, members, metadata, pages, plans, apiMedia, apiPicture, apiBackground); + } + } - private Set members; + @Builder(toBuilder = true) + record Native( + Export export, + ApiDescriptor.ApiDescriptorNative api, + Set members, + Set metadata, + List pages, + Collection plans, + List apiMedia, + String apiPicture, + String apiBackground + ) + implements GraviteeDefinition { + public Native( + ApiDescriptor.ApiDescriptorNative api, + Set members, + Set metadata, + List pages, + Collection plans, + List apiMedia, + String apiPicture, + String apiBackground + ) { + this(new Export(), api, members, metadata, pages, plans, apiMedia, apiPicture, apiBackground); + } + } - private Set metadata; + @Builder(toBuilder = true) + record GraviteeDefinitionFederated( + Export export, + ApiDescriptor.ApiDescriptorFederated api, + Set members, + Set metadata, + List pages, + Collection plans, + List apiMedia, + String apiPicture, + String apiBackground + ) + implements GraviteeDefinition { + public GraviteeDefinitionFederated( + ApiDescriptor.ApiDescriptorFederated api, + Set members, + Set metadata, + List pages, + Collection plans, + List apiMedia, + String apiPicture, + String apiBackground + ) { + this(new Export(), api, members, metadata, pages, plans, apiMedia, apiPicture, apiBackground); + } + } - private List pages; - - private Set plans; - - private List apiMedia; - - private String apiPicture; - private String apiBackground; + record Export(Instant date, String exportVersion, String apimVersion) { + public Export() { + this(TimeProvider.instantNow(), "1", Version.RUNTIME_VERSION.MAJOR_VERSION); + } + } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/PlanDescriptor.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/PlanDescriptor.java new file mode 100644 index 00000000000..19cdf9dcd64 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/model/import_definition/PlanDescriptor.java @@ -0,0 +1,84 @@ +/* + * Copyright © 2015 The Gravitee team (http://gravitee.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.gravitee.apim.core.api.model.import_definition; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.gravitee.apim.core.plan.model.Plan; +import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; +import io.gravitee.definition.model.v4.flow.AbstractFlow; +import io.gravitee.definition.model.v4.plan.PlanMode; +import io.gravitee.definition.model.v4.plan.PlanSecurity; +import io.gravitee.definition.model.v4.plan.PlanStatus; +import java.time.Instant; +import java.util.List; +import java.util.Set; +import lombok.Builder; + +public sealed interface PlanDescriptor { + String id(); + String name(); + + PlanStatus status(); + + @JsonIgnore + default boolean closed() { + return PlanStatus.CLOSED.equals(status()); + } + + @Builder + record PlanDescriptorV4( + String id, + String crossId, + String name, + DefinitionVersion definitionVersion, + String description, + + Instant createdAt, + Instant updatedAt, + Instant publishedAt, + Instant closedAt, + + Plan.PlanValidationType validation, + Plan.PlanType type, + PlanMode mode, + PlanSecurity security, + + Set tags, + + String selectionRule, + PlanStatus status, + String apiId, + String environmentId, + + int order, + List characteristics, + List excludedGroups, + boolean commentRequired, + String commentMessage, + String generalConditions, + ApiType apiType, + + List flows + ) + implements PlanDescriptor { + @JsonProperty("tags") + public Set tags() { + return tags != null ? tags : Set.of(); + } + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/membership/crud_service/MembershipCrudService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/membership/crud_service/MembershipCrudService.java index 014d44b3efa..d8786c48f53 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/membership/crud_service/MembershipCrudService.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/membership/crud_service/MembershipCrudService.java @@ -16,8 +16,10 @@ package io.gravitee.apim.core.membership.crud_service; import io.gravitee.apim.core.membership.model.Membership; +import java.util.Collection; public interface MembershipCrudService { Membership create(Membership membership); void delete(String id); + Collection findByApiId(String apiId); } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/metadata/crud_service/MetadataCrudService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/metadata/crud_service/MetadataCrudService.java index c288081f170..05f2034e84f 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/metadata/crud_service/MetadataCrudService.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/metadata/crud_service/MetadataCrudService.java @@ -17,11 +17,13 @@ import io.gravitee.apim.core.metadata.model.Metadata; import io.gravitee.apim.core.metadata.model.MetadataId; +import java.util.Collection; import java.util.Optional; public interface MetadataCrudService { Metadata create(Metadata metadata); Optional findById(MetadataId id); + Collection findByApiId(String id); Metadata update(Metadata metadata); void delete(MetadataId metadataId); } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/plan/crud_service/PlanCrudService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/plan/crud_service/PlanCrudService.java index c569f0f0ee9..033b3387a41 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/plan/crud_service/PlanCrudService.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/plan/crud_service/PlanCrudService.java @@ -16,6 +16,7 @@ package io.gravitee.apim.core.plan.crud_service; import io.gravitee.apim.core.plan.model.Plan; +import java.util.Collection; import java.util.Optional; public interface PlanCrudService { @@ -23,6 +24,8 @@ public interface PlanCrudService { Optional findById(String planId); + Collection findByApiId(String apiId); + Plan create(Plan plan); Plan update(Plan plan); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCase.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCase.java index d2e3343422c..0f8a2248499 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCase.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCase.java @@ -20,6 +20,7 @@ import io.gravitee.apim.core.api.crud_service.ApiCrudService; import io.gravitee.apim.core.api.domain_service.ApiExportDomainService; import io.gravitee.apim.core.api.exception.ApiNotFoundException; +import io.gravitee.apim.core.api.model.import_definition.ApiDescriptor; import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; import io.gravitee.apim.core.async_job.crud_service.AsyncJobCrudService; import io.gravitee.apim.core.async_job.model.AsyncJob; @@ -37,12 +38,12 @@ import io.gravitee.apim.core.scoring.service_provider.ScoringProvider; import io.gravitee.apim.core.utils.StringUtils; import io.gravitee.common.utils.TimeProvider; -import io.gravitee.definition.model.DefinitionVersion; import io.gravitee.rest.api.service.common.UuidString; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Single; +import java.util.EnumSet; import java.util.List; import lombok.RequiredArgsConstructor; @@ -80,7 +81,13 @@ public Completable execute(Input input) { .toList(); var export$ = Flowable - .fromCallable(() -> apiExportDomainService.export(input.apiId, input.auditInfo)) + .fromCallable(() -> + apiExportDomainService.export( + input.apiId, + input.auditInfo, + EnumSet.of(ApiExportDomainService.Excludable.SHARED_CONFIGURATION) + ) + ) .map(this::assetToScore) // export service throw error in some case (like if API isn't V4) .onErrorResumeNext(th -> Flowable.empty()); @@ -120,27 +127,25 @@ private ScoreRequest.AssetToScore assetToScore(Page page) { } private ScoreRequest.AssetToScore assetToScore(GraviteeDefinition definition) throws JsonProcessingException { - if (definition.getApi().getEndpointGroups() != null) { - // remove shared configuration because this produce some errors in scoring (invalid reference: APIM-7877) - definition.getApi().getEndpointGroups().forEach(endpoint -> endpoint.setSharedConfiguration((String) null)); - } return new ScoreRequest.AssetToScore( - definition.getApi().getId(), - new ScoreRequest.AssetType( - ScoringAssetType.GRAVITEE_DEFINITION, - definition.getApi().getDefinitionVersion() == DefinitionVersion.FEDERATED - ? ScoreRequest.Format.GRAVITEE_FEDERATED - : switch (definition.getApi().getType()) { - case PROXY -> ScoreRequest.Format.GRAVITEE_PROXY; - case MESSAGE -> ScoreRequest.Format.GRAVITEE_MESSAGE; - default -> null; - } - ), - definition.getApi().getName(), + definition.api().id(), + new ScoreRequest.AssetType(ScoringAssetType.GRAVITEE_DEFINITION, getFormat(definition)), + definition.api().name(), graviteeDefinitionSerializer.serialize(definition) ); } + private static ScoreRequest.Format getFormat(GraviteeDefinition definition) { + if (definition.api() instanceof ApiDescriptor.ApiDescriptorFederated) { + return ScoreRequest.Format.GRAVITEE_FEDERATED; + } + return switch (definition.api().type()) { + case PROXY -> ScoreRequest.Format.GRAVITEE_PROXY; + case MESSAGE -> ScoreRequest.Format.GRAVITEE_MESSAGE; + default -> null; + }; + } + private Maybe customRuleset(ScoringRuleset scoringRuleset) { if (StringUtils.isEmpty(scoringRuleset.payload())) { return Maybe.empty(); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/workflow/crud_service/WorkflowCrudService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/workflow/crud_service/WorkflowCrudService.java index 94f838176f1..f2dd9512db2 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/workflow/crud_service/WorkflowCrudService.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/workflow/crud_service/WorkflowCrudService.java @@ -16,7 +16,9 @@ package io.gravitee.apim.core.workflow.crud_service; import io.gravitee.apim.core.workflow.model.Workflow; +import java.util.Collection; public interface WorkflowCrudService { Workflow create(Workflow workflow); + Collection findByApiId(String apiId); } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/GraviteeDefinitionAdapter.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/GraviteeDefinitionAdapter.java index b9169709f75..4c2439e8fe1 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/GraviteeDefinitionAdapter.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/GraviteeDefinitionAdapter.java @@ -15,11 +15,29 @@ */ package io.gravitee.apim.infra.adapter; -import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; -import io.gravitee.apim.core.api.model.import_definition.PlanExport; -import io.gravitee.rest.api.model.v4.api.ExportApiEntity; -import io.gravitee.rest.api.model.v4.plan.BasePlanEntity; -import io.gravitee.rest.api.model.v4.plan.GenericPlanEntity; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; + +import io.gravitee.apim.core.api.model.Api; +import io.gravitee.apim.core.api.model.NewApiMetadata; +import io.gravitee.apim.core.api.model.import_definition.ApiDescriptor; +import io.gravitee.apim.core.api.model.import_definition.PageExport; +import io.gravitee.apim.core.api.model.import_definition.PlanDescriptor; +import io.gravitee.apim.core.documentation.model.Page; +import io.gravitee.apim.core.media.model.Media; +import io.gravitee.apim.core.membership.model.PrimaryOwnerEntity; +import io.gravitee.apim.core.metadata.model.Metadata; +import io.gravitee.apim.core.plan.model.Plan; +import io.gravitee.definition.model.v4.endpointgroup.EndpointGroup; +import io.gravitee.rest.api.model.MediaEntity; +import io.gravitee.rest.api.model.WorkflowState; +import io.gravitee.rest.api.model.v4.nativeapi.NativePlanEntity; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @@ -31,9 +49,59 @@ public interface GraviteeDefinitionAdapter { GraviteeDefinitionAdapter INSTANCE = Mappers.getMapper(GraviteeDefinitionAdapter.class); Logger logger = LoggerFactory.getLogger(GraviteeDefinitionAdapter.class); - @Mapping(target = "api", source = "apiEntity") - GraviteeDefinition map(ExportApiEntity source); + List mapPage(Collection source); + + List mapMedia(Collection src); + + Collection mapPlanV4(Collection src); + Collection mapPlanNative(Collection src); + + io.gravitee.rest.api.model.PrimaryOwnerEntity map(PrimaryOwnerEntity src); + + @Mapping(target = "id", source = "apiEntity.id") + @Mapping(target = "type", source = "apiEntity.type") + @Mapping(target = "state", source = "apiEntity.lifecycleState") + @Mapping(target = "lifecycleState", source = "apiEntity.apiLifecycleState") + @Mapping(target = "listeners", source = "apiEntity.apiDefinitionHttpV4.listeners") + @Mapping(target = "analytics", source = "apiEntity.apiDefinitionHttpV4.analytics") + @Mapping(target = "flowExecution", source = "apiEntity.apiDefinitionHttpV4.flowExecution") + @Mapping(target = "flows", source = "apiEntity.apiDefinitionHttpV4.flows") + @Mapping(target = "responseTemplates", source = "apiEntity.apiDefinitionHttpV4.responseTemplates") + @Mapping(target = "properties", source = "apiEntity.apiDefinitionHttpV4.properties") + @Mapping(target = "resources", source = "apiEntity.apiDefinitionHttpV4.resources") + @Mapping(target = "failover", expression = "java(apiEntity.getApiDefinitionHttpV4().failoverEnabled())") + ApiDescriptor.ApiDescriptorV4 mapV4( + Api apiEntity, + PrimaryOwnerEntity primaryOwner, + WorkflowState workflowState, + Set groups, + Collection metadata, + List endpointGroups + ); + + @Mapping(target = "id", source = "apiEntity.id") + @Mapping(target = "state", source = "apiEntity.lifecycleState") + @Mapping(target = "lifecycleState", source = "apiEntity.apiLifecycleState") + @Mapping(target = "listeners", source = "apiEntity.apiDefinitionNativeV4.listeners") + @Mapping(target = "flows", source = "apiEntity.apiDefinitionNativeV4.flows") + @Mapping(target = "properties", source = "apiEntity.apiDefinitionNativeV4.properties") + @Mapping(target = "resources", source = "apiEntity.apiDefinitionNativeV4.resources") + ApiDescriptor.ApiDescriptorNative mapNative( + Api apiEntity, + PrimaryOwnerEntity primaryOwner, + WorkflowState workflowState, + Set groups, + Collection metadata, + List endpointGroups + ); + + Set mapMetadata(Collection source); + + default Map map(Collection sources) { + return stream(sources).collect(Collectors.toMap(NewApiMetadata::getName, NewApiMetadata::getValue)); + } - @Mapping(target = "type", source = "planType") - PlanExport genericPlanMap(GenericPlanEntity source); + default Instant map(ZonedDateTime src) { + return src == null ? null : src.toInstant(); + } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/MemberAdapter.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/MemberAdapter.java index 9e4d6f66dcb..8aea99ebc56 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/MemberAdapter.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/adapter/MemberAdapter.java @@ -18,6 +18,7 @@ import io.gravitee.apim.core.api.model.import_definition.ApiMember; import io.gravitee.apim.core.api.model.import_definition.ApiMemberRole; import io.gravitee.apim.core.member.model.Member; +import io.gravitee.apim.core.membership.model.Membership; import io.gravitee.rest.api.model.MemberEntity; import io.gravitee.rest.api.model.RoleEntity; import java.util.List; @@ -29,7 +30,7 @@ public interface MemberAdapter { MemberAdapter INSTANCE = Mappers.getMapper(MemberAdapter.class); - ApiMember toApiMember(MemberEntity member); + ApiMember toApiMember(Membership member); MemberEntity toEntity(ApiMember member); Set toEntities(Set members); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/documentation/PageCrudServiceImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/documentation/PageCrudServiceImpl.java index f6ec13771fa..3708939069a 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/documentation/PageCrudServiceImpl.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/documentation/PageCrudServiceImpl.java @@ -15,6 +15,8 @@ */ package io.gravitee.apim.infra.crud_service.documentation; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; + import io.gravitee.apim.core.documentation.crud_service.PageCrudService; import io.gravitee.apim.core.documentation.exception.ApiPageInvalidReferenceTypeException; import io.gravitee.apim.core.documentation.exception.ApiPageNotDeletedException; @@ -23,8 +25,11 @@ import io.gravitee.apim.infra.adapter.PageAdapter; import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.PageRepository; +import io.gravitee.repository.management.api.search.PageCriteria; +import io.gravitee.repository.management.model.PageReferenceType; import io.gravitee.rest.api.service.exceptions.PageNotFoundException; import java.util.Collection; +import java.util.List; import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/membership/MembershipCrudServiceImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/membership/MembershipCrudServiceImpl.java index 3d90eef1ffd..9c09dc00dbb 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/membership/MembershipCrudServiceImpl.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/membership/MembershipCrudServiceImpl.java @@ -15,12 +15,16 @@ */ package io.gravitee.apim.infra.crud_service.membership; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; + import io.gravitee.apim.core.exception.TechnicalDomainException; import io.gravitee.apim.core.membership.crud_service.MembershipCrudService; import io.gravitee.apim.core.membership.model.Membership; import io.gravitee.apim.infra.adapter.MembershipAdapter; import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.MembershipRepository; +import io.gravitee.repository.management.model.MembershipReferenceType; +import java.util.Collection; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -51,4 +55,15 @@ public void delete(String id) { throw new TechnicalDomainException("An error occurs while trying to delete the membership: " + id, e); } } + + @Override + public Collection findByApiId(String apiId) { + try { + return stream(membershipRepository.findByReferenceIdAndReferenceType(apiId, MembershipReferenceType.API)) + .map(MembershipAdapter.INSTANCE::toEntity) + .toList(); + } catch (TechnicalException e) { + throw new TechnicalDomainException("An error occurs while trying to create the membership: " + apiId, e); + } + } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/metadata/MetadataCrudServiceImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/metadata/MetadataCrudServiceImpl.java index 552dc923257..4e93add5916 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/metadata/MetadataCrudServiceImpl.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/metadata/MetadataCrudServiceImpl.java @@ -15,6 +15,9 @@ */ package io.gravitee.apim.infra.crud_service.metadata; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; +import static io.gravitee.repository.management.model.MetadataReferenceType.API; + import io.gravitee.apim.core.exception.TechnicalDomainException; import io.gravitee.apim.core.metadata.crud_service.MetadataCrudService; import io.gravitee.apim.core.metadata.model.Metadata; @@ -23,6 +26,7 @@ import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.MetadataRepository; import io.gravitee.repository.management.model.MetadataReferenceType; +import java.util.Collection; import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; @@ -74,6 +78,15 @@ public Optional findById(MetadataId id) { } } + @Override + public Collection findByApiId(String id) { + try { + return stream(metadataRepository.findByReferenceTypeAndReferenceId(API, id)).map(MetadataAdapter.INSTANCE::toEntity).toList(); + } catch (TechnicalException e) { + throw new TechnicalDomainException("An error occurred while finding metadata by API id [%s]".formatted(id), e); + } + } + @Override public Metadata update(Metadata metadata) { try { diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/plan/PlanCrudServiceImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/plan/PlanCrudServiceImpl.java index 8e0799c89bb..362e460ce61 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/plan/PlanCrudServiceImpl.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/plan/PlanCrudServiceImpl.java @@ -15,6 +15,8 @@ */ package io.gravitee.apim.infra.crud_service.plan; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; + import io.gravitee.apim.core.exception.TechnicalDomainException; import io.gravitee.apim.core.plan.crud_service.PlanCrudService; import io.gravitee.apim.core.plan.model.Plan; @@ -22,6 +24,7 @@ import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.PlanRepository; import io.gravitee.rest.api.service.exceptions.PlanNotFoundException; +import java.util.Collection; import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; @@ -60,6 +63,16 @@ public Optional findById(String planId) { } } + @Override + public Collection findByApiId(String apiId) { + try { + log.debug("Find a plan by API id : {}", apiId); + return stream(planRepository.findByApi(apiId)).map(PlanAdapter.INSTANCE::fromRepository).toList(); + } catch (TechnicalException ex) { + throw new TechnicalDomainException(String.format("An error occurs while trying to find a plan by id: %s", apiId), ex); + } + } + @Override public Plan create(Plan plan) { try { diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/workflow/WorkflowCrudServiceImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/workflow/WorkflowCrudServiceImpl.java index 75e046ea036..4c10a41027f 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/workflow/WorkflowCrudServiceImpl.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/crud_service/workflow/WorkflowCrudServiceImpl.java @@ -15,12 +15,17 @@ */ package io.gravitee.apim.infra.crud_service.workflow; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; + import io.gravitee.apim.core.exception.TechnicalDomainException; import io.gravitee.apim.core.workflow.crud_service.WorkflowCrudService; import io.gravitee.apim.core.workflow.model.Workflow; import io.gravitee.apim.infra.adapter.WorkflowAdapter; import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.WorkflowRepository; +import io.gravitee.rest.api.model.WorkflowReferenceType; +import io.gravitee.rest.api.model.WorkflowType; +import java.util.Collection; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -49,4 +54,14 @@ public Workflow create(Workflow workflow) { ); } } + + public Collection findByApiId(String apiId) { + try { + return stream(workflowRepository.findByReferenceAndType(WorkflowReferenceType.API.name(), apiId, WorkflowType.REVIEW.name())) + .map(WorkflowAdapter.INSTANCE::toEntity) + .toList(); + } catch (TechnicalException e) { + throw new TechnicalDomainException("An error to find workflows of [apiId=%s]".formatted(apiId), e); + } + } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImpl.java index 9306746286f..472a75a8a87 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImpl.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImpl.java @@ -15,15 +15,55 @@ */ package io.gravitee.apim.infra.domain_service.api; +import static io.gravitee.apim.core.api.domain_service.ApiExportDomainService.Excludable.GROUPS; +import static io.gravitee.apim.core.api.domain_service.ApiExportDomainService.Excludable.MEMBERS; +import static io.gravitee.apim.core.api.domain_service.ApiExportDomainService.Excludable.METADATA; +import static io.gravitee.apim.core.api.domain_service.ApiExportDomainService.Excludable.PAGES_MEDIA; +import static io.gravitee.apim.core.api.domain_service.ApiExportDomainService.Excludable.SHARED_CONFIGURATION; +import static io.gravitee.apim.core.utils.CollectionUtils.stream; +import static io.gravitee.rest.api.model.permissions.RolePermission.API_DOCUMENTATION; +import static io.gravitee.rest.api.model.permissions.RolePermission.API_MEMBER; +import static io.gravitee.rest.api.model.permissions.RolePermission.API_PLAN; +import static io.gravitee.rest.api.model.permissions.RolePermissionAction.READ; + +import io.gravitee.apim.core.api.crud_service.ApiCrudService; import io.gravitee.apim.core.api.domain_service.ApiExportDomainService; +import io.gravitee.apim.core.api.exception.ApiNotFoundException; +import io.gravitee.apim.core.api.model.Api; +import io.gravitee.apim.core.api.model.NewApiMetadata; +import io.gravitee.apim.core.api.model.import_definition.ApiDescriptor; +import io.gravitee.apim.core.api.model.import_definition.ApiMember; import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; +import io.gravitee.apim.core.api.model.import_definition.PageExport; +import io.gravitee.apim.core.api.model.import_definition.PlanDescriptor; import io.gravitee.apim.core.audit.model.AuditInfo; +import io.gravitee.apim.core.documentation.query_service.PageQueryService; +import io.gravitee.apim.core.media.model.Media; +import io.gravitee.apim.core.membership.crud_service.MembershipCrudService; +import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerDomainService; +import io.gravitee.apim.core.membership.model.Membership; +import io.gravitee.apim.core.membership.model.PrimaryOwnerEntity; +import io.gravitee.apim.core.metadata.crud_service.MetadataCrudService; +import io.gravitee.apim.core.plan.crud_service.PlanCrudService; +import io.gravitee.apim.core.workflow.crud_service.WorkflowCrudService; +import io.gravitee.apim.core.workflow.model.Workflow; import io.gravitee.apim.infra.adapter.GraviteeDefinitionAdapter; -import io.gravitee.rest.api.model.v4.api.ApiEntity; -import io.gravitee.rest.api.model.v4.plan.BasePlanEntity; +import io.gravitee.apim.infra.adapter.MemberAdapter; +import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.endpointgroup.EndpointGroup; +import io.gravitee.rest.api.model.WorkflowState; +import io.gravitee.rest.api.model.permissions.RolePermission; +import io.gravitee.rest.api.service.MediaService; +import io.gravitee.rest.api.service.PermissionService; import io.gravitee.rest.api.service.common.ExecutionContext; -import io.gravitee.rest.api.service.v4.ApiImportExportService; +import io.gravitee.rest.api.service.common.GraviteeContext; +import io.gravitee.rest.api.service.exceptions.ApiDefinitionVersionNotSupportedException; +import jakarta.annotation.Nullable; +import java.util.Collection; +import java.util.List; import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -31,28 +71,119 @@ @RequiredArgsConstructor public class ApiExportDomainServiceImpl implements ApiExportDomainService { - private final ApiImportExportService exportService; + private final PermissionService permissionService; + private final MediaService mediaService; + + private final WorkflowCrudService workflowCrudService; + private final MembershipCrudService membershipCrudService; + private final MetadataCrudService metadataCrudService; + private final PageQueryService pageQueryService; + private final ApiCrudService apiCrudService; + private final ApiPrimaryOwnerDomainService apiPrimaryOwnerDomainService; + private final PlanCrudService planCrudService; @Override - public GraviteeDefinition export(String apiId, AuditInfo auditInfo) { + public GraviteeDefinition export(String apiId, AuditInfo auditInfo, Collection excluded) { var executionContext = new ExecutionContext(auditInfo.organizationId(), auditInfo.environmentId()); - var exportEntity = exportService.exportApi(executionContext, apiId, null, Set.of()); - var graviteeDefinition = GraviteeDefinitionAdapter.INSTANCE.map(exportEntity); - if (exportEntity.getApiEntity() instanceof ApiEntity v4) { - graviteeDefinition.getApi().setType(v4.getType()); - } - if (exportEntity.getPlans() != null) { - for (var source : exportEntity.getPlans()) { - if (source instanceof BasePlanEntity v4Plan) { - graviteeDefinition - .getPlans() - .stream() - .filter(p -> p.getId().equals(v4Plan.getId())) - .findFirst() - .ifPresent(target -> target.setSecurity(v4Plan.getSecurity())); - } - } + + var apiPrimaryOwner = apiPrimaryOwnerDomainService.getApiPrimaryOwner(auditInfo.organizationId(), apiId); + var api1 = apiCrudService.findById(apiId).orElseThrow(() -> new ApiNotFoundException(apiId)); + + var members = !excluded.contains(MEMBERS) ? exportApiMembers(apiId) : null; + var metadata = !excluded.contains(METADATA) ? exportApiMetadata(executionContext, apiId) : null; + + var pages = !excluded.contains(PAGES_MEDIA) ? exportApiPages(apiId) : null; + var medias = !excluded.contains(PAGES_MEDIA) ? exportApiMedia(apiId) : null; + //node.metadata() + + var workflowState = stream(workflowCrudService.findByApiId(apiId)) + .map(Workflow::getState) + .map(Enum::name) + .map(WorkflowState::valueOf) + .findFirst() + .orElse(null); + + if (api1.getDefinitionVersion() == DefinitionVersion.V4 && api1.getApiDefinitionHttpV4() != null) { + var api = exportV4(api1, apiPrimaryOwner, metadata, workflowState, excluded); + var plans = !excluded.contains(Excludable.PLANS) ? exportApiPlansV4(apiId) : null; + return new GraviteeDefinition.V4(api, members, metadata, pages, plans, medias, api1.getPicture(), api1.getBackground()); + } else if (api1.getDefinitionVersion() == DefinitionVersion.V4 && api1.getApiDefinitionNativeV4() != null) { + var api = exportNative(api1, apiPrimaryOwner, metadata, workflowState, excluded); + var plans = !excluded.contains(Excludable.PLANS) ? exportApiPlansNative(apiId) : null; + return new GraviteeDefinition.Native(api, members, metadata, pages, plans, medias, api1.getPicture(), api1.getBackground()); + } else { + throw new ApiDefinitionVersionNotSupportedException( + api1.getDefinitionVersion() != null ? api1.getDefinitionVersion().getLabel() : null + ); } - return graviteeDefinition; + } + + private ApiDescriptor.ApiDescriptorV4 exportV4( + Api apiEntity, + PrimaryOwnerEntity primaryOwner, + @Nullable Collection metadata, + @Nullable WorkflowState workflowState, + Collection excluded + ) { + Consumer fct = excluded.contains(SHARED_CONFIGURATION) + ? endpointGroup -> endpointGroup.setSharedConfiguration((String) null) + : endpointGroup -> {}; + var endpointGroups = stream(apiEntity.getApiDefinitionHttpV4().getEndpointGroups()).peek(fct).toList(); + var groups = excluded.contains(GROUPS) ? null : apiEntity.getGroups(); + return GraviteeDefinitionAdapter.INSTANCE.mapV4(apiEntity, primaryOwner, workflowState, groups, metadata, endpointGroups); + } + + private ApiDescriptor.ApiDescriptorNative exportNative( + Api nativeApi, + PrimaryOwnerEntity primaryOwner, + @Nullable Collection metadata, + @Nullable WorkflowState workflowState, + Collection excluded + ) { + Consumer fct = excluded.contains(SHARED_CONFIGURATION) + ? endpointGroup -> endpointGroup.setSharedConfiguration((String) null) + : endpointGroup -> {}; + var endpointGroups = stream(nativeApi.getApiDefinitionHttpV4().getEndpointGroups()).peek(fct).toList(); + var groups = excluded.contains(GROUPS) ? null : nativeApi.getGroups(); + return GraviteeDefinitionAdapter.INSTANCE.mapNative(nativeApi, primaryOwner, workflowState, groups, metadata, endpointGroups); + } + + private Set exportApiMembers(String apiId) { + return permissionService.hasPermission(GraviteeContext.getExecutionContext(), API_MEMBER, apiId, READ) + ? stream(membershipCrudService.findByApiId(apiId)) + .filter(memberEntity -> memberEntity.getMemberType() == Membership.Type.USER) + .map(MemberAdapter.INSTANCE::toApiMember) + .collect(Collectors.toSet()) + : null; + } + + private Set exportApiMetadata(ExecutionContext executionContext, String apiId) { + return permissionService.hasPermission(executionContext, RolePermission.API_METADATA, apiId, READ) + ? GraviteeDefinitionAdapter.INSTANCE.mapMetadata(metadataCrudService.findByApiId(apiId)) + : null; + } + + private List exportApiPages(String apiId) { + return permissionService.hasPermission(GraviteeContext.getExecutionContext(), API_DOCUMENTATION, apiId, READ) + ? GraviteeDefinitionAdapter.INSTANCE.mapPage(pageQueryService.searchByApiId(apiId)) + : null; + } + + private List exportApiMedia(String apiId) { + return permissionService.hasPermission(GraviteeContext.getExecutionContext(), API_DOCUMENTATION, apiId, READ) + ? GraviteeDefinitionAdapter.INSTANCE.mapMedia(mediaService.findAllByApiId(apiId)) + : null; + } + + private Collection exportApiPlansV4(String apiId) { + return permissionService.hasPermission(GraviteeContext.getExecutionContext(), API_PLAN, apiId, READ) + ? GraviteeDefinitionAdapter.INSTANCE.mapPlanV4(planCrudService.findByApiId(apiId)) + : null; + } + + private Collection exportApiPlansNative(String apiId) { + return permissionService.hasPermission(GraviteeContext.getExecutionContext(), API_PLAN, apiId, READ) + ? GraviteeDefinitionAdapter.INSTANCE.mapPlanV4(planCrudService.findByApiId(apiId)) + : null; } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/ApiFixtures.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/ApiFixtures.java index 718acd9c888..e76b2ae6c45 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/ApiFixtures.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/ApiFixtures.java @@ -50,11 +50,12 @@ public class ApiFixtures { private ApiFixtures() {} public static final String MY_API = "my-api"; + public static final String MY_API_NAME = "My Api"; private static final Supplier BASE = () -> Api .builder() .id(MY_API) - .name("My Api") + .name(MY_API_NAME) .environmentId("environment-id") .crossId("my-api-crossId") .description("api-description") @@ -82,7 +83,7 @@ public static Api aProxyApiV4() { io.gravitee.definition.model.v4.Api .builder() .id(MY_API) - .name("My Api") + .name(MY_API_NAME) .apiVersion("1.0.0") .analytics(Analytics.builder().enabled(false).build()) .failover(Failover.builder().enabled(false).build()) diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/GraviteeDefinitionFixtures.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/GraviteeDefinitionFixtures.java index 8ee88479cd7..b9cb622a54f 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/GraviteeDefinitionFixtures.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/GraviteeDefinitionFixtures.java @@ -16,12 +16,12 @@ package fixtures.core.model; import io.gravitee.apim.core.api.model.NewApiMetadata; -import io.gravitee.apim.core.api.model.import_definition.ApiExport; +import io.gravitee.apim.core.api.model.import_definition.ApiDescriptor; import io.gravitee.apim.core.api.model.import_definition.ApiMember; import io.gravitee.apim.core.api.model.import_definition.ApiMemberRole; import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; import io.gravitee.apim.core.api.model.import_definition.PageExport; -import io.gravitee.apim.core.api.model.import_definition.PlanExport; +import io.gravitee.apim.core.api.model.import_definition.PlanDescriptor; import io.gravitee.apim.core.documentation.model.Page; import io.gravitee.apim.core.metadata.model.Metadata; import io.gravitee.apim.core.plan.model.Plan; @@ -67,8 +67,8 @@ public class GraviteeDefinitionFixtures { private GraviteeDefinitionFixtures() {} - public static final Supplier BASE = () -> - GraviteeDefinition + public static final Supplier BASE = () -> + GraviteeDefinition.V4 .builder() .members( Set.of( @@ -95,13 +95,12 @@ private GraviteeDefinitionFixtures() {} .apiPicture("data:image/png;base64,picture") .apiBackground("data:image/png;base64,background"); - public static GraviteeDefinition aGraviteeDefinitionProxy() { + public static GraviteeDefinition.V4 aGraviteeDefinitionProxy() { return BASE .get() .api( - ApiExport + ApiDescriptor.ApiDescriptorV4 .builder() - .definitionVersion(DefinitionVersion.V4) .type(ApiType.PROXY) .listeners( List.of( @@ -250,7 +249,7 @@ public static GraviteeDefinition aGraviteeDefinitionProxy() { ) .plans( Set.of( - PlanExport + PlanDescriptor.PlanDescriptorV4 .builder() .id("plan-id") .name("Default Keyless (UNSECURED)") diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MembershipCrudServiceInMemory.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MembershipCrudServiceInMemory.java index 6f3268723f0..89d1bce2ffd 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MembershipCrudServiceInMemory.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MembershipCrudServiceInMemory.java @@ -18,6 +18,7 @@ import io.gravitee.apim.core.membership.crud_service.MembershipCrudService; import io.gravitee.apim.core.membership.model.Membership; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -36,6 +37,14 @@ public void delete(String id) { storage.removeIf(s -> s.getId().equals(id)); } + @Override + public Collection findByApiId(String apiId) { + return storage + .stream() + .filter(m -> m.getReferenceType() == Membership.ReferenceType.API && apiId.equals(m.getReferenceId())) + .toList(); + } + @Override public void initWith(List items) { storage.addAll(items); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MetadataCrudServiceInMemory.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MetadataCrudServiceInMemory.java index d825209b6e2..084377ccf3e 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MetadataCrudServiceInMemory.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/MetadataCrudServiceInMemory.java @@ -19,6 +19,7 @@ import io.gravitee.apim.core.metadata.model.Metadata; import io.gravitee.apim.core.metadata.model.MetadataId; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -47,6 +48,11 @@ public Optional findById(MetadataId id) { .findFirst(); } + @Override + public Collection findByApiId(String id) { + return storage.stream().filter(m -> Metadata.ReferenceType.API == m.getReferenceType() && id.equals(m.getReferenceId())).toList(); + } + @Override public Metadata update(Metadata metadata) { OptionalInt index = diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/PlanCrudServiceInMemory.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/PlanCrudServiceInMemory.java index 9de179a3325..a8a65a9c17d 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/PlanCrudServiceInMemory.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/PlanCrudServiceInMemory.java @@ -20,6 +20,7 @@ import io.gravitee.rest.api.service.exceptions.PlanNotFoundException; import io.gravitee.rest.api.service.exceptions.TechnicalManagementException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -46,6 +47,11 @@ public Optional findById(String planId) { return storage.stream().filter(plan -> planId.equals(plan.getId())).findFirst(); } + @Override + public Collection findByApiId(String apiId) { + return storage.stream().filter(plan -> plan.getApiId().equals(apiId)).toList(); + } + @Override public Plan create(Plan plan) { storage.add(plan); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/WorkflowCrudServiceInMemory.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/WorkflowCrudServiceInMemory.java index 7011c9adf34..1b83d7a9cfa 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/WorkflowCrudServiceInMemory.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/WorkflowCrudServiceInMemory.java @@ -18,6 +18,7 @@ import io.gravitee.apim.core.workflow.crud_service.WorkflowCrudService; import io.gravitee.apim.core.workflow.model.Workflow; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import org.springframework.stereotype.Service; @@ -33,6 +34,14 @@ public Workflow create(Workflow entity) { return entity; } + @Override + public Collection findByApiId(String apiId) { + return storage + .stream() + .filter(w -> w.getReferenceType() == Workflow.ReferenceType.API && apiId.equals(w.getReferenceId())) + .toList(); + } + @Override public void initWith(List items) { storage.addAll(items); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCaseTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCaseTest.java index 9b0615f990a..c43dbd94018 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCaseTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/scoring/use_case/ScoreApiRequestUseCaseTest.java @@ -17,6 +17,9 @@ import static assertions.CoreAssertions.assertThat; import static fixtures.core.model.ApiFixtures.MY_API; +import static fixtures.core.model.ApiFixtures.MY_API_NAME; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -34,7 +37,7 @@ import inmemory.ScoringRulesetQueryServiceInMemory; import io.gravitee.apim.core.api.domain_service.ApiExportDomainService; import io.gravitee.apim.core.api.model.Api; -import io.gravitee.apim.core.api.model.import_definition.ApiExport; +import io.gravitee.apim.core.api.model.import_definition.ApiDescriptor; import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; import io.gravitee.apim.core.async_job.model.AsyncJob; import io.gravitee.apim.core.audit.model.AuditInfo; @@ -45,15 +48,17 @@ import io.gravitee.apim.core.scoring.model.ScoringRuleset; import io.gravitee.apim.infra.json.jackson.GraviteeDefinitionJacksonJsonSerializer; import io.gravitee.common.utils.TimeProvider; -import io.gravitee.definition.model.DefinitionVersion; import io.gravitee.rest.api.service.common.UuidString; import io.gravitee.rest.api.service.exceptions.ApiDefinitionVersionNotSupportedException; +import io.vertx.core.json.JsonObject; import java.time.Clock; import java.time.Instant; import java.time.ZoneId; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; +import org.assertj.core.api.Condition; +import org.assertj.core.data.Index; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -88,6 +93,7 @@ class ScoreApiRequestUseCaseTest { ApiExportDomainService apiExportDomainService = mock(ApiExportDomainService.class); ScoreApiRequestUseCase scoreApiRequestUseCase; + private Condition REQUEST_API_CONDITION; @BeforeAll static void beforeAll() { @@ -115,13 +121,12 @@ void setUp() { scoringFunctionQueryService ); - when(apiExportDomainService.export("my-api", AUDIT_INFO)) - .thenReturn( - GraviteeDefinition - .builder() - .api(ApiExport.builder().id(MY_API).name("My Api").definitionVersion(DefinitionVersion.FEDERATED).build()) - .build() - ); + GraviteeDefinition build = GraviteeDefinition.GraviteeDefinitionFederated + .builder() + .api(ApiDescriptor.ApiDescriptorFederated.builder().id(MY_API).name(MY_API_NAME).build()) + .build(); + when(apiExportDomainService.export(eq("my-api"), eq(AUDIT_INFO), anyCollection())).thenReturn(build); + REQUEST_API_CONDITION = is(MY_API, MY_API_NAME); } @AfterEach @@ -146,23 +151,29 @@ public void should_trigger_scoring_for_gravitee_definition_v4() { // Then assertThat(scoringProvider.pendingRequests()) - .containsExactly( - new ScoreRequest( - "generated-id", - ORGANIZATION_ID, - ENVIRONMENT_ID, + .hasSize(1) + .first() + .usingRecursiveComparison() + .ignoringFields("assets") + .isEqualTo(new ScoreRequest("generated-id", ORGANIZATION_ID, ENVIRONMENT_ID, api.getId(), null)); + assertThat(scoringProvider.pendingRequests().get(0).assets()) + .first() + .usingRecursiveComparison() + .ignoringFields("content") + .isEqualTo( + new ScoreRequest.AssetToScore( api.getId(), - List.of( - new ScoreRequest.AssetToScore( - api.getId(), - new ScoreRequest.AssetType(ScoringAssetType.GRAVITEE_DEFINITION, ScoreRequest.Format.GRAVITEE_FEDERATED), - api.getName(), - """ - {"api":{"id":"my-api","name":"My Api","definitionVersion":"FEDERATED","tags":[],"properties":[],"resources":[],"responseTemplates":{},"state":"STOPPED","originContext":{"origin":"MANAGEMENT"},"disableMembershipNotifications":false}}""" - ) - ) + new ScoreRequest.AssetType(ScoringAssetType.GRAVITEE_DEFINITION, ScoreRequest.Format.GRAVITEE_FEDERATED), + api.getName(), + """ + {"api":{"id":"my-api","name":"My Api","definitionVersion":"FEDERATED","tags":[],"properties":[],"resources":[],"responseTemplates":{},"state":"STOPPED","originContext":{"origin":"MANAGEMENT"},"disableMembershipNotifications":false}}""" ) ); + assertThat(scoringProvider.pendingRequests().get(0).assets()) + .map(ScoreRequest.AssetToScore::content) + .map(JsonObject::new) + .first() + .is(REQUEST_API_CONDITION); assertThat(asyncJobCrudService.storage()) .containsExactly( AsyncJob @@ -184,7 +195,8 @@ public void should_trigger_scoring_for_gravitee_definition_v4() { public void should_trigger_scoring_for_unsupported_version_of_gravitee_definition() { // Given var api = givenExistingApi(ApiFixtures.aFederatedApi()); - when(apiExportDomainService.export("my-api", AUDIT_INFO)).thenThrow(new ApiDefinitionVersionNotSupportedException("UNKNOW")); + when(apiExportDomainService.export(eq("my-api"), eq(AUDIT_INFO), anyCollection())) + .thenThrow(new ApiDefinitionVersionNotSupportedException("UNKNOW")); // When scoreApiRequestUseCase @@ -369,19 +381,40 @@ public void should_ignore_page_type_supported(Page.Type pageType) { .hasJobId("generated-id") .hasOrganizationId(ORGANIZATION_ID) .hasEnvironmentId(ENVIRONMENT_ID) - .hasApiId(api.getId()) - .hasOnlyAssets( + .hasApiId(api.getId()); + assertThat(request.assets()) + .hasSize(1) + .first() + .usingRecursiveComparison() + .ignoringFields("content") + .isEqualTo( new ScoreRequest.AssetToScore( api.getId(), new ScoreRequest.AssetType(ScoringAssetType.GRAVITEE_DEFINITION, ScoreRequest.Format.GRAVITEE_FEDERATED), api.getName(), - """ - {"api":{"id":"my-api","name":"My Api","definitionVersion":"FEDERATED","tags":[],"properties":[],"resources":[],"responseTemplates":{},"state":"STOPPED","originContext":{"origin":"MANAGEMENT"},"disableMembershipNotifications":false}}""" + null ) ); + assertThat(request.assets()) + .map(ScoreRequest.AssetToScore::content) + .map(JsonObject::new) + .hasSize(1) + .has(REQUEST_API_CONDITION, Index.atIndex(0)); }); } + private static Condition is(String id, String name) { + return new Condition<>( + json -> { + JsonObject api = json.getJsonObject("api"); + return name.equals(api.getString("name")) && id.equals(api.getString("id")); + }, + "should have name %s and id %s", + name, + id + ); + } + private Api givenExistingApi(Api api) { apiCrudService.initWith(List.of(api)); return api; diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImplTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImplTest.java index 97c0d55ed9e..f9b9ca1ab50 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImplTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/infra/domain_service/api/ApiExportDomainServiceImplTest.java @@ -16,21 +16,26 @@ package io.gravitee.apim.infra.domain_service.api; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import io.gravitee.apim.core.api.crud_service.ApiCrudService; +import io.gravitee.apim.core.api.domain_service.ApiExportDomainService; +import io.gravitee.apim.core.api.model.Api; import io.gravitee.apim.core.api.model.import_definition.GraviteeDefinition; -import io.gravitee.apim.core.api.model.import_definition.PlanExport; import io.gravitee.apim.core.audit.model.AuditInfo; -import io.gravitee.apim.core.plan.model.Plan; +import io.gravitee.apim.core.documentation.query_service.PageQueryService; +import io.gravitee.apim.core.membership.crud_service.MembershipCrudService; +import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerDomainService; +import io.gravitee.apim.core.metadata.crud_service.MetadataCrudService; +import io.gravitee.apim.core.plan.crud_service.PlanCrudService; +import io.gravitee.apim.core.workflow.crud_service.WorkflowCrudService; +import io.gravitee.definition.model.DefinitionVersion; import io.gravitee.definition.model.v4.ApiType; -import io.gravitee.definition.model.v4.plan.PlanSecurity; -import io.gravitee.rest.api.model.v4.api.ApiEntity; -import io.gravitee.rest.api.model.v4.api.ExportApiEntity; -import io.gravitee.rest.api.model.v4.plan.BasePlanEntity; -import io.gravitee.rest.api.model.v4.plan.PlanType; -import io.gravitee.rest.api.service.v4.ApiImportExportService; -import java.util.Set; +import io.gravitee.rest.api.service.MediaService; +import io.gravitee.rest.api.service.PermissionService; +import java.util.EnumSet; +import java.util.Optional; import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -42,7 +47,31 @@ class ApiExportDomainServiceImplTest { @Mock - ApiImportExportService exportService; + PermissionService permissionService; + + @Mock + MediaService mediaService; + + @Mock + WorkflowCrudService workflowCrudService; + + @Mock + MembershipCrudService membershipCrudService; + + @Mock + MetadataCrudService metadataCrudService; + + @Mock + PageQueryService pageQueryService; + + @Mock + ApiCrudService apiCrudService; + + @Mock + ApiPrimaryOwnerDomainService apiPrimaryOwnerDomainService; + + @Mock + PlanCrudService planCrudService; @InjectMocks ApiExportDomainServiceImpl sut; @@ -51,21 +80,20 @@ class ApiExportDomainServiceImplTest { void exportServiceMustMapTypeWhenExportV4() { // Given String apiId = UUID.randomUUID().toString(); - ApiEntity api = new ApiEntity(); - api.setType(ApiType.PROXY); - BasePlanEntity plan = new BasePlanEntity(); - plan.setId(UUID.randomUUID().toString()); - plan.setSecurity(new PlanSecurity()); - plan.setType(PlanType.API); - when(exportService.exportApi(any(), any(), any(), any())) - .thenReturn(new ExportApiEntity(api, null, null, null, Set.of(plan), null)); + var definition = new io.gravitee.definition.model.v4.Api(); + Api api = Api + .builder() + .id(apiId) + .type(ApiType.PROXY) + .definitionVersion(DefinitionVersion.V4) + .apiDefinitionHttpV4(definition) + .build(); + when(apiCrudService.findById(anyString())).thenReturn(Optional.of(api)); // When - GraviteeDefinition export = sut.export(apiId, AuditInfo.builder().build()); + GraviteeDefinition export = sut.export(apiId, AuditInfo.builder().build(), EnumSet.allOf(ApiExportDomainService.Excludable.class)); // Then - assertThat(export.getApi().getType()).isEqualTo(ApiType.PROXY); - assertThat(export.getPlans()).map(PlanExport::getType).first().isEqualTo(Plan.PlanType.API); - assertThat(export.getPlans()).map(PlanExport::getSecurity).first().isNotNull(); + assertThat(export.api().type()).isEqualTo(ApiType.PROXY); } }