Skip to content

Commit

Permalink
refactor(rest-api-flow): Added flow id to the v2 management model and…
Browse files Browse the repository at this point in the history
… implement flow save that will handle concurrency
  • Loading branch information
wbabyte committed Mar 18, 2024
1 parent 75ad6c8 commit bffe4a9
Show file tree
Hide file tree
Showing 26 changed files with 411 additions and 98 deletions.
Expand Up @@ -46,6 +46,8 @@
@With
public class Flow implements Serializable {

private String id;

private String name;

@Builder.Default
Expand Down
Expand Up @@ -138,6 +138,7 @@ describe('API - V4 - Import - Gravitee Definition - Only API -', () => {
expect(apiV4).toBeTruthy();
expect(apiV4.id).toStrictEqual(importedApi.id);
expect(apiV4.flows).toHaveLength(1);
apiToImport.api.flows[0].id = apiV4.flows[0].id;
expect(apiV4.flows).toEqual(apiToImport.api.flows);
expect(apiV4.tags).toEqual(apiToImport.api.tags);
expect(apiV4.services).toEqual(apiToImport.api.services);
Expand Down
@@ -1,4 +1,5 @@
// Copyright 2015 The gRPC Authors
//
// 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.
Expand All @@ -11,6 +12,8 @@
// 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.
//

syntax = "proto3";

option java_multiple_files = true;
Expand Down
@@ -1,4 +1,5 @@
// Copyright 2015 The gRPC Authors
//
// 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.
Expand All @@ -11,6 +12,8 @@
// 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.
//

syntax = "proto3";

option java_multiple_files = true;
Expand Down
Expand Up @@ -15,13 +15,13 @@
*/
package io.gravitee.repository.management.api;/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
Expand All @@ -32,6 +32,7 @@
import io.gravitee.repository.exceptions.TechnicalException;
import io.gravitee.repository.management.model.flow.Flow;
import io.gravitee.repository.management.model.flow.FlowReferenceType;
import java.util.Collection;
import java.util.List;

/**
Expand All @@ -42,4 +43,6 @@ public interface FlowRepository extends CrudRepository<Flow, String> {
List<Flow> findByReference(FlowReferenceType referenceType, String referenceId) throws TechnicalException;

void deleteByReference(FlowReferenceType referenceType, String referenceId) throws TechnicalException;

void deleteAllById(Collection<String> ids) throws TechnicalException;
}
Expand Up @@ -36,6 +36,7 @@
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -454,20 +455,36 @@ public void deleteByReference(FlowReferenceType referenceType, String referenceI
referenceType.name()
);

if (!flows.isEmpty()) {
List<String> flowIds = flows.stream().map(Flow::getId).collect(Collectors.toList());
String buildInClause = getOrm().buildInClause(flowIds);
String[] ids = flowIds.toArray(new String[0]);
jdbcTemplate.update("delete from " + tableName + " where id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_STEPS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_SELECTORS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_SELECTOR_HTTP_METHODS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_SELECTOR_CHANNEL_OPERATIONS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_SELECTOR_CHANNEL_ENTRYPOINTS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_TAGS + " where flow_id in (" + buildInClause + ")", ids);
this.deleteAllById(flows.stream().map(Flow::getId).collect(Collectors.toList()));
} catch (final Exception ex) {
LOGGER.error("Failed to delete flows by reference:", ex);
throw new TechnicalException("Failed to delete flows by reference", ex);
}
}

@Override
public void deleteAllById(Collection<String> ids) throws TechnicalException {
LOGGER.debug("JdbcFlowRepository.deleteByIds({})", ids);
try {
if (!ids.isEmpty()) {
String buildInClause = getOrm().buildInClause(ids);
String[] flowIds = ids.toArray(new String[0]);
jdbcTemplate.update("delete from " + tableName + " where id in (" + buildInClause + ")", flowIds);
jdbcTemplate.update("delete from " + FLOW_STEPS + " where flow_id in (" + buildInClause + ")", flowIds);
jdbcTemplate.update("delete from " + FLOW_SELECTORS + " where flow_id in (" + buildInClause + ")", flowIds);
jdbcTemplate.update("delete from " + FLOW_SELECTOR_HTTP_METHODS + " where flow_id in (" + buildInClause + ")", flowIds);
jdbcTemplate.update(
"delete from " + FLOW_SELECTOR_CHANNEL_OPERATIONS + " where flow_id in (" + buildInClause + ")",
flowIds
);
jdbcTemplate.update(
"delete from " + FLOW_SELECTOR_CHANNEL_ENTRYPOINTS + " where flow_id in (" + buildInClause + ")",
flowIds
);
jdbcTemplate.update("delete from " + FLOW_TAGS + " where flow_id in (" + buildInClause + ")", flowIds);
// deprecated data
jdbcTemplate.update("delete from " + FLOW_METHODS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_CONSUMERS + " where flow_id in (" + buildInClause + ")", ids);
jdbcTemplate.update("delete from " + FLOW_METHODS + " where flow_id in (" + buildInClause + ")", flowIds);
jdbcTemplate.update("delete from " + FLOW_CONSUMERS + " where flow_id in (" + buildInClause + ")", flowIds);
}
} catch (final Exception ex) {
LOGGER.error("Failed to delete flows by reference:", ex);
Expand Down
Expand Up @@ -22,6 +22,7 @@
import io.gravitee.repository.mongodb.management.internal.flow.FlowMongoRepository;
import io.gravitee.repository.mongodb.management.internal.model.FlowMongo;
import io.gravitee.repository.mongodb.management.mapper.GraviteeMapper;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -97,6 +98,13 @@ public void deleteByReference(FlowReferenceType referenceType, String referenceI
internalRepository.deleteAll(internalRepository.findAll(referenceType.name(), referenceId));
}

@Override
public void deleteAllById(Collection<String> ids) {
logger.debug("Delete flows [{}]", ids);
internalRepository.deleteAllById(ids);
logger.debug("Delete flows [{}] - Done", ids);
}

@Override
public Set<Flow> findAll() throws TechnicalException {
return internalRepository.findAll().stream().map(this::map).collect(Collectors.toSet());
Expand Down
Expand Up @@ -31,6 +31,8 @@
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;

/**
Expand Down Expand Up @@ -215,4 +217,15 @@ public void shouldDeleteByReference() throws TechnicalException {

assertEquals(0, flowRepository.findByReference(FlowReferenceType.ORGANIZATION, "orga-deleted").size());
}

@Test
public void shouldDeleteByIds() throws TechnicalException {
List<Flow> flows = flowRepository.findByReference(FlowReferenceType.ORGANIZATION, "orga-deleted");
assertEquals(2, flows.size());
Set<String> ids = flows.stream().map(Flow::getId).collect(Collectors.toSet());

flowRepository.deleteAllById(ids);

assertEquals(0, flowRepository.findByReference(FlowReferenceType.ORGANIZATION, "orga-deleted").size());
}
}
Expand Up @@ -31,6 +31,7 @@
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;

/**
Expand Down Expand Up @@ -246,4 +247,15 @@ public void shouldDeleteByReference() throws TechnicalException {

assertEquals(0, flowRepository.findByReference(FlowReferenceType.ORGANIZATION, "orga-v4-deleted").size());
}

@Test
public void shouldDeleteByIds() throws TechnicalException {
List<Flow> flows = flowRepository.findByReference(FlowReferenceType.ORGANIZATION, "orga-v4-deleted");
assertEquals(2, flows.size());
Set<String> ids = flows.stream().map(Flow::getId).collect(Collectors.toSet());

flowRepository.deleteAllById(ids);

assertEquals(0, flowRepository.findByReference(FlowReferenceType.ORGANIZATION, "orga-v4-deleted").size());
}
}
Expand Up @@ -2846,6 +2846,10 @@ components:
FlowV4:
type: object
properties:
id:
type: string
description: Flow's uuid.
example: 4e6abbd2-c0c6-462d-be9e-6371209af34b
name:
type: string
description: Flow's name.
Expand Down
Expand Up @@ -19,6 +19,7 @@
import java.util.List;

public interface FlowCrudService {
List<Flow> saveApiFlows(String apiId, List<Flow> flows);
List<Flow> savePlanFlows(String planId, List<Flow> flows);

List<Flow> saveApiFlows(String apiId, List<Flow> flows);
}
Expand Up @@ -29,6 +29,7 @@
import io.gravitee.rest.api.service.common.UuidString;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;

@Mapper(imports = { UuidString.class, TimeProvider.class })
Expand All @@ -40,6 +41,10 @@ public interface FlowAdapter {
@Mapping(target = "updatedAt", expression = "java(java.util.Date.from(TimeProvider.instantNow()))")
Flow toRepository(io.gravitee.definition.model.v4.flow.Flow source, FlowReferenceType referenceType, String referenceId, int order);

@Mapping(target = "id", ignore = true)
@Mapping(target = "updatedAt", expression = "java(java.util.Date.from(TimeProvider.instantNow()))")
Flow toRepositoryUpdate(@MappingTarget Flow repository, io.gravitee.definition.model.v4.flow.Flow source, int order);

@Mapping(target = "id", expression = "java(UuidString.generateRandom())")
@Mapping(target = "createdAt", expression = "java(java.util.Date.from(TimeProvider.instantNow()))")
@Mapping(target = "updatedAt", expression = "java(java.util.Date.from(TimeProvider.instantNow()))")
Expand Down Expand Up @@ -76,11 +81,14 @@ default Selector toModel(FlowSelector source) {
}

FlowHttpSelector toRepository(HttpSelector source);

HttpSelector toModel(FlowHttpSelector source);

FlowChannelSelector toRepository(ChannelSelector source);

ChannelSelector toModel(FlowChannelSelector source);

FlowConditionSelector toRepository(ConditionSelector source);

ConditionSelector toModel(FlowConditionSelector source);
}
Expand Up @@ -25,6 +25,12 @@
import io.gravitee.rest.api.service.impl.TransactionalService;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
Expand All @@ -40,33 +46,52 @@ public FlowCrudServiceImpl(@Lazy FlowRepository flowRepository) {
}

@Override
public List<Flow> saveApiFlows(String apiId, List<Flow> flows) {
return save(FlowReferenceType.API, apiId, flows);
public List<Flow> savePlanFlows(String planId, List<Flow> flows) {
return save(FlowReferenceType.PLAN, planId, flows);
}

@Override
public List<Flow> savePlanFlows(String planId, List<Flow> flows) {
return save(FlowReferenceType.PLAN, planId, flows);
public List<Flow> saveApiFlows(String apiId, List<Flow> flows) {
return save(FlowReferenceType.API, apiId, flows);
}

private List<Flow> save(FlowReferenceType referenceType, String referenceId, List<Flow> flows) {
private List<Flow> save(FlowReferenceType flowReferenceType, String referenceId, List<Flow> flows) {
try {
log.debug("Save flows for reference {} {}", referenceType, referenceId);
flowRepository.deleteByReference(referenceType, referenceId);
if (flows == null) {
log.debug("Save flows for reference {},{}", flowReferenceType, flowReferenceType);
if (flows == null || flows.isEmpty()) {
flowRepository.deleteByReference(flowReferenceType, referenceId);
return List.of();
}
Map<String, io.gravitee.repository.management.model.flow.Flow> dbFlowsById = flowRepository
.findByReference(flowReferenceType, referenceId)
.stream()
.collect(Collectors.toMap(io.gravitee.repository.management.model.flow.Flow::getId, Function.identity()));

Set<String> flowIdsToSave = flows.stream().map(Flow::getId).filter(Objects::nonNull).collect(Collectors.toSet());

Set<String> flowIdsToDelete = dbFlowsById
.keySet()
.stream()
.filter(Predicate.not(flowIdsToSave::contains))
.collect(Collectors.toSet());
if (!flowIdsToDelete.isEmpty()) {
flowRepository.deleteAllById(flowIdsToDelete);
}

List<Flow> createdFlows = new ArrayList<>();
List<Flow> savedFlows = new ArrayList<>();
io.gravitee.repository.management.model.flow.Flow dbFlow;
for (int order = 0; order < flows.size(); ++order) {
io.gravitee.repository.management.model.flow.Flow createdFlow = flowRepository.create(
FlowAdapter.INSTANCE.toRepository(flows.get(order), referenceType, referenceId, order)
);
createdFlows.add(FlowAdapter.INSTANCE.toFlowV4(createdFlow));
Flow flow = flows.get(order);
if (flow.getId() == null || !dbFlowsById.containsKey(flow.getId())) {
dbFlow = flowRepository.create(FlowAdapter.INSTANCE.toRepository(flow, flowReferenceType, referenceId, order));
} else {
dbFlow = flowRepository.update(FlowAdapter.INSTANCE.toRepositoryUpdate(dbFlowsById.get(flow.getId()), flow, order));
}
savedFlows.add(FlowAdapter.INSTANCE.toFlowV4(dbFlow));
}
return createdFlows;
return savedFlows;
} catch (TechnicalException ex) {
final String error = "An error occurs while trying to save flows for " + referenceType + ": " + referenceId;
final String error = "An error occurs while trying to save flows for " + flowReferenceType + ": " + referenceId;
log.error(error, ex);
throw new TechnicalDomainException(error, ex);
}
Expand Down
Expand Up @@ -28,6 +28,4 @@ public interface FlowService {
String getPlatformFlowSchemaForm(final ExecutionContext executionContext);

List<Flow> findByReference(final FlowReferenceType flowReferenceType, final String referenceId);

List<Flow> save(final FlowReferenceType flowReferenceType, final String referenceId, final List<Flow> flows);
}

0 comments on commit bffe4a9

Please sign in to comment.