diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/ApiMapper.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/ApiMapper.java index 7af415a905e..6d08df826af 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/ApiMapper.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/ApiMapper.java @@ -50,6 +50,7 @@ import io.gravitee.rest.api.model.v4.api.ApiEntity; import io.gravitee.rest.api.model.v4.api.GenericApiEntity; import io.gravitee.rest.api.model.v4.api.UpdateApiEntity; +import io.gravitee.rest.api.model.v4.nativeapi.NativeApiEntity; import jakarta.ws.rs.core.UriInfo; import java.util.ArrayList; import java.util.List; @@ -117,7 +118,11 @@ default Api map(GenericApiEntity apiEntity, UriInfo uriInfo, Boolean isSynchroni return new io.gravitee.rest.api.management.v2.rest.model.Api(this.mapToFederated((FederatedApiEntity) apiEntity, uriInfo)); } if (apiEntity.getDefinitionVersion() == io.gravitee.definition.model.DefinitionVersion.V4) { - return new io.gravitee.rest.api.management.v2.rest.model.Api(this.mapToV4((ApiEntity) apiEntity, uriInfo, state)); + if (apiEntity instanceof ApiEntity asApiEntity) { + return new io.gravitee.rest.api.management.v2.rest.model.Api(this.mapToV4(asApiEntity, uriInfo, state)); + } else if (apiEntity instanceof NativeApiEntity asNativeApiEntity) { + return new io.gravitee.rest.api.management.v2.rest.model.Api(this.mapToV4(asNativeApiEntity, uriInfo, state)); + } } if (apiEntity.getDefinitionVersion() == io.gravitee.definition.model.DefinitionVersion.V2) { return new io.gravitee.rest.api.management.v2.rest.model.Api( @@ -155,6 +160,11 @@ default List map(List apiEntities, UriInfo uriInfo, Funct @Mapping(target = "links", expression = "java(computeApiLinks(apiEntity, uriInfo))") ApiV4 mapToV4(ApiEntity apiEntity, UriInfo uriInfo, GenericApi.DeploymentStateEnum deploymentState); + @Mapping(target = "definitionContext", source = "apiEntity.originContext") + @Mapping(target = "listeners", qualifiedByName = "fromNativeListeners") + @Mapping(target = "links", expression = "java(computeApiLinks(apiEntity, uriInfo))") + ApiV4 mapToV4(NativeApiEntity apiEntity, UriInfo uriInfo, GenericApi.DeploymentStateEnum deploymentState); + default ApiV4 mapToV4(io.gravitee.apim.core.api.model.Api source, UriInfo uriInfo, GenericApi.DeploymentStateEnum deploymentState) { if (ApiType.NATIVE.equals(source.getType())) { return mapToNativeV4(source, uriInfo, deploymentState); @@ -199,7 +209,11 @@ io.gravitee.rest.api.management.v2.rest.model.ApiV1 mapToV1( @Mapping(target = "listeners", qualifiedByName = "fromHttpListeners") @Mapping(target = "links", ignore = true) - ApiV4 map(ApiEntity apiEntity); + ApiV4 mapFromHttpApiEntity(ApiEntity apiEntity); + + @Mapping(target = "listeners", qualifiedByName = "fromNativeListeners") + @Mapping(target = "links", ignore = true) + ApiV4 mapFromNativeApiEntity(NativeApiEntity apiEntity); @Mapping(target = "listeners", qualifiedByName = "toHttpListeners") ApiEntity map(ApiV4 api); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource.java index 72476336856..67fd460b57f 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource.java @@ -87,6 +87,7 @@ import io.gravitee.rest.api.model.v4.api.ExportApiEntity; import io.gravitee.rest.api.model.v4.api.GenericApiEntity; import io.gravitee.rest.api.model.v4.api.UpdateApiEntity; +import io.gravitee.rest.api.model.v4.nativeapi.NativeApiEntity; import io.gravitee.rest.api.rest.annotation.Permission; import io.gravitee.rest.api.rest.annotation.Permissions; import io.gravitee.rest.api.security.utils.ImageUtils; @@ -851,11 +852,17 @@ private void setPicturesUrl(final GenericApiEntity apiEntity) { String backgroundUrl = uriBuilder.build().toString(); if (apiEntity.getDefinitionVersion() == DefinitionVersion.V4) { - ApiEntity apiEntityV4 = (ApiEntity) apiEntity; - apiEntityV4.setPictureUrl(pictureUrl); - apiEntityV4.setPicture(null); - apiEntityV4.setBackgroundUrl(backgroundUrl); - apiEntityV4.setBackground(null); + if (apiEntity instanceof ApiEntity apiEntityV4) { + apiEntityV4.setPictureUrl(pictureUrl); + apiEntityV4.setPicture(null); + apiEntityV4.setBackgroundUrl(backgroundUrl); + apiEntityV4.setBackground(null); + } else if (apiEntity instanceof NativeApiEntity nativeApiEntityV4) { + nativeApiEntityV4.setPictureUrl(pictureUrl); + nativeApiEntityV4.setPicture(null); + nativeApiEntityV4.setBackgroundUrl(backgroundUrl); + nativeApiEntityV4.setBackground(null); + } } if (apiEntity.getDefinitionVersion() == DefinitionVersion.V2) { io.gravitee.rest.api.model.api.ApiEntity apiEntityV2 = (io.gravitee.rest.api.model.api.ApiEntity) apiEntity; @@ -868,7 +875,11 @@ private void setPicturesUrl(final GenericApiEntity apiEntity) { private void filterSensitiveData(GenericApiEntity apiEntity) { if (apiEntity.getDefinitionVersion() == DefinitionVersion.V4) { - filterSensitiveData((ApiEntity) apiEntity); + if (apiEntity instanceof ApiEntity asApiEntity) { + filterSensitiveData(asApiEntity); + } else if (apiEntity instanceof NativeApiEntity asNativeApiEntity) { + filterSensitiveData(asNativeApiEntity); + } } if (apiEntity.getDefinitionVersion() == DefinitionVersion.V2) { filterSensitiveData((io.gravitee.rest.api.model.api.ApiEntity) apiEntity); @@ -897,6 +908,12 @@ private void filterSensitiveData(ApiEntity apiEntity) { apiEntity.setResponseTemplates(null); } + private void filterSensitiveData(NativeApiEntity apiEntity) { + apiEntity.setProperties(null); + apiEntity.setServices(null); + apiEntity.setResources(null); + } + private void filterSensitiveData(io.gravitee.rest.api.model.api.ApiEntity apiEntity) { final Proxy filteredProxy = new Proxy(); final VirtualHost virtualHost = apiEntity.getProxy().getVirtualHosts().get(0); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource_getApiByIdTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource_getApiByIdTest.java index 02d95f8da7c..dce973cf7a7 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource_getApiByIdTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/api/ApiResource_getApiByIdTest.java @@ -40,6 +40,7 @@ import io.gravitee.definition.model.services.dynamicproperty.DynamicPropertyService; import io.gravitee.definition.model.services.dynamicproperty.http.HttpDynamicPropertyProviderConfiguration; import io.gravitee.definition.model.services.healthcheck.HealthCheckService; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.definition.model.v4.analytics.Analytics; import io.gravitee.definition.model.v4.endpointgroup.Endpoint; import io.gravitee.definition.model.v4.endpointgroup.EndpointGroup; @@ -56,6 +57,12 @@ import io.gravitee.definition.model.v4.listener.http.Path; import io.gravitee.definition.model.v4.listener.subscription.SubscriptionListener; import io.gravitee.definition.model.v4.listener.tcp.TcpListener; +import io.gravitee.definition.model.v4.nativeapi.NativeApiServices; +import io.gravitee.definition.model.v4.nativeapi.NativeEndpoint; +import io.gravitee.definition.model.v4.nativeapi.NativeEndpointGroup; +import io.gravitee.definition.model.v4.nativeapi.NativeEntrypoint; +import io.gravitee.definition.model.v4.nativeapi.NativeFlow; +import io.gravitee.definition.model.v4.nativeapi.kafka.KafkaListener; import io.gravitee.definition.model.v4.property.Property; import io.gravitee.definition.model.v4.resource.Resource; import io.gravitee.definition.model.v4.service.ApiServices; @@ -68,6 +75,7 @@ import io.gravitee.rest.api.model.permissions.RolePermission; import io.gravitee.rest.api.model.permissions.RolePermissionAction; import io.gravitee.rest.api.model.v4.api.ApiEntity; +import io.gravitee.rest.api.model.v4.nativeapi.NativeApiEntity; import io.gravitee.rest.api.service.common.GraviteeContext; import io.gravitee.rest.api.service.exceptions.ApiNotFoundException; import jakarta.ws.rs.core.Response; @@ -266,6 +274,138 @@ public void should_get_filtered_api_V4() { assertNull((responseApi.getListeners().get(0).getHttpListener()).getPaths().get(0).getHost()); } + @Test + public void should_get_native_api_V4() throws JsonProcessingException { + when(apiSearchServiceV4.findGenericById(GraviteeContext.getExecutionContext(), API)).thenReturn(this.fakeNativeApiEntityV4()); + + when(apiStateServiceV4.isSynchronized(eq(GraviteeContext.getExecutionContext()), any())).thenReturn(false); + + final Response response = rootTarget(API).request().get(); + + assertEquals(OK_200, response.getStatus()); + + final ApiV4 responseApi = response.readEntity(ApiV4.class); + + assertNotNull(responseApi); + assertEquals(API, responseApi.getName()); + assertEquals(GenericApi.DeploymentStateEnum.NEED_REDEPLOY, responseApi.getDeploymentState()); + assertNotNull(responseApi.getLinks()); + assertNotNull(responseApi.getLinks().getPictureUrl()); + assertTrue(responseApi.getLinks().getPictureUrl().contains("environments/my-env/apis/my-api/picture")); + assertNotNull(responseApi.getLinks().getBackgroundUrl()); + assertTrue(responseApi.getLinks().getBackgroundUrl().contains("environments/my-env/apis/my-api/background")); + assertNotNull(responseApi.getProperties()); + assertEquals(1, responseApi.getProperties().size()); + assertNotNull(responseApi.getServices()); + assertNotNull(responseApi.getResources()); + assertEquals(1, responseApi.getResources().size()); + assertEquals(0, responseApi.getResponseTemplates().size()); + + assertNotNull(responseApi.getListeners()); + assertEquals(1, responseApi.getListeners().size()); + + var kafkaListener = responseApi.getListeners().get(0).getKafkaListener(); + assertNotNull(kafkaListener); + assertNotNull(kafkaListener.getHost()); + assertNotNull(kafkaListener.getPort()); + var foundEntrypoint = kafkaListener.getEntrypoints().get(0); + assertNotNull(foundEntrypoint); + LinkedHashMap configuration = (LinkedHashMap) foundEntrypoint.getConfiguration(); + assertEquals("configuration", configuration.get("nice")); + assertEquals("native-kafka", foundEntrypoint.getType()); + assertEquals("KAFKA", kafkaListener.getType().toString()); + + assertNotNull(responseApi.getEndpointGroups()); + assertEquals(1, responseApi.getEndpointGroups().size()); + assertNotNull(responseApi.getEndpointGroups().get(0)); + EndpointGroupV4 endpointGroup = responseApi.getEndpointGroups().get(0); + LinkedHashMap sharedConfig = (LinkedHashMap) endpointGroup.getSharedConfiguration(); + assertNotNull(sharedConfig); + assertEquals("configuration", sharedConfig.get("shared")); + assertNotNull(responseApi.getEndpointGroups().get(0).getEndpoints()); + assertEquals(1, responseApi.getEndpointGroups().get(0).getEndpoints().size()); + + var endpoint = responseApi.getEndpointGroups().get(0).getEndpoints().get(0); + assertNotNull(endpoint); + assertEquals("native-kafka", endpoint.getType()); + + JsonNode jsonNode = mapper.valueToTree((LinkedHashMap) endpoint.getConfiguration()); + assertEquals("kafka:9092", jsonNode.get("bootstrapServers").asText()); + assertEquals(List.of("demo"), mapper.readValue(jsonNode.get("topics").toString(), List.class)); + + assertNotNull(responseApi.getFlows()); + assertEquals(1, responseApi.getFlows().size()); + + var flow = responseApi.getFlows().get(0); + assertNotNull(flow); + assertEquals("flowName", flow.getName()); + assertEquals(Boolean.TRUE, flow.getEnabled()); + assertNotNull(flow.getTags()); + assertEquals(2, flow.getTags().size()); + assertEquals(Set.of("tag1", "tag2"), flow.getTags()); + assertNotNull(flow.getConnect()); + assertNotNull(flow.getInteract()); + assertNotNull(flow.getPublish()); + assertNotNull(flow.getSubscribe()); + + assertNull(flow.getRequest()); + assertNull(flow.getResponse()); + assertNull(flow.getSelectors()); + + assertEquals(1, flow.getConnect().size()); + + var step = flow.getConnect().get(0); + assertNotNull(step); + assertEquals(Boolean.TRUE, step.getEnabled()); + assertEquals("my-policy", step.getPolicy()); + assertEquals("my-condition", step.getCondition()); + + var service = responseApi.getServices(); + assertNotNull(service); + assertNotNull(service.getDynamicProperty()); + assertNotNull(service.getDynamicProperty().getConfiguration()); + + ServiceV4 dynamicPropertyConfiguration = service.getDynamicProperty(); + assertNotNull(dynamicPropertyConfiguration); + assertEquals(true, dynamicPropertyConfiguration.getEnabled()); + assertEquals(true, dynamicPropertyConfiguration.getOverrideConfiguration()); + assertNotNull(dynamicPropertyConfiguration.getConfiguration()); + assertEquals("dynamic-property", dynamicPropertyConfiguration.getType()); + } + + @Test + public void should_get_filtered_native_api_V4() { + when(apiSearchServiceV4.findGenericById(GraviteeContext.getExecutionContext(), API)).thenReturn(this.fakeNativeApiEntityV4()); + when( + permissionService.hasPermission( + GraviteeContext.getExecutionContext(), + RolePermission.API_DEFINITION, + API, + RolePermissionAction.READ + ) + ) + .thenReturn(false); + + final Response response = rootTarget(API).request().get(); + + assertEquals(OK_200, response.getStatus()); + + final ApiV4 responseApi = response.readEntity(ApiV4.class); + assertNotNull(responseApi); + assertEquals(API, responseApi.getName()); + assertNotNull(responseApi.getLinks()); + assertNotNull(responseApi.getLinks().getPictureUrl()); + assertNotNull(responseApi.getLinks().getBackgroundUrl()); + assertNotNull(responseApi.getProperties()); + assertTrue(responseApi.getProperties().isEmpty()); + assertNull(responseApi.getServices()); + assertNull(responseApi.getResources()); + assertNotNull(responseApi.getResponseTemplates()); + assertEquals(0, responseApi.getResponseTemplates().size()); + assertNotNull(responseApi.getListeners()); + assertNotNull(responseApi.getListeners().get(0)); + } + @Test public void should_get_api_V2() { when(apiSearchServiceV4.findGenericById(GraviteeContext.getExecutionContext(), API)).thenReturn(this.fakeApiEntityV2()); @@ -465,6 +605,91 @@ private ApiEntity fakeApiEntityV4() { return apiEntity; } + private NativeApiEntity fakeNativeApiEntityV4() { + var apiEntity = new NativeApiEntity(); + apiEntity.setDefinitionVersion(DefinitionVersion.V4); + apiEntity.setType(ApiType.NATIVE); + apiEntity.setId(API); + apiEntity.setName(API); + var kafkaListener = new KafkaListener(); + kafkaListener.setHost("my.fake.host"); + kafkaListener.setPort(1000); + + var entrypoint = new NativeEntrypoint(); + entrypoint.setType("native-kafka"); + entrypoint.setConfiguration("{\"nice\": \"configuration\"}"); + kafkaListener.setEntrypoints(List.of(entrypoint)); + kafkaListener.setType(ListenerType.KAFKA); + + apiEntity.setListeners(List.of(kafkaListener)); + apiEntity.setProperties(List.of(new Property())); + apiEntity.setServices(new NativeApiServices()); + apiEntity.setResources(List.of(new Resource())); + apiEntity.setUpdatedAt(new Date()); + + var endpointGroup = new NativeEndpointGroup(); + endpointGroup.setType("native-kafka"); + endpointGroup.setSharedConfiguration("{\"shared\": \"configuration\"}"); + var endpoint = new NativeEndpoint(); + endpoint.setType("native-kafka"); + endpoint.setConfiguration( + "{\n" + + " \"bootstrapServers\": \"kafka:9092\",\n" + + " \"topics\": [\n" + + " \"demo\"\n" + + " ],\n" + + " \"producer\": {\n" + + " \"enabled\": false\n" + + " },\n" + + " \"consumer\": {\n" + + " \"encodeMessageId\": true,\n" + + " \"enabled\": true,\n" + + " \"autoOffsetReset\": \"earliest\"\n" + + " }\n" + + " }" + ); + endpointGroup.setEndpoints(List.of(endpoint)); + apiEntity.setEndpointGroups(List.of(endpointGroup)); + + var flow = new NativeFlow(); + flow.setName("flowName"); + flow.setEnabled(true); + + Step step = new Step(); + step.setEnabled(true); + step.setPolicy("my-policy"); + step.setCondition("my-condition"); + flow.setInteract(List.of(step)); + flow.setConnect(List.of(step)); + flow.setPublish(List.of(step)); + flow.setSubscribe(List.of(step)); + flow.setTags(Set.of("tag1", "tag2")); + + apiEntity.setFlows(List.of(flow)); + + Service dynamicProperty = new Service(); + dynamicProperty.setConfiguration( + "{\n" + + " \"enabled\": true,\n" + + " \"schedule\": \"*/10 * * * * *\",\n" + + " \"provider\": \"HTTP\",\n" + + " \"configuration\": {\n" + + " \"url\": \"https://api.gravitee.io/echo\",\n" + + " \"specification\": \"[{\\n \\\"operation\\\": \\\"shift\\\",\\n \\\"spec\\\": {\\n \\\"rating\\\": {\\n \\\"primary\\\": {\\n \\\"value\\\": \\\"Rating\\\",\\n \\\"max\\\": \\\"RatingRange\\\"\\n },\\n \\\"*\\\": {\\n \\\"max\\\": \\\"SecondaryRatings.&1.Range\\\",\\n \\\"value\\\": \\\"SecondaryRatings.&1.Value\\\",\\n \\\"$\\\": \\\"SecondaryRatings.&1.Id\\\"\\n }\\n }\\n }\\n },\\n {\\n \\\"operation\\\": \\\"default\\\",\\n \\\"spec\\\": {\\n \\\"Range\\\": 5,\\n \\\"SecondaryRatings\\\": {\\n \\\"*\\\": {\\n \\\"Range\\\": 5\\n }\\n }\\n }\\n }\\n]\",\n" + + " \"useSystemProxy\": false,\n" + + " \"method\": \"GET\",\n" + + " \"body\": \"{\\n \\\"rating\\\": {\\n \\\"primary\\\": {\\n \\\"value\\\": 3\\n },\\n \\\"quality\\\": {\\n \\\"value\\\": 3\\n }\\n }\\n }\"\n" + + " }\n" + + " }" + ); + dynamicProperty.setType("dynamic-property"); + dynamicProperty.setEnabled(true); + dynamicProperty.setOverrideConfiguration(true); + apiEntity.setServices(new NativeApiServices(dynamicProperty)); + + return apiEntity; + } + private io.gravitee.rest.api.model.api.ApiEntity fakeApiEntityV2() { var apiEntity = new io.gravitee.rest.api.model.api.ApiEntity(); apiEntity.setGraviteeDefinitionVersion(DefinitionVersion.V2.getLabel()); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-model/src/main/java/io/gravitee/rest/api/model/v4/nativeapi/NativeApiEntity.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-model/src/main/java/io/gravitee/rest/api/model/v4/nativeapi/NativeApiEntity.java new file mode 100644 index 00000000000..a9d34534f54 --- /dev/null +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-model/src/main/java/io/gravitee/rest/api/model/v4/nativeapi/NativeApiEntity.java @@ -0,0 +1,203 @@ +/* + * 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.rest.api.model.v4.nativeapi; + +import com.fasterxml.jackson.annotation.JsonIgnore; +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.failover.Failover; +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.NativeApiServices; +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.DeploymentRequired; +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 io.gravitee.rest.api.model.v4.api.GenericApiEntity; +import io.gravitee.rest.api.model.v4.plan.PlanEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.With; + +/** + * @author Guillaume LAMIRAND (guillaume.lamirand at graviteesource.com) + * @author GraviteeSource Team + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@EqualsAndHashCode +@Schema(name = "NativeApiEntityV4") +@Builder(toBuilder = true) +@With +public class NativeApiEntity implements GenericApiEntity { + + @Schema(description = "API's uuid.", example = "00f8c9e7-78fc-4907-b8c9-e778fc790750") + private String id; + + @Schema(description = "API's crossId. Identifies API across environments.", example = "df83b2a4-cc3e-3f80-9f0d-c138c106c076") + private String crossId; + + @Schema(description = "API's name. Duplicate names can exists.", example = "My Api") + private String name; + + @Schema(description = "Api's version. It's a simple string only used in the portal.", example = "v1.0") + private String apiVersion; + + @Schema(description = "API's gravitee definition version") + private DefinitionVersion definitionVersion; + + @Schema(description = "API's type", defaultValue = "NATIVE") + @Builder.Default + private ApiType type = ApiType.NATIVE; + + @Schema(description = "The last date (as timestamp) when the API was deployed.", example = "1581256457163") + private Date deployedAt; + + @Schema(description = "The date (as a timestamp) when the API was created.", example = "1581256457163") + private Date createdAt; + + @Schema(description = "The last date (as a timestamp) when the API was updated.", example = "1581256457163") + private Date updatedAt; + + @Schema( + description = "API's description. A short description of your API.", + example = "I can use a hundred characters to describe this API." + ) + private String description; + + @Schema(description = "The list of sharding tags associated with this API.", example = "public, private") + @DeploymentRequired + @Builder.Default + private Set tags = new HashSet<>(); + + @Schema(description = "A list of listeners used to describe our you api could be reached.") + @DeploymentRequired + private List listeners; + + @Schema(description = "A list of endpoint describing the endpoints to contact.") + @DeploymentRequired + private List endpointGroups; + + @Schema(description = "A dictionary (could be dynamic) of properties available in the API context.") + @DeploymentRequired + @Builder.Default + private List properties = new ArrayList<>(); + + @Schema(description = "The list of API resources used by policies like cache resources or oauth2") + @DeploymentRequired + @Builder.Default + private List resources = new ArrayList<>(); + + @Schema(description = "A list of plans to apply on the API") + @DeploymentRequired + @Builder.Default + private Set plans = new HashSet<>(); + + @Schema(description = "A list of flows containing the policies configuration.") + @DeploymentRequired + private List flows; + + @DeploymentRequired + @Schema(description = "The configuration of API services like the dynamic properties.") + private NativeApiServices services; + + @Schema(description = "API's groups. Used to add team in your API.", example = "['MY_GROUP1', 'MY_GROUP2']") + private Set groups; + + @Schema(description = "The visibility of the API regarding the portal.", example = "PUBLIC") + private Visibility visibility; + + @Schema(description = "The status of the API regarding the gateway.", example = "STARTED") + @Builder.Default + private Lifecycle.State state = Lifecycle.State.STOPPED; + + @Schema(description = "The user with role PRIMARY_OWNER on this API.") + private PrimaryOwnerEntity primaryOwner; + + @Schema(description = "the API logo encoded in base64") + private String picture; + + @Schema( + description = "the API logo URL.", + example = "https://gravitee.mycompany.com/management/apis/6c530064-0b2c-4004-9300-640b2ce0047b/picture" + ) + private String pictureUrl; + + @Schema(description = "the list of categories associated with this API", example = "Product, Customer, Misc") + private Set categories; + + @Schema(description = "the free list of labels associated with this API", example = "json, read_only, awesome") + private List labels; + + /** Context explaining where the API comes from. */ + @Builder.Default + private OriginContext originContext = new OriginContext.Management(); + + @JsonIgnore + @Builder.Default + private Map metadata = new HashMap<>(); + + private ApiLifecycleState lifecycleState; + + private WorkflowState workflowState; + + private boolean disableMembershipNotifications; + + @Schema(description = "the API background encoded in base64") + private String background; + + @Schema( + description = "the API background url.", + example = "https://gravitee.mycompany.com/management/apis/6c530064-0b2c-4004-9300-640b2ce0047b/background" + ) + private String backgroundUrl; + + @JsonIgnore + private String referenceType; + + @JsonIgnore + private String referenceId; +} diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/impl/search/lucene/transformer/IndexableApiDocumentTransformer.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/impl/search/lucene/transformer/IndexableApiDocumentTransformer.java index be84c2cabc1..6052c2d4668 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/impl/search/lucene/transformer/IndexableApiDocumentTransformer.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/impl/search/lucene/transformer/IndexableApiDocumentTransformer.java @@ -52,6 +52,7 @@ import io.gravitee.apim.core.exception.TechnicalDomainException; import io.gravitee.apim.core.search.model.IndexableApi; import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.definition.model.v4.listener.ListenerType; import io.gravitee.definition.model.v4.listener.http.HttpListener; import io.gravitee.rest.api.model.search.Indexable; @@ -176,7 +177,24 @@ private boolean accept(IndexableApi indexableApi) { } private void transformV4Api(Document doc, IndexableApi api) { - var apiDefinitionV4 = api.getApi().getApiDefinitionHttpV4(); + var apiDefinitionV4 = api.getApi().getType() == ApiType.NATIVE + ? api.getApi().getApiDefinitionNativeV4() + : api.getApi().getApiDefinitionHttpV4(); + + if (api.getApi().getType() != ApiType.NATIVE) { + transformV4ApiHttpListeners(doc, api.getApi().getApiDefinitionHttpV4()); + } + + // tags + if (apiDefinitionV4.getTags() != null) { + for (String tag : apiDefinitionV4.getTags()) { + doc.add(new StringField(FIELD_TAGS, tag, Field.Store.NO)); + doc.add(new TextField(FIELD_TAGS_SPLIT, tag, Field.Store.NO)); + } + } + } + + private void transformV4ApiHttpListeners(Document doc, io.gravitee.definition.model.v4.Api apiDefinitionV4) { if (apiDefinitionV4 != null && apiDefinitionV4.getListeners() != null) { final int[] pathIndex = { 0 }; apiDefinitionV4 @@ -189,14 +207,6 @@ private void transformV4Api(Document doc, IndexableApi api) { }) .forEach(path -> appendPath(doc, pathIndex, path.getHost(), path.getPath())); } - - // tags - if (apiDefinitionV4.getTags() != null) { - for (String tag : apiDefinitionV4.getTags()) { - doc.add(new StringField(FIELD_TAGS, tag, Field.Store.NO)); - doc.add(new TextField(FIELD_TAGS_SPLIT, tag, Field.Store.NO)); - } - } } private void appendPath(final Document doc, final int[] pathIndex, final String host, final String path) { diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/ApiMapper.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/ApiMapper.java index 90c109dbf9e..e7dc858c2ec 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/ApiMapper.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/ApiMapper.java @@ -26,6 +26,7 @@ import io.gravitee.common.component.Lifecycle; import io.gravitee.definition.model.v4.ApiType; import io.gravitee.definition.model.v4.flow.Flow; +import io.gravitee.definition.model.v4.nativeapi.NativeFlow; import io.gravitee.definition.model.v4.property.Property; import io.gravitee.repository.management.model.Api; import io.gravitee.repository.management.model.ApiLifecycleState; @@ -42,6 +43,8 @@ import io.gravitee.rest.api.model.v4.api.ApiEntity; import io.gravitee.rest.api.model.v4.api.NewApiEntity; import io.gravitee.rest.api.model.v4.api.UpdateApiEntity; +import io.gravitee.rest.api.model.v4.nativeapi.NativeApiEntity; +import io.gravitee.rest.api.model.v4.nativeapi.NativePlanEntity; import io.gravitee.rest.api.model.v4.plan.PlanEntity; import io.gravitee.rest.api.service.ParameterService; import io.gravitee.rest.api.service.WorkflowService; @@ -159,6 +162,65 @@ public ApiEntity toEntity(final Api api, final PrimaryOwnerEntity primaryOwner) return apiEntity; } + public NativeApiEntity toNativeEntity(final Api api, final PrimaryOwnerEntity primaryOwner) { + NativeApiEntity apiEntity = new NativeApiEntity(); + + apiEntity.setId(api.getId()); + apiEntity.setCrossId(api.getCrossId()); + apiEntity.setName(api.getName()); + apiEntity.setApiVersion(api.getVersion()); + apiEntity.setUpdatedAt(api.getUpdatedAt()); + apiEntity.setDeployedAt(api.getDeployedAt()); + apiEntity.setCreatedAt(api.getCreatedAt()); + apiEntity.setDescription(api.getDescription()); + + if (api.getDefinition() != null) { + try { + var apiDefinition = objectMapper.readValue(api.getDefinition(), io.gravitee.definition.model.v4.nativeapi.NativeApi.class); + apiEntity.setDefinitionVersion(apiDefinition.getDefinitionVersion()); + apiEntity.setListeners(apiDefinition.getListeners()); + apiEntity.setEndpointGroups(apiDefinition.getEndpointGroups()); + apiEntity.setServices(apiDefinition.getServices()); + apiEntity.setResources(apiDefinition.getResources()); + apiEntity.setProperties(apiDefinition.getProperties()); + apiEntity.setTags(apiDefinition.getTags()); + apiEntity.setFlows(apiDefinition.getFlows()); + } catch (IOException ioe) { + log.error("Unexpected error while generating API definition", ioe); + } + } + + if (api.getType() != null) { + apiEntity.setType(ApiType.fromLabel(api.getType().getLabel())); + } + apiEntity.setGroups(api.getGroups()); + apiEntity.setDisableMembershipNotifications(api.isDisableMembershipNotifications()); + apiEntity.setReferenceType(ReferenceContext.Type.ENVIRONMENT.name()); + apiEntity.setReferenceId(api.getEnvironmentId()); + apiEntity.setCategories(categoryMapper.toCategoryKey(api.getEnvironmentId(), api.getCategories())); + apiEntity.setPicture(api.getPicture()); + apiEntity.setBackground(api.getBackground()); + apiEntity.setLabels(api.getLabels()); + + final LifecycleState state = api.getLifecycleState(); + if (state != null) { + apiEntity.setState(Lifecycle.State.valueOf(state.name())); + } + if (api.getVisibility() != null) { + apiEntity.setVisibility(io.gravitee.rest.api.model.Visibility.valueOf(api.getVisibility().toString())); + } + + final ApiLifecycleState lifecycleState = api.getApiLifecycleState(); + if (lifecycleState != null) { + apiEntity.setLifecycleState(io.gravitee.rest.api.model.api.ApiLifecycleState.valueOf(lifecycleState.name())); + } + + apiEntity.setOriginContext(ApiAdapterDecorator.toOriginContext(api)); + + apiEntity.setPrimaryOwner(primaryOwner); + return apiEntity; + } + public FederatedApiEntity federatedToEntity(final Api api, final PrimaryOwnerEntity primaryOwner) { api.setCategories(categoryMapper.toCategoryKey(api.getEnvironmentId(), api.getCategories())); return ApiAdapter.INSTANCE.toFederatedApiEntity(api, PrimaryOwnerAdapter.INSTANCE.fromRestEntity(primaryOwner)); @@ -199,6 +261,41 @@ public ApiEntity toEntity( return apiEntity; } + public NativeApiEntity toNativeEntity( + final ExecutionContext executionContext, + final Api api, + final PrimaryOwnerEntity primaryOwner, + final boolean readDatabaseFlows + ) { + NativeApiEntity apiEntity = toNativeEntity(api, primaryOwner); + + Set plans = planService.findNativePlansByApi(executionContext, api.getId()); + apiEntity.setPlans(plans); + + if (readDatabaseFlows) { + List flows = flowService.findNativeFlowByReference(FlowReferenceType.API, api.getId()); + apiEntity.setFlows(flows); + } + + apiEntity.setCategories(categoryMapper.toCategoryKey(executionContext.getEnvironmentId(), api.getCategories())); + + if ( + parameterService.findAsBoolean( + executionContext, + Key.API_REVIEW_ENABLED, + api.getEnvironmentId(), + ParameterReferenceType.ENVIRONMENT + ) + ) { + final List workflows = workflowService.findByReferenceAndType(API, api.getId(), REVIEW); + if (workflows != null && !workflows.isEmpty()) { + apiEntity.setWorkflowState(WorkflowState.valueOf(workflows.get(0).getState())); + } + } + + return apiEntity; + } + public FederatedApiEntity federatedToEntity( final ExecutionContext executionContext, final Api api, diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapper.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapper.java index 9c228706b35..1d003a7da59 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapper.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapper.java @@ -37,7 +37,10 @@ public class GenericApiMapper { public GenericApiEntity toGenericApi(final Api api, final PrimaryOwnerEntity primaryOwner) { return switch (getVersionOfDefault(api)) { - case V4 -> apiMapper.toEntity(api, primaryOwner); + case V4 -> switch (api.getType()) { + case NATIVE -> apiMapper.toNativeEntity(api, primaryOwner); + case MESSAGE, PROXY -> apiMapper.toEntity(api, primaryOwner); + }; case FEDERATED -> apiMapper.federatedToEntity(api, primaryOwner); case V1, V2 -> apiConverter.toApiEntity(api, primaryOwner); }; @@ -45,7 +48,10 @@ public GenericApiEntity toGenericApi(final Api api, final PrimaryOwnerEntity pri public GenericApiEntity toGenericApi(final ExecutionContext executionContext, final Api api, final PrimaryOwnerEntity primaryOwner) { return switch (getVersionOfDefault(api)) { - case V4 -> apiMapper.toEntity(executionContext, api, primaryOwner, true); + case V4 -> switch (api.getType()) { + case NATIVE -> apiMapper.toNativeEntity(executionContext, api, primaryOwner, true); + case MESSAGE, PROXY -> apiMapper.toEntity(executionContext, api, primaryOwner, true); + }; case FEDERATED -> apiMapper.federatedToEntity(executionContext, api, primaryOwner); case V1, V2 -> apiConverter.toApiEntity(executionContext, api, primaryOwner, true); }; diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/repository/ApiFixtures.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/repository/ApiFixtures.java index b835f035aab..65da76d4fb0 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/repository/ApiFixtures.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/fixtures/repository/ApiFixtures.java @@ -18,6 +18,7 @@ import fixtures.definition.ApiDefinitionFixtures; import io.gravitee.definition.jackson.datatype.GraviteeMapper; import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.repository.management.model.Api; import java.time.Instant; import lombok.SneakyThrows; @@ -55,6 +56,7 @@ public static Api aV2Api() { public static Api aV4Api() { return BASE .definitionVersion(DefinitionVersion.V4) + .type(ApiType.PROXY) .definition(GRAVITEE_MAPPER.writeValueAsString(ApiDefinitionFixtures.anApiV4())) .build(); } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchServiceImplTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchServiceImplTest.java index 957f4dc068d..94d1a99b3d1 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchServiceImplTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchServiceImplTest.java @@ -270,6 +270,7 @@ public void shouldFindV4GenericApiWithDefinitionVersionV4() throws TechnicalExce Api api = new Api(); api.setId(API_ID); api.setDefinitionVersion(DefinitionVersion.V4); + api.setType(ApiType.PROXY); api.setEnvironmentId("DEFAULT"); when(apiRepository.findById(API_ID)).thenReturn(Optional.of(api)); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchService_SearchTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchService_SearchTest.java index adc91935979..ce0edf957f9 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchService_SearchTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiSearchService_SearchTest.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.gravitee.common.data.domain.Page; import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.repository.management.api.ApiRepository; import io.gravitee.repository.management.api.IntegrationRepository; import io.gravitee.repository.management.api.search.ApiCriteria; @@ -246,11 +247,13 @@ public void should_return_page_of_simple_api_entity_if_ids_found() { var apiEntity1 = new Api(); apiEntity1.setId("id-3"); apiEntity1.setDefinitionVersion(DefinitionVersion.V4); + apiEntity1.setType(ApiType.PROXY); apiEntity1.setLifecycleState(LifecycleState.STARTED); var apiEntity2 = new Api(); apiEntity2.setId("id-4"); apiEntity2.setDefinitionVersion(DefinitionVersion.V4); + apiEntity2.setType(ApiType.PROXY); apiEntity2.setLifecycleState(LifecycleState.STARTED); when( @@ -328,11 +331,13 @@ public void should_get_user_api_ids_if_not_admin() { var apiEntity1 = new Api(); apiEntity1.setId("id-1"); apiEntity1.setDefinitionVersion(DefinitionVersion.V4); + apiEntity1.setType(ApiType.PROXY); apiEntity1.setLifecycleState(LifecycleState.STARTED); var apiEntity2 = new Api(); apiEntity2.setId("id-2"); apiEntity2.setDefinitionVersion(DefinitionVersion.V4); + apiEntity2.setType(ApiType.PROXY); apiEntity2.setLifecycleState(LifecycleState.STARTED); when(apiAuthorizationService.findApiIdsByUserId(eq(GraviteeContext.getExecutionContext()), eq(USER_ID), isNull(), eq(true))) @@ -472,11 +477,13 @@ public void should_return_page_of_full_api_entities_if_ids_found() { var apiEntity1 = new Api(); apiEntity1.setId("id-1"); apiEntity1.setDefinitionVersion(DefinitionVersion.V4); + apiEntity1.setType(ApiType.PROXY); apiEntity1.setLifecycleState(LifecycleState.STARTED); var apiEntity2 = new Api(); apiEntity2.setId("id-2"); apiEntity2.setDefinitionVersion(DefinitionVersion.V4); + apiEntity2.setType(ApiType.PROXY); apiEntity2.setLifecycleState(LifecycleState.STARTED); when( diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiServiceImplTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiServiceImplTest.java index 3b046028fed..9e9a46fddb9 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiServiceImplTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiServiceImplTest.java @@ -408,6 +408,7 @@ public void setUp() { api.setId(API_ID); api.setEnvironmentId(GraviteeContext.getExecutionContext().getEnvironmentId()); api.setDefinitionVersion(DefinitionVersion.V4); + api.setType(ApiType.PROXY); updatedApi = new Api(); updatedApi.setId(API_ID); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImplTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImplTest.java index 26c20da174d..843857a82c1 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImplTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImplTest.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.gravitee.definition.jackson.datatype.GraviteeMapper; import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.ApiRepository; import io.gravitee.repository.management.api.EventLatestRepository; @@ -203,6 +204,7 @@ public void setUp() { api.setName(API_NAME); api.setEnvironmentId(executionContext.getEnvironmentId()); api.setDefinitionVersion(DefinitionVersion.V4); + api.setType(ApiType.PROXY); updatedApi = new Api(api); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImpl_DeployTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImpl_DeployTest.java index 9c9a820e45e..109bec077fa 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImpl_DeployTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/impl/ApiStateServiceImpl_DeployTest.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.gravitee.definition.jackson.datatype.GraviteeMapper; import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.ApiRepository; import io.gravitee.repository.management.api.EventLatestRepository; @@ -197,6 +198,7 @@ public void setUp() { api.setName(API_NAME); api.setEnvironmentId(GraviteeContext.getExecutionContext().getEnvironmentId()); api.setDefinitionVersion(DefinitionVersion.V4); + api.setType(ApiType.PROXY); updatedApi = new Api(api); diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/ApiMapperTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/ApiMapperTest.java index da1ec8324af..659c37ab32f 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/ApiMapperTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/ApiMapperTest.java @@ -32,6 +32,10 @@ import io.gravitee.definition.model.v4.flow.execution.FlowExecution; import io.gravitee.definition.model.v4.flow.execution.FlowMode; import io.gravitee.definition.model.v4.listener.http.HttpListener; +import io.gravitee.definition.model.v4.nativeapi.NativeApiServices; +import io.gravitee.definition.model.v4.nativeapi.NativeEndpointGroup; +import io.gravitee.definition.model.v4.nativeapi.NativeFlow; +import io.gravitee.definition.model.v4.nativeapi.kafka.KafkaListener; import io.gravitee.definition.model.v4.property.Property; import io.gravitee.definition.model.v4.resource.Resource; import io.gravitee.definition.model.v4.service.ApiServices; @@ -483,4 +487,78 @@ public void shouldCreateRepositoryApiFromApiEntity() throws JsonProcessingExcept ); assertThat(api.getDefinition()).isEqualTo(objectMapper.writeValueAsString(apiDefinition)); } + + @Test + public void shouldCreateNativeEntityFromApiRepository() throws JsonProcessingException { + var apiDefinition = new io.gravitee.definition.model.v4.nativeapi.NativeApi(); + apiDefinition.setDefinitionVersion(DefinitionVersion.V4); + apiDefinition.setListeners(List.of(new KafkaListener())); + apiDefinition.setEndpointGroups(List.of(new NativeEndpointGroup())); + apiDefinition.setServices(new NativeApiServices()); + apiDefinition.setResources(List.of(new Resource())); + apiDefinition.setProperties(List.of(new Property("key", "value"))); + apiDefinition.setTags(Set.of("tag")); + apiDefinition.setFlows(List.of(new NativeFlow(), new NativeFlow())); + + Api api = new Api(); + api.setId("id"); + api.setCrossId("crossId"); + api.setType(ApiType.NATIVE); + api.setName("name"); + api.setVersion("version"); + api.setUpdatedAt(new Date()); + api.setDeployedAt(new Date()); + api.setCreatedAt(new Date()); + api.setDescription("description"); + api.setGroups(Set.of("group1")); + api.setEnvironmentId("environmentId"); + api.setCategories(Set.of("category")); + api.setPicture("picture"); + api.setBackground("background"); + api.setLabels(List.of("label")); + api.setLifecycleState(LifecycleState.STARTED); + api.setVisibility(Visibility.PUBLIC); + api.setApiLifecycleState(ApiLifecycleState.CREATED); + + api.setDefinition(objectMapper.writeValueAsString(apiDefinition)); + + when(categoryMapper.toCategoryKey(any(), eq(api.getCategories()))).thenReturn(api.getCategories()); + + var nativeEntity = apiMapper.toNativeEntity(api, new PrimaryOwnerEntity()); + + assertThat(nativeEntity.getId()).isEqualTo("id"); + assertThat(nativeEntity.getCrossId()).isEqualTo("crossId"); + assertThat(nativeEntity.getType()).isEqualTo(ApiType.NATIVE); + assertThat(nativeEntity.getName()).isEqualTo("name"); + assertThat(nativeEntity.getApiVersion()).isEqualTo("version"); + assertThat(nativeEntity.getUpdatedAt()).isNotNull(); + assertThat(nativeEntity.getDeployedAt()).isNotNull(); + assertThat(nativeEntity.getCreatedAt()).isNotNull(); + assertThat(nativeEntity.getDescription()).isEqualTo("description"); + assertThat(nativeEntity.getGroups().size()).isEqualTo(1); + assertThat(nativeEntity.getReferenceType()).isEqualTo(ReferenceContext.Type.ENVIRONMENT.name()); + assertThat(nativeEntity.getReferenceId()).isEqualTo("environmentId"); + assertThat(nativeEntity.getCategories().size()).isEqualTo(1); + assertThat(nativeEntity.getPicture()).isEqualTo("picture"); + assertThat(nativeEntity.getBackground()).isEqualTo("background"); + assertThat(nativeEntity.getLabels().size()).isEqualTo(1); + assertThat(nativeEntity.getLifecycleState()).isEqualTo(io.gravitee.rest.api.model.api.ApiLifecycleState.CREATED); + assertThat(nativeEntity.getState()).isEqualTo(Lifecycle.State.STARTED); + assertThat(nativeEntity.getVisibility()).isEqualTo(io.gravitee.rest.api.model.Visibility.PUBLIC); + + assertThat(nativeEntity.getDefinitionVersion()).isEqualTo(DefinitionVersion.V4); + assertThat(nativeEntity.getListeners()).isNotNull(); + assertThat(nativeEntity.getListeners().size()).isEqualTo(1); + assertThat(nativeEntity.getEndpointGroups()).isNotNull(); + assertThat(nativeEntity.getEndpointGroups().size()).isEqualTo(1); + assertThat(nativeEntity.getServices()).isNotNull(); + assertThat(nativeEntity.getResources()).isNotNull(); + assertThat(nativeEntity.getResources().size()).isEqualTo(1); + assertThat(nativeEntity.getProperties()).isNotNull(); + assertThat(nativeEntity.getProperties().size()).isEqualTo(1); + assertThat(nativeEntity.getTags()).isNotNull(); + assertThat(nativeEntity.getTags().size()).isEqualTo(1); + assertThat(nativeEntity.getFlows()).isNotNull(); + assertThat(nativeEntity.getFlows().size()).isEqualTo(2); + } } diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapperTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapperTest.java index b423e9bc7b4..c72366ec8d7 100644 --- a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapperTest.java +++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/test/java/io/gravitee/rest/api/service/v4/mapper/GenericApiMapperTest.java @@ -18,6 +18,7 @@ import static org.mockito.Mockito.verify; import io.gravitee.definition.model.DefinitionVersion; +import io.gravitee.definition.model.v4.ApiType; import io.gravitee.repository.management.model.Api; import io.gravitee.rest.api.service.converter.ApiConverter; import org.junit.Before; @@ -47,14 +48,25 @@ public void before() { } @Test - public void shouldCallV4ApiMapperWhenDefinitionVersionV4() { + public void shouldCallHttpV4ApiMapperWhenDefinitionVersionV4AndNotNative() { Api api = new Api(); api.setDefinitionVersion(DefinitionVersion.V4); + api.setType(ApiType.PROXY); genericApiMapper.toGenericApi(api, null); verify(apiMapper).toEntity(api, null); } + @Test + public void shouldCallNativeV4ApiMapperWhenDefinitionVersionV4AndNative() { + Api api = new Api(); + api.setDefinitionVersion(DefinitionVersion.V4); + api.setType(ApiType.NATIVE); + + genericApiMapper.toGenericApi(api, null); + verify(apiMapper).toNativeEntity(api, null); + } + @Test public void shouldCallV2ApiMapperWhenV2DefinitionVersion() { Api api = new Api();