diff --git a/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/DefinitionVersion.java b/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/DefinitionVersion.java index 0320e2a1029..dcad7856d2f 100644 --- a/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/DefinitionVersion.java +++ b/gravitee-apim-definition/gravitee-apim-definition-model/src/main/java/io/gravitee/definition/model/DefinitionVersion.java @@ -32,7 +32,8 @@ public enum DefinitionVersion { @JsonEnumDefaultValue V1("1.0.0"), V2("2.0.0"), - V4("4.0.0"); + V4("4.0.0"), + FEDERATED("FEDERATED"); private static final Map BY_LABEL = new HashMap<>(); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-apis.yaml b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-apis.yaml index 13b6c72bc0c..d929b33a4b3 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-apis.yaml +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-apis.yaml @@ -1966,12 +1966,14 @@ components: - $ref: "#/components/schemas/ApiV1" - $ref: "#/components/schemas/ApiV2" - $ref: "#/components/schemas/ApiV4" + - $ref: "#/components/schemas/ApiFederated" discriminator: propertyName: definitionVersion mapping: V1: ApiV1 V2: ApiV2 V4: ApiV4 + FEDERATED: ApiFederated BaseApi: type: object @@ -2106,6 +2108,7 @@ components: V1: ApiV1 V2: ApiV2 V4: ApiV4 + FEDERATED: ApiFederated required: - definitionVersion ApiV1: @@ -2210,6 +2213,13 @@ components: $ref: "#/components/schemas/FlowV4" services: $ref: "#/components/schemas/ApiServices" + + ApiFederated: + type: object + title: "ApiFederated" + allOf: + - $ref: "#/components/schemas/GenericApi" + ApiLinks: type: object properties: @@ -2413,11 +2423,13 @@ components: oneOf: - $ref: "#/components/schemas/UpdateApiV2" - $ref: "#/components/schemas/UpdateApiV4" + - $ref: "#/components/schemas/UpdateApiFederated" discriminator: propertyName: definitionVersion mapping: V2: UpdateApiV2 V4: UpdateApiV4 + FEDERATED: UpdateApiFederated UpdateGenericApi: type: object properties: @@ -2498,6 +2510,7 @@ components: mapping: V2: UpdateApiV2 V4: UpdateApiV4 + FEDERATED: UpdateApiFederated UpdateApiV2: type: object title: "UpdateApiV2" @@ -2555,6 +2568,12 @@ components: $ref: "#/components/schemas/ApiServices" required: [listeners, endpointGroups, type] + UpdateApiFederated: + type: object + title: "UpdateApiFederated" + allOf: + - $ref: "#/components/schemas/UpdateGenericApi" + ApiDeployment: type: object properties: @@ -2761,6 +2780,7 @@ components: - V1 - V2 - V4 + - FEDERATED EndpointV4: type: object properties: diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/CreateApiDomainService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/CreateApiDomainService.java index 861f4edea8f..50438445135 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/CreateApiDomainService.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/CreateApiDomainService.java @@ -34,9 +34,11 @@ import io.gravitee.apim.core.parameters.query_service.ParametersQueryService; import io.gravitee.apim.core.search.Indexer; import io.gravitee.apim.core.workflow.crud_service.WorkflowCrudService; +import io.gravitee.definition.model.v4.flow.Flow; import io.gravitee.rest.api.model.parameters.Key; import io.gravitee.rest.api.model.parameters.ParameterReferenceType; import java.util.Collections; +import java.util.List; import java.util.function.UnaryOperator; @DomainService @@ -98,13 +100,13 @@ public ApiWithFlows create(Api api, PrimaryOwnerEntity primaryOwner, AuditInfo a apiPrimaryOwnerDomainService.createApiPrimaryOwnerMembership(created.getId(), primaryOwner, auditInfo); - createDefaultMailNotification(created.getId()); + createDefaultMailNotification(created); - apiMetadataDomainService.createDefaultApiMetadata(created.getId(), auditInfo); + createDefaultMetadata(created, auditInfo); - flowCrudService.saveApiFlows(api.getId(), api.getApiDefinitionV4().getFlows()); + var createdFlows = saveApiFlows(api); - if (isApiReviewEnabled(auditInfo.organizationId(), auditInfo.environmentId())) { + if (isApiReviewEnabled(created, auditInfo.organizationId(), auditInfo.environmentId())) { workflowCrudService.create(newApiReviewWorkflow(api.getId(), auditInfo.actor().userId())); } @@ -113,7 +115,7 @@ public ApiWithFlows create(Api api, PrimaryOwnerEntity primaryOwner, AuditInfo a created, primaryOwner ); - return new ApiWithFlows(created, api.getApiDefinitionV4().getFlows()); + return new ApiWithFlows(created, createdFlows); } private void createAuditLog(Api created, AuditInfo auditInfo) { @@ -132,14 +134,38 @@ private void createAuditLog(Api created, AuditInfo auditInfo) { ); } - private void createDefaultMailNotification(String apiId) { - notificationConfigCrudService.create(NotificationConfig.defaultMailNotificationConfigFor(apiId)); + private void createDefaultMailNotification(Api api) { + switch (api.getDefinitionVersion()) { + case V4 -> notificationConfigCrudService.create(NotificationConfig.defaultMailNotificationConfigFor(api.getId())); + case V1, V2, FEDERATED -> { + // nothing to do + } + } } - private boolean isApiReviewEnabled(String organizationId, String environmentId) { - return parametersQueryService.findAsBoolean( - Key.API_REVIEW_ENABLED, - new ParameterContext(environmentId, organizationId, ParameterReferenceType.ENVIRONMENT) - ); + private void createDefaultMetadata(Api api, AuditInfo auditInfo) { + switch (api.getDefinitionVersion()) { + case V4 -> apiMetadataDomainService.createDefaultApiMetadata(api.getId(), auditInfo); + case V1, V2, FEDERATED -> { + // nothing to do + } + } + } + + private List saveApiFlows(Api api) { + return switch (api.getDefinitionVersion()) { + case V4 -> flowCrudService.saveApiFlows(api.getId(), api.getApiDefinitionV4().getFlows()); + case V1, V2, FEDERATED -> null; + }; + } + + private boolean isApiReviewEnabled(Api api, String organizationId, String environmentId) { + return switch (api.getDefinitionVersion()) { + case V1, V2, V4 -> parametersQueryService.findAsBoolean( + Key.API_REVIEW_ENABLED, + new ParameterContext(environmentId, organizationId, ParameterReferenceType.ENVIRONMENT) + ); + case FEDERATED -> false; + }; } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ValidateFederatedApiDomainService.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ValidateFederatedApiDomainService.java new file mode 100644 index 00000000000..7082af5e004 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/domain_service/ValidateFederatedApiDomainService.java @@ -0,0 +1,37 @@ +/* + * 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.domain_service; + +import io.gravitee.apim.core.DomainService; +import io.gravitee.apim.core.api.model.Api; +import io.gravitee.apim.core.exception.ValidationDomainException; +import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.rest.api.service.exceptions.InvalidDataException; + +@DomainService +public class ValidateFederatedApiDomainService { + + public Api validateAndSanitizeForCreation(final Api api) { + if (api.getDefinitionVersion() != DefinitionVersion.FEDERATED) { + throw new ValidationDomainException("Definition version is unsupported, should be FEDERATED"); + } + + // Reset lifecycle state as Federated API are not deployed on Gateway + api.setLifecycleState(null); + + return api; + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/exception/IntegrationNotFoundException.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/exception/IntegrationNotFoundException.java new file mode 100644 index 00000000000..b26c5f45089 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/exception/IntegrationNotFoundException.java @@ -0,0 +1,25 @@ +/* + * 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.integration.exception; + +import io.gravitee.apim.core.exception.NotFoundDomainException; + +public class IntegrationNotFoundException extends NotFoundDomainException { + + public IntegrationNotFoundException(String id) { + super("Integration not found.", id); + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Asset.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Asset.java new file mode 100644 index 00000000000..26f798a7fd4 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Asset.java @@ -0,0 +1,21 @@ +/* + * 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.integration.model; + +import lombok.Builder; + +@Builder(toBuilder = true) +public record Asset(String integrationId, String id, String name, String description, String version) {} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Integration.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Integration.java index 938f4e2c192..d0f7d3a0c67 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Integration.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/model/Integration.java @@ -20,6 +20,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.With; /** * @author Remi Baptiste (remi.baptiste at graviteesource.com) @@ -31,7 +32,9 @@ @AllArgsConstructor public class Integration { + @With String id; + String name; String description; String provider; diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/spi/IntegrationAgent.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/spi/IntegrationAgent.java new file mode 100644 index 00000000000..77040b4a8a3 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/spi/IntegrationAgent.java @@ -0,0 +1,24 @@ +/* + * 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.integration.spi; + +import io.gravitee.apim.core.integration.model.Asset; +import io.gravitee.apim.core.integration.model.Integration; +import io.reactivex.rxjava3.core.Flowable; + +public interface IntegrationAgent { + Flowable fetchAllAssets(Integration integration); +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/use_case/DiscoverIntegrationAssetUseCase.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/use_case/DiscoverIntegrationAssetUseCase.java new file mode 100644 index 00000000000..7b80dbccaf1 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/integration/use_case/DiscoverIntegrationAssetUseCase.java @@ -0,0 +1,111 @@ +/* + * 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.integration.use_case; + +import io.gravitee.apim.core.UseCase; +import io.gravitee.apim.core.api.domain_service.CreateApiDomainService; +import io.gravitee.apim.core.api.domain_service.ValidateFederatedApiDomainService; +import io.gravitee.apim.core.api.model.Api; +import io.gravitee.apim.core.audit.model.AuditInfo; +import io.gravitee.apim.core.integration.crud_service.IntegrationCrudService; +import io.gravitee.apim.core.integration.exception.IntegrationNotFoundException; +import io.gravitee.apim.core.integration.model.Asset; +import io.gravitee.apim.core.integration.model.Integration; +import io.gravitee.apim.core.integration.spi.IntegrationAgent; +import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerFactory; +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.Single; +import lombok.extern.slf4j.Slf4j; + +@UseCase +@Slf4j +public class DiscoverIntegrationAssetUseCase { + + private final IntegrationCrudService integrationCrudService; + private final ApiPrimaryOwnerFactory apiPrimaryOwnerFactory; + private final ValidateFederatedApiDomainService validateFederatedApi; + private final CreateApiDomainService createApiDomainService; + private final IntegrationAgent integrationAgent; + + public DiscoverIntegrationAssetUseCase( + IntegrationCrudService integrationCrudService, + ApiPrimaryOwnerFactory apiPrimaryOwnerFactory, + ValidateFederatedApiDomainService validateFederatedApi, + CreateApiDomainService apiCrudService, + IntegrationAgent integrationAgent + ) { + this.integrationCrudService = integrationCrudService; + this.apiPrimaryOwnerFactory = apiPrimaryOwnerFactory; + this.validateFederatedApi = validateFederatedApi; + this.createApiDomainService = apiCrudService; + this.integrationAgent = integrationAgent; + } + + public Completable execute(Input input) { + var integrationId = input.integrationId; + var auditInfo = input.auditInfo; + var organizationId = auditInfo.organizationId(); + var environmentId = auditInfo.environmentId(); + + var primaryOwner = apiPrimaryOwnerFactory.createForNewApi(organizationId, environmentId, input.auditInfo.actor().userId()); + + return Single + .fromCallable(() -> + integrationCrudService + .findById(integrationId) + .filter(integration -> integration.getEnvironmentId().equals(environmentId)) + .orElseThrow(() -> new IntegrationNotFoundException(integrationId)) + ) + .flatMapPublisher(integration -> + integrationAgent + .fetchAllAssets(integration) + .doOnNext(asset -> { + try { + createApiDomainService.create( + adaptAssetToApi(asset, integration), + primaryOwner, + auditInfo, + validateFederatedApi::validateAndSanitizeForCreation + ); + } catch (Exception e) { + log.warn("An error occurred while importing asset {}", asset, e); + } + }) + ) + .ignoreElements(); + } + + public record Input(String integrationId, AuditInfo auditInfo) {} + + public Api adaptAssetToApi(Asset asset, Integration integration) { + var now = TimeProvider.now(); + return Api + .builder() + .id(UuidString.generateRandom()) + .version(asset.version()) + .definitionVersion(DefinitionVersion.FEDERATED) + .name(asset.name()) + .description(asset.description()) + .createdAt(now) + .updatedAt(now) + .environmentId(integration.getEnvironmentId()) + .lifecycleState(null) + .build(); + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/integration/AgentIntegrationImpl.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/integration/AgentIntegrationImpl.java new file mode 100644 index 00000000000..b469296be13 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/infra/integration/AgentIntegrationImpl.java @@ -0,0 +1,31 @@ +/* + * 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.infra.integration; + +import io.gravitee.apim.core.integration.model.Asset; +import io.gravitee.apim.core.integration.model.Integration; +import io.gravitee.apim.core.integration.spi.IntegrationAgent; +import io.reactivex.rxjava3.core.Flowable; +import org.springframework.stereotype.Component; + +@Component +public class AgentIntegrationImpl implements IntegrationAgent { + + @Override + public Flowable fetchAllAssets(Integration integration) { + return Flowable.empty(); + } +} 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 80067dc2e96..ae68a7d0c44 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 @@ -265,4 +265,14 @@ public static Api aTcpApiV4(List hosts) { ) .build(); } + + public static Api aFederatedApi() { + return BASE + .get() + .definitionVersion(DefinitionVersion.FEDERATED) + .lifecycleState(null) + .apiDefinitionV4(null) + .apiDefinition(null) + .build(); + } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/IntegrationAssetFixtures.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/IntegrationAssetFixtures.java new file mode 100644 index 00000000000..07335e36a77 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/core/model/IntegrationAssetFixtures.java @@ -0,0 +1,37 @@ +/* + * 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 fixtures.core.model; + +import io.gravitee.apim.core.integration.model.Asset; +import java.util.function.Supplier; + +public class IntegrationAssetFixtures { + + private IntegrationAssetFixtures() {} + + private static final Supplier BASE = () -> + Asset + .builder() + .integrationId("integration-id") + .id("asset-id") + .name("An alien API") + .description("An alien API description") + .version("1.0.0"); + + public static Asset anAssetForIntegration(String integrationId) { + return BASE.get().integrationId(integrationId).build(); + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/IntegrationAgentInMemory.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/IntegrationAgentInMemory.java new file mode 100644 index 00000000000..d9bbf2d2b7f --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/inmemory/IntegrationAgentInMemory.java @@ -0,0 +1,49 @@ +/* + * 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 inmemory; + +import io.gravitee.apim.core.integration.model.Asset; +import io.gravitee.apim.core.integration.model.Integration; +import io.gravitee.apim.core.integration.spi.IntegrationAgent; +import io.reactivex.rxjava3.core.Flowable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class IntegrationAgentInMemory implements IntegrationAgent, InMemoryAlternative { + + List storage = new ArrayList<>(); + + @Override + public Flowable fetchAllAssets(Integration integration) { + return Flowable.fromIterable(storage).filter(asset -> asset.integrationId().equals(integration.getId())); + } + + @Override + public void initWith(List items) { + this.storage.addAll(items); + } + + @Override + public void reset() { + this.storage.clear(); + } + + @Override + public List storage() { + return Collections.unmodifiableList(storage); + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/api/domain_service/ValidateFederatedApiDomainServiceTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/api/domain_service/ValidateFederatedApiDomainServiceTest.java new file mode 100644 index 00000000000..1d8feb4bb89 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/api/domain_service/ValidateFederatedApiDomainServiceTest.java @@ -0,0 +1,60 @@ +/* + * 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.domain_service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import fixtures.core.model.ApiFixtures; +import io.gravitee.apim.core.api.model.Api; +import io.gravitee.apim.core.exception.ValidationDomainException; +import io.gravitee.definition.model.DefinitionVersion; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +class ValidateFederatedApiDomainServiceTest { + + ValidateFederatedApiDomainService service = new ValidateFederatedApiDomainService(); + + @Test + void should_return_the_api_when_valid() { + var api = ApiFixtures.aFederatedApi(); + + var result = service.validateAndSanitizeForCreation(api); + + assertThat(result).isSameAs(api); + } + + @Test + void should_reset_lifecycle_state_when_defined() { + var api = ApiFixtures.aFederatedApi().toBuilder().lifecycleState(Api.LifecycleState.STARTED).build(); + + var result = service.validateAndSanitizeForCreation(api); + + assertThat(result).extracting(Api::getLifecycleState).isNull(); + } + + @ParameterizedTest + @EnumSource(value = DefinitionVersion.class, mode = EnumSource.Mode.EXCLUDE, names = { "FEDERATED" }) + void should_throw_when_definition_version_is_incorrect(DefinitionVersion definitionVersion) { + var api = ApiFixtures.aFederatedApi().toBuilder().definitionVersion(definitionVersion).build(); + + var throwable = catchThrowable(() -> service.validateAndSanitizeForCreation(api)); + + assertThat(throwable).isInstanceOf(ValidationDomainException.class); + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/integration/use_case/DiscoverIntegrationAssetUseCaseTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/integration/use_case/DiscoverIntegrationAssetUseCaseTest.java new file mode 100644 index 00000000000..949039474d8 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/apim/core/integration/use_case/DiscoverIntegrationAssetUseCaseTest.java @@ -0,0 +1,461 @@ +/* + * 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.integration.use_case; + +import static fixtures.core.model.RoleFixtures.apiPrimaryOwnerRoleId; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import fixtures.core.model.AuditInfoFixtures; +import fixtures.core.model.IntegrationAssetFixtures; +import fixtures.core.model.IntegrationFixture; +import inmemory.ApiCrudServiceInMemory; +import inmemory.ApiMetadataQueryServiceInMemory; +import inmemory.AuditCrudServiceInMemory; +import inmemory.FlowCrudServiceInMemory; +import inmemory.GroupQueryServiceInMemory; +import inmemory.InMemoryAlternative; +import inmemory.IndexerInMemory; +import inmemory.IntegrationAgentInMemory; +import inmemory.IntegrationCrudServiceInMemory; +import inmemory.MembershipCrudServiceInMemory; +import inmemory.MembershipQueryServiceInMemory; +import inmemory.MetadataCrudServiceInMemory; +import inmemory.NotificationConfigCrudServiceInMemory; +import inmemory.ParametersQueryServiceInMemory; +import inmemory.RoleQueryServiceInMemory; +import inmemory.UserCrudServiceInMemory; +import inmemory.WorkflowCrudServiceInMemory; +import io.gravitee.apim.core.api.domain_service.ApiIndexerDomainService; +import io.gravitee.apim.core.api.domain_service.ApiMetadataDecoderDomainService; +import io.gravitee.apim.core.api.domain_service.ApiMetadataDomainService; +import io.gravitee.apim.core.api.domain_service.CreateApiDomainService; +import io.gravitee.apim.core.api.domain_service.ValidateFederatedApiDomainService; +import io.gravitee.apim.core.api.model.Api; +import io.gravitee.apim.core.audit.domain_service.AuditDomainService; +import io.gravitee.apim.core.audit.model.AuditEntity; +import io.gravitee.apim.core.audit.model.AuditInfo; +import io.gravitee.apim.core.audit.model.event.ApiAuditEvent; +import io.gravitee.apim.core.audit.model.event.MembershipAuditEvent; +import io.gravitee.apim.core.exception.ValidationDomainException; +import io.gravitee.apim.core.integration.exception.IntegrationNotFoundException; +import io.gravitee.apim.core.integration.model.Asset; +import io.gravitee.apim.core.integration.model.Integration; +import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerDomainService; +import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerFactory; +import io.gravitee.apim.core.membership.model.Membership; +import io.gravitee.apim.core.membership.model.PrimaryOwnerEntity; +import io.gravitee.apim.core.policy.domain_service.PolicyValidationDomainService; +import io.gravitee.apim.core.search.model.IndexableApi; +import io.gravitee.apim.core.user.model.BaseUserEntity; +import io.gravitee.apim.infra.json.jackson.JacksonJsonDiffProcessor; +import io.gravitee.apim.infra.template.FreemarkerTemplateProcessor; +import io.gravitee.common.utils.TimeProvider; +import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.repository.management.model.Parameter; +import io.gravitee.repository.management.model.ParameterReferenceType; +import io.gravitee.rest.api.model.parameters.Key; +import io.gravitee.rest.api.model.settings.ApiPrimaryOwnerMode; +import io.gravitee.rest.api.service.common.UuidString; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +class DiscoverIntegrationAssetUseCaseTest { + + private static final Instant INSTANT_NOW = Instant.parse("2023-10-22T10:15:30Z"); + private static final String INTEGRATION_ID = "integration-id"; + private static final String ORGANIZATION_ID = "organization-id"; + private static final String ENVIRONMENT_ID = "environment-id"; + private static final String USER_ID = "user-id"; + + private static final AuditInfo AUDIT_INFO = AuditInfoFixtures.anAuditInfo(ORGANIZATION_ID, ENVIRONMENT_ID, USER_ID); + + PolicyValidationDomainService policyValidationDomainService = mock(PolicyValidationDomainService.class); + ApiCrudServiceInMemory apiCrudService = new ApiCrudServiceInMemory(); + AuditCrudServiceInMemory auditCrudService = new AuditCrudServiceInMemory(); + GroupQueryServiceInMemory groupQueryService = new GroupQueryServiceInMemory(); + IntegrationCrudServiceInMemory integrationCrudService = new IntegrationCrudServiceInMemory(); + MembershipCrudServiceInMemory membershipCrudService = new MembershipCrudServiceInMemory(); + MetadataCrudServiceInMemory metadataCrudService = new MetadataCrudServiceInMemory(); + NotificationConfigCrudServiceInMemory notificationConfigCrudService = new NotificationConfigCrudServiceInMemory(); + ParametersQueryServiceInMemory parametersQueryService = new ParametersQueryServiceInMemory(); + RoleQueryServiceInMemory roleQueryService = new RoleQueryServiceInMemory(); + UserCrudServiceInMemory userCrudService = new UserCrudServiceInMemory(); + WorkflowCrudServiceInMemory workflowCrudService = new WorkflowCrudServiceInMemory(); + + IntegrationAgentInMemory integrationAgent = new IntegrationAgentInMemory(); + IndexerInMemory indexer = new IndexerInMemory(); + + ValidateFederatedApiDomainService validateFederatedApiDomainService = spy(new ValidateFederatedApiDomainService()); + DiscoverIntegrationAssetUseCase useCase; + + @BeforeAll + static void beforeAll() { + UuidString.overrideGenerator(() -> "generated-id"); + TimeProvider.overrideClock(Clock.fixed(INSTANT_NOW, ZoneId.systemDefault())); + } + + @AfterAll + static void afterAll() { + UuidString.reset(); + TimeProvider.overrideClock(Clock.systemDefaultZone()); + } + + @BeforeEach + void setUp() { + var auditDomainService = new AuditDomainService(auditCrudService, userCrudService, new JacksonJsonDiffProcessor()); + + var metadataQueryService = new ApiMetadataQueryServiceInMemory(metadataCrudService); + var membershipQueryService = new MembershipQueryServiceInMemory(membershipCrudService); + var apiPrimaryOwnerFactory = new ApiPrimaryOwnerFactory( + groupQueryService, + membershipQueryService, + parametersQueryService, + roleQueryService, + userCrudService + ); + + var createApiDomainService = new CreateApiDomainService( + apiCrudService, + auditDomainService, + new ApiIndexerDomainService( + new ApiMetadataDecoderDomainService(metadataQueryService, new FreemarkerTemplateProcessor()), + indexer + ), + new ApiMetadataDomainService(metadataCrudService, auditDomainService), + new ApiPrimaryOwnerDomainService( + auditDomainService, + groupQueryService, + membershipCrudService, + membershipQueryService, + roleQueryService, + userCrudService + ), + new FlowCrudServiceInMemory(), + notificationConfigCrudService, + parametersQueryService, + workflowCrudService + ); + + useCase = + new DiscoverIntegrationAssetUseCase( + integrationCrudService, + apiPrimaryOwnerFactory, + validateFederatedApiDomainService, + createApiDomainService, + integrationAgent + ); + + enableApiPrimaryOwnerMode(ApiPrimaryOwnerMode.USER); + when(policyValidationDomainService.validateAndSanitizeConfiguration(any(), any())) + .thenAnswer(invocation -> invocation.getArgument(1)); + + roleQueryService.resetSystemRoles(ORGANIZATION_ID); + givenExistingUsers( + List.of(BaseUserEntity.builder().id(USER_ID).firstname("Jane").lastname("Doe").email("jane.doe@gravitee.io").build()) + ); + } + + @AfterEach + void tearDown() { + Stream + .of( + apiCrudService, + auditCrudService, + groupQueryService, + integrationCrudService, + membershipCrudService, + parametersQueryService, + userCrudService + ) + .forEach(InMemoryAlternative::reset); + } + + @Nested + class FederatedApiCreation { + + @Test + void should_create_and_index_a_federated_api() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets( + IntegrationAssetFixtures + .anAssetForIntegration(INTEGRATION_ID) + .toBuilder() + .id("asset-1") + .name("api-1") + .description("my description") + .version("1.1.1") + .build() + ); + + // When + useCase.execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)).test().awaitDone(10, TimeUnit.SECONDS); + + // Then + SoftAssertions.assertSoftly(soft -> { + Api expectedApi = Api + .builder() + .id("generated-id") + .definitionVersion(DefinitionVersion.FEDERATED) + .name("api-1") + .description("my description") + .version("1.1.1") + .createdAt(INSTANT_NOW.atZone(ZoneId.systemDefault())) + .updatedAt(INSTANT_NOW.atZone(ZoneId.systemDefault())) + .environmentId(ENVIRONMENT_ID) + .lifecycleState(null) + .build(); + soft.assertThat(apiCrudService.storage()).containsExactlyInAnyOrder(expectedApi); + soft + .assertThat(indexer.storage()) + .containsExactly( + new IndexableApi( + expectedApi, + new PrimaryOwnerEntity(USER_ID, "jane.doe@gravitee.io", "Jane Doe", PrimaryOwnerEntity.Type.USER), + Map.of() + ) + ); + }); + } + + @Test + void should_create_an_audit() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets(IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID)); + + // When + useCase.execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)).test().awaitDone(10, TimeUnit.SECONDS); + + // Then + assertThat(auditCrudService.storage()) + .usingRecursiveFieldByFieldElementComparatorIgnoringFields("patch") + .containsExactly( + // API Audit + AuditEntity + .builder() + .id("generated-id") + .organizationId(ORGANIZATION_ID) + .environmentId(ENVIRONMENT_ID) + .referenceType(AuditEntity.AuditReferenceType.API) + .referenceId("generated-id") + .user(USER_ID) + .properties(Collections.emptyMap()) + .event(ApiAuditEvent.API_CREATED.name()) + .createdAt(INSTANT_NOW.atZone(ZoneId.systemDefault())) + .build(), + // Membership Audit + AuditEntity + .builder() + .id("generated-id") + .organizationId(ORGANIZATION_ID) + .environmentId(ENVIRONMENT_ID) + .referenceType(AuditEntity.AuditReferenceType.API) + .referenceId("generated-id") + .user(USER_ID) + .properties(Map.of("USER", USER_ID)) + .event(MembershipAuditEvent.MEMBERSHIP_CREATED.name()) + .createdAt(INSTANT_NOW.atZone(ZoneId.systemDefault())) + .build() + ); + } + + @ParameterizedTest + @EnumSource(value = ApiPrimaryOwnerMode.class, mode = EnumSource.Mode.INCLUDE, names = { "USER", "HYBRID" }) + void should_create_primary_owner_membership_when_user_or_hybrid_mode_is_enabled(ApiPrimaryOwnerMode mode) { + // Given + enableApiPrimaryOwnerMode(mode); + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets(IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID)); + + // When + useCase.execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)).test().awaitDone(10, TimeUnit.SECONDS); + + // Then + assertThat(membershipCrudService.storage()) + .contains( + Membership + .builder() + .id("generated-id") + .roleId(apiPrimaryOwnerRoleId(ORGANIZATION_ID)) + .memberId(USER_ID) + .memberType(Membership.Type.USER) + .referenceId("generated-id") + .referenceType(Membership.ReferenceType.API) + .source("system") + .createdAt(INSTANT_NOW.atZone(ZoneId.systemDefault())) + .updatedAt(INSTANT_NOW.atZone(ZoneId.systemDefault())) + .build() + ); + } + + @Test + void should_not_create_default_metadata() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets(IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID)); + + // When + useCase.execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)).test().awaitDone(10, TimeUnit.SECONDS); + + // Then + assertThat(metadataCrudService.storage()).isEmpty(); + } + + @Test + void should_not_create_default_email_notification_configuration() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets(IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID)); + + // When + useCase.execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)).test().awaitDone(10, TimeUnit.SECONDS); + + // Then + assertThat(notificationConfigCrudService.storage()).isEmpty(); + } + + @Test + void should_ignore_api_review_mode() { + // Given + enableApiReview(); + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets( + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-1").name("api-1").build(), + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-2").name("api-2").build() + ); + + // When + useCase + .execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)) + .test() + .awaitDone(0, TimeUnit.SECONDS) + .assertComplete(); + + // Then + assertThat(workflowCrudService.storage()).isEmpty(); + } + + private void enableApiReview() { + parametersQueryService.define( + new Parameter(Key.API_REVIEW_ENABLED.key(), ENVIRONMENT_ID, ParameterReferenceType.ENVIRONMENT, "true") + ); + } + } + + @Test + void should_throw_when_no_integration_is_found() { + // When + var obs = useCase.execute(new DiscoverIntegrationAssetUseCase.Input("unknown", AUDIT_INFO)).test(); + + // Then + obs.assertError(IntegrationNotFoundException.class); + } + + @Test + void should_do_nothing_when_no_asset_to_import() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration().withId(INTEGRATION_ID)); + + // When + useCase.execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)).test().awaitDone(10, TimeUnit.SECONDS); + + // Then + Assertions.assertThat(apiCrudService.storage()).isEmpty(); + } + + @Test + void should_create_a_federated_api_for_each_asset() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets( + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-1").name("api-1").build(), + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-2").name("api-2").build() + ); + + // When + useCase + .execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)) + .test() + .awaitDone(0, TimeUnit.SECONDS) + .assertComplete(); + + // Then + Assertions.assertThat(apiCrudService.storage()).extracting(Api::getName).containsExactlyInAnyOrder("api-1", "api-2"); + } + + @Test + void should_skip_creating_federated_api_when_validation_fails() { + // Given + givenAnIntegration(IntegrationFixture.anIntegration(ENVIRONMENT_ID).withId(INTEGRATION_ID)); + givenAssets( + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-1").name("api-1").build(), + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-2").name("api-2").build(), + IntegrationAssetFixtures.anAssetForIntegration(INTEGRATION_ID).toBuilder().id("asset-3").name("api-3").build() + ); + when(validateFederatedApiDomainService.validateAndSanitizeForCreation(argThat(api -> api.getName().equals("api-2")))) + .thenThrow(new ValidationDomainException("validation failed")); + + // When + useCase + .execute(new DiscoverIntegrationAssetUseCase.Input(INTEGRATION_ID, AUDIT_INFO)) + .test() + .awaitDone(0, TimeUnit.SECONDS) + .assertComplete(); + + // Then + Assertions.assertThat(apiCrudService.storage()).extracting(Api::getName).containsExactlyInAnyOrder("api-1", "api-3"); + } + + private void givenAnIntegration(Integration integration) { + integrationCrudService.initWith(List.of(integration)); + } + + private void givenAssets(Asset... assets) { + integrationAgent.initWith(List.of(assets)); + } + + private void givenExistingUsers(List users) { + userCrudService.initWith(users); + } + + private void enableApiPrimaryOwnerMode(ApiPrimaryOwnerMode mode) { + parametersQueryService.initWith( + List.of(new Parameter(Key.API_PRIMARY_OWNER_MODE.key(), ENVIRONMENT_ID, ParameterReferenceType.ENVIRONMENT, mode.name())) + ); + } +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-test-fixtures/src/main/java/fixtures/ApiModelFixtures.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-test-fixtures/src/main/java/fixtures/ApiModelFixtures.java index c701fbc0b60..a2870d25f41 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-test-fixtures/src/main/java/fixtures/ApiModelFixtures.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-test-fixtures/src/main/java/fixtures/ApiModelFixtures.java @@ -16,6 +16,7 @@ package fixtures; import io.gravitee.common.component.Lifecycle; +import io.gravitee.definition.model.DefinitionVersion; import io.gravitee.definition.model.Rule; import io.gravitee.definition.model.services.Services; import io.gravitee.definition.model.v4.flow.execution.FlowExecution; @@ -104,6 +105,16 @@ private ApiModelFixtures() {} .background("my-background") .backgroundUrl("my-background-url"); + private static final ApiEntity.ApiEntityBuilder BASE_MODEL_API_FEDERATED = ApiEntity + .builder() + .id("my-id") + .name("my-name") + .apiVersion("v1.0") + .definitionVersion(DefinitionVersion.FEDERATED) + .deployedAt(new Date()) + .createdAt(new Date()) + .updatedAt(new Date()); + public static io.gravitee.rest.api.model.api.ApiEntity aModelApiV1() { return BASE_MODEL_API_V1.build(); } @@ -121,6 +132,7 @@ public static GenericApiEntity aGenericApiEntity(final io.gravitee.definition.mo case V1 -> aModelApiV1(); case V2 -> aModelApiV2(); case V4 -> aModelApiV4(); + case FEDERATED -> BASE_MODEL_API_FEDERATED.build(); }; } }