Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.gravitee.definition.model.v4.flow.step.Step;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
Expand All @@ -44,11 +45,13 @@
@With
public class Flow extends AbstractFlow {

@Builder.Default
@Valid
private List<Step> request;
private List<Step> request = new ArrayList<>();

@Builder.Default
@Valid
private List<Step> response;
private List<Step> response = new ArrayList<>();

@Valid
private List<Step> subscribe;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public void should_not_import_when_no_definition_permission() {
@SneakyThrows
public void should_throw_invalid_paths_exception() {
// Given
when(oaiDomainService.convert(any(), any(), any())).thenReturn(
when(oaiDomainService.convert(any(), any(), any(), any(), any())).thenReturn(
ImportDefinition.builder()
.apiExport(
ApiExport.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,11 @@
import io.gravitee.rest.api.model.ImportSwaggerDescriptorEntity;

public interface OAIDomainService {
ImportDefinition convert(String organizationId, String environmentId, ImportSwaggerDescriptorEntity importSwaggerDescriptor);
ImportDefinition convert(
String organizationId,
String environmentId,
ImportSwaggerDescriptorEntity importSwaggerDescriptor,
boolean withDocumentation,
boolean withOASValidationPolicy
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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.cockpit;

import io.gravitee.apim.core.DomainService;
import io.gravitee.apim.core.api.domain_service.ApiStateDomainService;
import io.gravitee.apim.core.api.domain_service.UpdateApiDomainService;
import io.gravitee.apim.core.api.model.Api;
import io.gravitee.apim.core.audit.model.AuditInfo;
import io.gravitee.definition.model.v4.plan.Plan;
import io.gravitee.definition.model.v4.plan.PlanMode;
import io.gravitee.definition.model.v4.plan.PlanSecurity;
import io.gravitee.definition.model.v4.plan.PlanStatus;
import io.gravitee.rest.api.model.ImportSwaggerDescriptorEntity;
import io.gravitee.rest.api.service.common.UuidString;
import java.util.List;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@DomainService
public class DeployModelToApiDomainService {

private final UpdateApiDomainService updateApiDomainService;
private final ApiStateDomainService apiStateDomainService;

public DeployModelToApiDomainService(UpdateApiDomainService updateApiDomainService, ApiStateDomainService apiStateDomainService) {
this.updateApiDomainService = updateApiDomainService;
this.apiStateDomainService = apiStateDomainService;
}

public ImportSwaggerDescriptorEntity configure(Mode mode, String swaggerDefinition) {
var importSwaggerDescriptor = ImportSwaggerDescriptorEntity.builder().payload(swaggerDefinition);

importSwaggerDescriptor.withDocumentation(true);

if (mode == Mode.MOCKED || mode == Mode.PUBLISHED) {
importSwaggerDescriptor.withPolicyPaths(true);
importSwaggerDescriptor.withPolicies(List.of("mock"));
}

return importSwaggerDescriptor.build();
}

public Api manageApiState(Api api, AuditInfo audit, Mode mode) {
// API should be published
if (mode == Mode.PUBLISHED) {
log.debug("Published v4 API [id: {} / crossId: {}].", api.getId(), api.getCrossId());

api.setVisibility(Api.Visibility.PUBLIC);
api.setApiLifecycleState(Api.ApiLifecycleState.PUBLISHED);
}

var updatedApi = updateApiDomainService.updateV4(api, audit);

// API should be started
if (mode == Mode.MOCKED || mode == Mode.PUBLISHED) {
log.debug("Started v4 API [id: {} / crossId: {}].", api.getId(), api.getCrossId());
apiStateDomainService.start(api, audit);
}

// Force API deployment if out of sync
if (!apiStateDomainService.isSynchronized(api, audit)) {
apiStateDomainService.deploy(api, "Managed by Gravitee Cloud", audit);
}

return updatedApi;
}

public Plan createDefaultPlan() {
return Plan.builder()
.id(UuidString.generateRandom())
.name("Default plan")
.mode(PlanMode.STANDARD)
.status(PlanStatus.PUBLISHED)
.security(PlanSecurity.builder().type("key-less").build())
.build();
}

public enum Mode {
DOCUMENTED,
MOCKED,
PUBLISHED,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,12 @@
*/
package io.gravitee.apim.core.api.use_case;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gravitee.apim.core.UseCase;
import io.gravitee.apim.core.api.domain_service.ImportDefinitionCreateDomainService;
import io.gravitee.apim.core.api.domain_service.OAIDomainService;
import io.gravitee.apim.core.api.exception.InvalidImportWithOASValidationPolicyException;
import io.gravitee.apim.core.api.model.ApiWithFlows;
import io.gravitee.apim.core.api.model.import_definition.ImportDefinition;
import io.gravitee.apim.core.audit.model.AuditInfo;
import io.gravitee.apim.core.documentation.model.Page;
import io.gravitee.apim.core.group.model.Group;
import io.gravitee.apim.core.group.query_service.GroupQueryService;
import io.gravitee.apim.core.plugin.crud_service.PolicyPluginCrudService;
import io.gravitee.apim.core.plugin.domain_service.EndpointConnectorPluginDomainService;
import io.gravitee.apim.core.plugin.model.PolicyPlugin;
import io.gravitee.apim.core.tag.model.Tag;
import io.gravitee.apim.core.tag.query_service.TagQueryService;
import io.gravitee.definition.model.flow.Operator;
import io.gravitee.definition.model.v4.endpointgroup.EndpointGroup;
import io.gravitee.definition.model.v4.flow.Flow;
import io.gravitee.definition.model.v4.flow.selector.HttpSelector;
import io.gravitee.definition.model.v4.flow.step.Step;
import io.gravitee.definition.model.v4.resource.Resource;
import io.gravitee.rest.api.model.ImportSwaggerDescriptorEntity;
import io.gravitee.rest.api.service.exceptions.TechnicalManagementException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Builder;

@UseCase
Expand All @@ -67,191 +43,32 @@ public record Input(
public record Output(ApiWithFlows apiWithFlows) {}

private final OAIDomainService oaiDomainService;
private final GroupQueryService groupQueryService;
private final TagQueryService tagsQueryService;
private final EndpointConnectorPluginDomainService endpointConnectorPluginService;
private final ImportDefinitionCreateDomainService importDefinitionCreateDomainService;
private final PolicyPluginCrudService policyPluginCrudService;

public OAIToImportApiUseCase(
OAIDomainService oaiDomainService,
GroupQueryService groupQueryService,
TagQueryService tagsQueryService,
EndpointConnectorPluginDomainService endpointConnectorPluginService,
ImportDefinitionCreateDomainService importDefinitionCreateDomainService,
PolicyPluginCrudService policyPluginCrudService
ImportDefinitionCreateDomainService importDefinitionCreateDomainService
) {
this.oaiDomainService = oaiDomainService;
this.groupQueryService = groupQueryService;
this.tagsQueryService = tagsQueryService;
this.endpointConnectorPluginService = endpointConnectorPluginService;
this.importDefinitionCreateDomainService = importDefinitionCreateDomainService;
this.policyPluginCrudService = policyPluginCrudService;
}

public Output execute(Input input) {
var organizationId = input.auditInfo.organizationId();
var environmentId = input.auditInfo.environmentId();
var importDefinition = oaiDomainService.convert(organizationId, environmentId, input.importSwaggerDescriptor);
var importDefinition = oaiDomainService.convert(
organizationId,
environmentId,
input.importSwaggerDescriptor,
input.withDocumentation(),
input.withOASValidationPolicy()
);

if (importDefinition != null) {
var importWithEndpointGroupsSharedConfiguration = addEndpointGroupSharedConfiguration(importDefinition);
var importWithGroups = replaceGroupNamesWithIds(environmentId, importWithEndpointGroupsSharedConfiguration);
var importWithTags = replaceTagsNamesWithIds(organizationId, importWithGroups);
var importWithDocumentation = addOAIDocumentation(
input.withDocumentation(),
input.importSwaggerDescriptor.getPayload(),
importWithTags
);
var importWithOASValidationPolicy = addOASValidationPolicy(
input.withOASValidationPolicy(),
input.importSwaggerDescriptor.getPayload(),
importWithDocumentation
);

final ApiWithFlows apiWithFlows = importDefinitionCreateDomainService.create(input.auditInfo, importWithOASValidationPolicy);
final ApiWithFlows apiWithFlows = importDefinitionCreateDomainService.create(input.auditInfo, importDefinition);
return new Output(apiWithFlows);
}

return null;
}

private ImportDefinition addEndpointGroupSharedConfiguration(ImportDefinition importDefinition) {
var sharedConfiguration = endpointConnectorPluginService.getDefaultSharedConfiguration("http-proxy");
var endpointGroups = importDefinition.getApiExport().getEndpointGroups();
if (endpointGroups == null || endpointGroups.isEmpty()) {
return importDefinition;
}

return importDefinition
.toBuilder()
.apiExport(
importDefinition
.getApiExport()
.toBuilder()
.endpointGroups(
endpointGroups
.stream()
.peek(endpointGroup -> endpointGroup.setSharedConfiguration(sharedConfiguration))
.toList()
)
.build()
)
.build();
}

private ImportDefinition replaceGroupNamesWithIds(String environmentId, ImportDefinition importDefinition) {
var groups = importDefinition.getApiExport().getGroups();
if (groups == null || groups.isEmpty()) {
return importDefinition;
}

return importDefinition
.toBuilder()
.apiExport(
importDefinition
.getApiExport()
.toBuilder()
.groups(
groups
.stream()
.flatMap(group -> groupQueryService.findByNames(environmentId, Set.of(group)).stream())
.map(Group::getId)
.collect(Collectors.toSet())
)
.build()
)
.build();
}

private ImportDefinition replaceTagsNamesWithIds(String organizationId, ImportDefinition importDefinition) {
if (
importDefinition.getApiExport() == null ||
importDefinition.getApiExport().getTags() == null ||
importDefinition.getApiExport().getTags().isEmpty()
) {
return importDefinition;
}

return importDefinition
.toBuilder()
.apiExport(
importDefinition
.getApiExport()
.toBuilder()
.tags(
importDefinition
.getApiExport()
.getTags()
.stream()
.flatMap(group -> tagsQueryService.findByName(organizationId, group).stream())
.map(Tag::getId)
.collect(Collectors.toSet())
)
.build()
)
.build();
}

private ImportDefinition addOAIDocumentation(boolean withDocumentation, String payload, ImportDefinition importWithTags) {
if (!withDocumentation) {
return importWithTags;
}
var page = io.gravitee.apim.core.documentation.model.Page.builder()
.name(DEFAULT_IMPORT_PAGE_NAME)
.type(io.gravitee.apim.core.documentation.model.Page.Type.SWAGGER)
.homepage(false)
.content(payload)
.referenceType(io.gravitee.apim.core.documentation.model.Page.ReferenceType.API)
.published(true)
.visibility(Page.Visibility.PUBLIC)
.build();

return importWithTags.toBuilder().pages(List.of(page)).build();
}

private ImportDefinition addOASValidationPolicy(boolean withOASValidationPolicy, String payload, ImportDefinition importDefinition) {
if (!withOASValidationPolicy) {
return importDefinition;
}

PolicyPlugin oasValidationPolicy = policyPluginCrudService
.get("oas-validation")
.orElseThrow(() -> new InvalidImportWithOASValidationPolicyException("Policy not found"));

try {
// Add Content provider inline resource to API resources
Resource resource = Resource.builder()
.name("OpenAPI Specification")
.type("content-provider-inline-resource")
.configuration(new ObjectMapper().writeValueAsString(new LinkedHashMap<>(Map.of("content", payload))))
.build();
importDefinition.getApiExport().setResources(List.of(resource));

// Add Flow with OAS validation policy to API flows
var step = Step.builder()
.policy(oasValidationPolicy.getId())
.name(oasValidationPolicy.getName())
.configuration(new ObjectMapper().writeValueAsString(new LinkedHashMap<>(Map.of("resourceName", "OpenAPI Specification"))))
.build();

var httpSelector = HttpSelector.builder().path("/").pathOperator(Operator.STARTS_WITH).build();

var flow = Flow.builder()
.name("OpenAPI Specification Validation")
.selectors(List.of(httpSelector))
.request(List.of(step))
.response(List.of(step))
.build();

List<Flow> apiExportFlows = (List<Flow>) importDefinition.getApiExport().getFlows();
apiExportFlows.addFirst(flow);

importDefinition.getApiExport().setFlows(apiExportFlows);

return importDefinition;
} catch (JsonProcessingException e) {
throw new TechnicalManagementException("Error while serializing OpenAPI Specification", e);
}
}
}
Loading