package com.dbg.cloud.acheron.pluginconfig.endpoints;
import com.dbg.cloud.acheron.adminendpoints.AdminEndpoint;
import com.dbg.cloud.acheron.exception.TechnicalException;
import com.dbg.cloud.acheron.exception.ValidationException;
import com.dbg.cloud.acheron.pluginconfig.PluginConfig;
import com.dbg.cloud.acheron.pluginconfig.service.PluginConfigService;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/admin/plugin-configs")
@AllArgsConstructor
@Slf4j
final class PluginConfigController implements AdminEndpoint {
private final PluginConfigService pluginConfigService;
@RequestMapping(value = "", method = RequestMethod.GET)
public List<PluginConfigTO> readPlugins() {
try {
final List<PluginConfig> pluginConfigList = pluginConfigService.getAllPluginConfigs();
return pluginConfigList.stream()
.map(pluginConfig -> new PluginConfigTO(pluginConfig))
.collect(Collectors.toList());
} catch (final TechnicalException e) {
throw new InternalServerError();
}
}
@RequestMapping(value = "/{pluginConfigId}", method = RequestMethod.GET)
public ResponseEntity<?> readPlugin(final @PathVariable String pluginConfigId) {
try {
final UUID uuidPluginConfigId =
parseUUID(pluginConfigId).orElseThrow(() -> new PluginConfigNotFoundException(pluginConfigId));
final Optional<PluginConfig> optionalPlugin = pluginConfigService.getPluginConfig(uuidPluginConfigId);
return ResponseEntity.ok(new PluginConfigTO(
optionalPlugin.orElseThrow(() -> new PluginConfigNotFoundException(pluginConfigId))));
} catch (final TechnicalException e) {
throw new InternalServerError();
}
}
@RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<?> addPluginConfig(
final @JsonView(View.Create.class) @RequestBody PluginConfigTO pluginConfig) {
if (!validatePluginConfigForCreate(pluginConfig)) {
return ResponseEntity.badRequest().build();
}
try {
return ResponseEntity
.status(HttpStatus.CREATED)
.body(new PluginConfigTO(
pluginConfigService.addNewPluginConfig(new PluginConfig.ForCreation(
pluginConfig.getName(),
pluginConfig.getRouteId(),
pluginConfig.getHttpMethods(),
pluginConfig.getConsumerId() != null ?
UUID.fromString(pluginConfig.getConsumerId()) : null,
pluginConfig.safeGetConfig(),
pluginConfig.getEnabled() != null ? pluginConfig.getEnabled() : true))));
} catch (final ValidationException e) {
return ResponseEntity.badRequest().build();
} catch (final TechnicalException e) {
throw new InternalServerError();
}
}
@RequestMapping(value = "/{pluginConfigId}", method = RequestMethod.PATCH)
public ResponseEntity<?> mergePluginConfig(final @PathVariable String pluginConfigId,
final @JsonView(View.Merge.class) @RequestBody PluginConfigTO
pluginConfig) {
try {
// 404 if the plugin config does not exist
final UUID uuid =
parseUUID(pluginConfigId).orElseThrow(() -> new PluginConfigNotFoundException(pluginConfigId));
pluginConfigService.getPluginConfig(uuid).orElseThrow(
() -> new PluginConfigNotFoundException(pluginConfig.getId()));
// Validate
if (!validatePluginConfigForMerge(pluginConfig, pluginConfigId)) {
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(
new PluginConfigTO(
pluginConfigService.mergePluginConfig(
toPluginConfig(pluginConfig))));
} catch (final ValidationException e) {
return ResponseEntity.badRequest().build();
} catch (final TechnicalException e) {
throw new InternalServerError();
}
}
@RequestMapping(value = "/{pluginConfigId}", method = RequestMethod.PUT)
public ResponseEntity<?> replacePluginConfig(final @PathVariable String pluginConfigId,
final @JsonView(View.Replace.class) @RequestBody PluginConfigTO
pluginConfig) {
try {
// 404 if the plugin config does not exist
final UUID uuid = parseUUID(pluginConfigId).get();
pluginConfigService.getPluginConfig(uuid).orElseThrow(
() -> new PluginConfigNotFoundException(pluginConfig.getId()));
// Validate
if (!validatePluginConfigForReplace(pluginConfig, pluginConfigId)) {
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(
new PluginConfigTO(
pluginConfigService.replacePluginConfig(
toPluginConfig(pluginConfig))));
} catch (final ValidationException e) {
return ResponseEntity.badRequest().build();
} catch (final TechnicalException e) {
throw new InternalServerError();
}
}
@RequestMapping(value = "/{pluginConfigId}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteConsumer(final @PathVariable String pluginConfigId) {
try {
// 404 if the plugin config does not exist
final UUID uuidPluginConfigId =
parseUUID(pluginConfigId).orElseThrow(() -> new PluginConfigNotFoundException(pluginConfigId));
pluginConfigService.deletePluginConfig(uuidPluginConfigId);
return ResponseEntity.noContent().build();
} catch (final TechnicalException e) {
throw new InternalServerError();
}
}
private boolean validatePluginConfigForCreate(final PluginConfigTO pluginConfig) {
// consumer id is either null or a UUID
return pluginConfig.getConsumerId() == null || parseUUID(pluginConfig.getConsumerId()).isPresent();
}
private boolean validatePluginConfigForReplace(final PluginConfigTO pluginConfig, final String id) {
return parseUUID(id).isPresent() && // id is a UUID (and not null)
// object id is either not present OR equal to id
(pluginConfig.getId() == null || id.equals(pluginConfig.getId())) &&
// whatever validation for create says
validatePluginConfigForCreate(pluginConfig);
}
private boolean validatePluginConfigForMerge(final PluginConfigTO pluginConfig, final String id) {
return parseUUID(id).isPresent() && // id is a UUID (and not null)
// object id is either not present OR equal to id
(pluginConfig.getId() == null || id.equals(pluginConfig.getId())) &&
// if consumer id is present, it's a UUID
(pluginConfig.getConsumerId() == null || parseUUID(pluginConfig.getConsumerId()).isPresent());
}
private Optional<UUID> parseUUID(final String candidateUUID) {
UUID uuid = null;
try {
uuid = UUID.fromString(candidateUUID);
} catch (Exception e) {
log.info("Passed id is not a UUID {}", candidateUUID);
}
return Optional.ofNullable(uuid);
}
private PluginConfig toPluginConfig(final PluginConfigTO pluginConfigTO) {
return new PluginConfig.Default(
UUID.fromString(pluginConfigTO.getId()),
pluginConfigTO.getName(),
pluginConfigTO.getRouteId(),
pluginConfigTO.getHttpMethods(),
pluginConfigTO.getConsumerId() != null ? // being null is a legitimate case
UUID.fromString(pluginConfigTO.getConsumerId()) : null,
pluginConfigTO.safeGetConfig(),
pluginConfigTO.getEnabled() != null ? pluginConfigTO.getEnabled() : true,
pluginConfigTO.getCreatedAt()
);
}
@ResponseStatus(HttpStatus.NOT_FOUND)
class PluginConfigNotFoundException extends RuntimeException {
public PluginConfigNotFoundException(final String pluginConfig) {
super("Could not find plugin config '" + pluginConfig + "'");
}
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
class InternalServerError extends RuntimeException {
}
}