Initial commit.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package org.takiguchi.cerberus.cerberusapp;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CerberusApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CerberusApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceStatus;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.ApplicationService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.Application.anApplication;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/applications")
|
||||
public class ApplicationController {
|
||||
private final ApplicationService service;
|
||||
|
||||
public ApplicationController(ApplicationService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@GetMapping("/{applicationId}")
|
||||
public Optional<Application> getById(@PathVariable("applicationId") UUID applicationId) {
|
||||
return service.getById(applicationId);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<Application> getAll() {
|
||||
return service.getAll();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Application add(@RequestBody Application application) {
|
||||
Application applicationToAdd = anApplication()
|
||||
.withName(application.getName())
|
||||
.withServiceName(application.getServiceName())
|
||||
.withServiceType(application.getServiceType())
|
||||
.build();
|
||||
return service.add(applicationToAdd);
|
||||
}
|
||||
|
||||
@PutMapping("/{applicationId}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void update(@PathVariable("applicationId") UUID applicationId, @RequestBody Application application) {
|
||||
Application applicationToUpdate = anApplication()
|
||||
.withId(applicationId)
|
||||
.withName(application.getName())
|
||||
.withServiceName(application.getServiceName())
|
||||
.withServiceType(application.getServiceType())
|
||||
.build();
|
||||
service.update(applicationToUpdate);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{applicationId}")
|
||||
public void remove(@PathVariable("applicationId") UUID applicationId) {
|
||||
service.remove(applicationId);
|
||||
}
|
||||
|
||||
@GetMapping("/{applicationId}/status")
|
||||
public ServiceStatus getStatus(@PathVariable("applicationId") UUID applicationId) {
|
||||
return service.checkStatus(applicationId);
|
||||
}
|
||||
|
||||
@PostMapping("/{applicationId}/start")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void start(@PathVariable("applicationId") UUID applicationId) {
|
||||
service.start(applicationId);
|
||||
}
|
||||
|
||||
@PostMapping("/{applicationId}/stop")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void stop(@PathVariable("applicationId") UUID applicationId) {
|
||||
service.stop(applicationId);
|
||||
}
|
||||
|
||||
@PostMapping("/{applicationId}/restart")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void restart(@PathVariable("applicationId") UUID applicationId) {
|
||||
service.restart(applicationId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
|
||||
public class BadRequestException extends BusinessException {
|
||||
public BadRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BadRequestException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
/**
|
||||
* Business exception.
|
||||
*/
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
public BusinessException() {}
|
||||
|
||||
/**
|
||||
* Constructs an exception with a message.
|
||||
* @param message The description of the error met.
|
||||
*/
|
||||
public BusinessException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception with a message and a code.
|
||||
* @param message The description of the error met.
|
||||
* @param cause The cause of the exception.
|
||||
*/
|
||||
public BusinessException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Exception thrown when user attempt to access a resource that he has not rights.
|
||||
*/
|
||||
@ResponseStatus(value = HttpStatus.FORBIDDEN)
|
||||
public class ForbiddenException extends BusinessException {
|
||||
|
||||
public ForbiddenException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ForbiddenException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ForbiddenException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public class InternalServerErrorException extends TechnicalException {
|
||||
public InternalServerErrorException() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public InternalServerErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InternalServerErrorException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(value = HttpStatus.NO_CONTENT)
|
||||
public class NoContentException extends BusinessException {
|
||||
public NoContentException() {}
|
||||
|
||||
public NoContentException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NoContentException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND)
|
||||
public class NotFoundException extends BusinessException {
|
||||
public NotFoundException() {}
|
||||
|
||||
public NotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
/**
|
||||
* Technical exception.
|
||||
*/
|
||||
public class TechnicalException extends RuntimeException {
|
||||
/**
|
||||
* Constructs an exception with a message.
|
||||
* @param message The description of the error met.
|
||||
*/
|
||||
public TechnicalException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception with a message and a code.
|
||||
* @param message The description of the error met.
|
||||
* @param cause The cause of the exception.
|
||||
*/
|
||||
public TechnicalException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Exception thrown when an anonymous user attempt to access to secured resource or if he failed to login.
|
||||
*/
|
||||
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
|
||||
public class UnauthorizedException extends BusinessException {
|
||||
public UnauthorizedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UnauthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnauthorizedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Application {
|
||||
private final UUID id;
|
||||
/** The name to display. */
|
||||
private final String name;
|
||||
/** The technical service name, like a docker container name or a system V service name. */
|
||||
private final String serviceName;
|
||||
private final ServiceType serviceType;
|
||||
|
||||
public static Builder anApplication() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
private Application(UUID id, String name, String serviceName, ServiceType serviceType) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.serviceName = serviceName;
|
||||
this.serviceType = serviceType;
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public ServiceType getServiceType() {
|
||||
return serviceType;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private UUID id;
|
||||
private String name;
|
||||
private String serviceName;
|
||||
private ServiceType serviceType;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder withId(UUID id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withServiceType(ServiceType serviceType) {
|
||||
this.serviceType = serviceType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Application build() {
|
||||
return new Application(id, name, serviceName, serviceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.model;
|
||||
|
||||
public enum ServiceStatus {
|
||||
STARTED,
|
||||
STOPPED,
|
||||
UNKNOWN;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.model;
|
||||
|
||||
public enum ServiceType {
|
||||
FAKE,
|
||||
SERVICE,
|
||||
DOCKER;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.persistence.adapter;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.persistence.mapper.ApplicationEntityMapper;
|
||||
import org.takiguchi.cerberus.cerberusapp.persistence.model.ApplicationEntity;
|
||||
import org.takiguchi.cerberus.cerberusapp.persistence.repository.ApplicationRepository;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.ApplicationRepositoryPort;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class ApplicationJpaRepositoryAdapter implements ApplicationRepositoryPort {
|
||||
private final ApplicationRepository repository;
|
||||
private final ApplicationEntityMapper applicationMapper;
|
||||
|
||||
public ApplicationJpaRepositoryAdapter(ApplicationRepository repository,
|
||||
ApplicationEntityMapper applicationMapper) {
|
||||
this.repository = repository;
|
||||
this.applicationMapper = applicationMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Application> getById(UUID applicationId) {
|
||||
return repository.findById(applicationId)
|
||||
.map(applicationMapper::mapToDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Application> getAll() {
|
||||
return repository.findAll()
|
||||
.stream()
|
||||
.map(applicationMapper::mapToDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Application add(Application application) {
|
||||
ApplicationEntity entityToSave = applicationMapper.mapToEntity(application);
|
||||
ApplicationEntity savedApplication = repository.save(entityToSave);
|
||||
return applicationMapper.mapToDomain(savedApplication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Application application) {
|
||||
ApplicationEntity entityToSave = applicationMapper.mapToEntity(application);
|
||||
repository.save(entityToSave);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(UUID applicationId) {
|
||||
repository.deleteById(applicationId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.persistence.mapper;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.persistence.model.ApplicationEntity;
|
||||
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.Application.anApplication;
|
||||
|
||||
@Component
|
||||
public class ApplicationEntityMapper {
|
||||
|
||||
public Application mapToDomain(ApplicationEntity entity) {
|
||||
return anApplication()
|
||||
.withId(entity.getId())
|
||||
.withName(entity.getName())
|
||||
.withServiceName(entity.getServiceName())
|
||||
.withServiceType(entity.getServiceType())
|
||||
.build();
|
||||
}
|
||||
|
||||
public ApplicationEntity mapToEntity(Application application) {
|
||||
return new ApplicationEntity(
|
||||
application.getId(),
|
||||
application.getName(),
|
||||
application.getServiceName(),
|
||||
application.getServiceType()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.persistence.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceType;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.UUID;
|
||||
|
||||
import static javax.persistence.EnumType.ORDINAL;
|
||||
|
||||
@Entity
|
||||
@Table(name = "application")
|
||||
public class ApplicationEntity {
|
||||
@Id
|
||||
@GeneratedValue(generator = "system-uuid")
|
||||
private UUID id;
|
||||
private String name;
|
||||
private String serviceName;
|
||||
@Enumerated
|
||||
private ServiceType serviceType;
|
||||
|
||||
public ApplicationEntity() {
|
||||
}
|
||||
|
||||
public ApplicationEntity(UUID id, String name, String serviceName, ServiceType serviceType) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.serviceName = serviceName;
|
||||
this.serviceType = serviceType;
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public ServiceType getServiceType() {
|
||||
return serviceType;
|
||||
}
|
||||
|
||||
public void setServiceType(ServiceType serviceType) {
|
||||
this.serviceType = serviceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.persistence.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.takiguchi.cerberus.cerberusapp.persistence.model.ApplicationEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface ApplicationRepository extends JpaRepository<ApplicationEntity, UUID> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service;
|
||||
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ApplicationRepositoryPort {
|
||||
Optional<Application> getById(UUID applicationId);
|
||||
List<Application> getAll();
|
||||
Application add(Application application);
|
||||
void update(Application application);
|
||||
void remove(UUID applicationId);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.takiguchi.cerberus.cerberusapp.exception.NotFoundException;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceStatus;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.servicemanager.ServiceManagerProvider;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.validator.ApplicationValidator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.Application.anApplication;
|
||||
|
||||
@Service
|
||||
public class ApplicationService {
|
||||
private final ApplicationValidator applicationValidator;
|
||||
private final ApplicationRepositoryPort applicationRepositoryPort;
|
||||
private final ServiceManagerProvider serviceManagerProvider;
|
||||
|
||||
public ApplicationService(ApplicationValidator applicationValidator,
|
||||
ApplicationRepositoryPort applicationRepositoryPort,
|
||||
ServiceManagerProvider serviceManagerProvider) {
|
||||
this.applicationValidator = applicationValidator;
|
||||
this.applicationRepositoryPort = applicationRepositoryPort;
|
||||
this.serviceManagerProvider = serviceManagerProvider;
|
||||
}
|
||||
|
||||
public Optional<Application> getById(UUID applicationId) {
|
||||
return applicationRepositoryPort.getById(applicationId);
|
||||
}
|
||||
|
||||
public List<Application> getAll() {
|
||||
return applicationRepositoryPort.getAll();
|
||||
}
|
||||
|
||||
public Application add(Application application) {
|
||||
applicationValidator.validate(application);
|
||||
return applicationRepositoryPort.add(application);
|
||||
}
|
||||
|
||||
public void update(Application application) {
|
||||
applicationRepositoryPort.getById(application.getId())
|
||||
.map(existingApplication -> anApplication()
|
||||
.withId(existingApplication.getId())
|
||||
.withName(application.getName())
|
||||
.withServiceName(application.getServiceName())
|
||||
.withServiceType(application.getServiceType())
|
||||
.build()
|
||||
)
|
||||
.ifPresentOrElse(this::validateThenSave, NotFoundException::new);
|
||||
}
|
||||
|
||||
private void validateThenSave(Application updatedApplication) {
|
||||
applicationValidator.validate(updatedApplication);
|
||||
applicationRepositoryPort.update(updatedApplication);
|
||||
}
|
||||
|
||||
public void remove(UUID applicationId) {
|
||||
applicationRepositoryPort.remove(applicationId);
|
||||
}
|
||||
|
||||
public ServiceStatus checkStatus(UUID applicationId) {
|
||||
return applicationRepositoryPort.getById(applicationId)
|
||||
.map(application -> serviceManagerProvider.getServiceManager(application)
|
||||
.getStatus(application)
|
||||
).orElseThrow(NotFoundException::new);
|
||||
}
|
||||
|
||||
public void start(UUID applicationId) {
|
||||
applicationRepositoryPort.getById(applicationId)
|
||||
.ifPresentOrElse(
|
||||
application -> serviceManagerProvider.getServiceManager(application)
|
||||
.start(application),
|
||||
NotFoundException::new
|
||||
);
|
||||
}
|
||||
|
||||
public void stop(UUID applicationId) {
|
||||
applicationRepositoryPort.getById(applicationId)
|
||||
.ifPresentOrElse(
|
||||
application -> serviceManagerProvider.getServiceManager(application)
|
||||
.stop(application),
|
||||
NotFoundException::new
|
||||
);
|
||||
}
|
||||
|
||||
public void restart(UUID applicationId) {
|
||||
applicationRepositoryPort.getById(applicationId)
|
||||
.ifPresentOrElse(
|
||||
application -> serviceManagerProvider.getServiceManager(application)
|
||||
.restart(application),
|
||||
NotFoundException::new
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.exception;
|
||||
|
||||
public class ValidationException extends RuntimeException {
|
||||
public ValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.servicemanager;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceStatus;
|
||||
|
||||
@Service
|
||||
public class DockerServiceManager implements ServiceManager {
|
||||
@Override
|
||||
public ServiceStatus getStatus(Application application) {
|
||||
throw new IllegalStateException("DockerServiceManager#getStatus not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Application application) {
|
||||
throw new IllegalStateException("DockerServiceManager#start not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Application application) {
|
||||
throw new IllegalStateException("DockerServiceManager#stop not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart(Application application) {
|
||||
throw new IllegalStateException("DockerServiceManager#restart not implemented.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.servicemanager;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceStatus;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.ServiceStatus.*;
|
||||
|
||||
@Service
|
||||
public class FakeServiceManager implements ServiceManager {
|
||||
private final Map<UUID, ServiceStatus> handledApplications = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public ServiceStatus getStatus(Application application) {
|
||||
return handledApplications.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getKey().equals(application.getId()))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue)
|
||||
.orElse(STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Application application) {
|
||||
handledApplications.put(application.getId(), STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Application application) {
|
||||
handledApplications.put(application.getId(), STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart(Application application) {
|
||||
start(application);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.servicemanager;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceStatus;
|
||||
|
||||
@Service
|
||||
public class LinuxServiceManager implements ServiceManager {
|
||||
@Override
|
||||
public ServiceStatus getStatus(Application application) {
|
||||
throw new IllegalStateException("LinuxServiceManager#getStatus not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Application application) {
|
||||
throw new IllegalStateException("LinuxServiceManager#start not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Application application) {
|
||||
throw new IllegalStateException("LinuxServiceManager#stop not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart(Application application) {
|
||||
throw new IllegalStateException("LinuxServiceManager#restart not implemented.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.servicemanager;
|
||||
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceStatus;
|
||||
|
||||
public interface ServiceManager {
|
||||
ServiceStatus getStatus(Application application);
|
||||
void start(Application application);
|
||||
void stop(Application application);
|
||||
void restart(Application application);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.servicemanager;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.takiguchi.cerberus.cerberusapp.exception.InternalServerErrorException;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class ServiceManagerProvider {
|
||||
private final List<ServiceManager> serviceManagers;
|
||||
|
||||
public ServiceManagerProvider(List<ServiceManager> serviceManagers) {
|
||||
this.serviceManagers = serviceManagers;
|
||||
}
|
||||
|
||||
public ServiceManager getServiceManager(Application application) {
|
||||
ServiceManager result;
|
||||
switch (application.getServiceType()) {
|
||||
case FAKE:
|
||||
result = getServiceByClass(FakeServiceManager.class);
|
||||
break;
|
||||
case SERVICE:
|
||||
result = getServiceByClass(LinuxServiceManager.class);
|
||||
break;
|
||||
case DOCKER:
|
||||
result = getServiceByClass(DockerServiceManager.class);
|
||||
break;
|
||||
default:
|
||||
throw new InternalServerErrorException("");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ServiceManager getServiceByClass(Class<? extends ServiceManager> serviceManagerClass) {
|
||||
return serviceManagers.stream()
|
||||
.filter(serviceManagerClass::isInstance)
|
||||
.findFirst()
|
||||
.orElseThrow(InternalServerErrorException::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.validator;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.exception.ValidationException;
|
||||
|
||||
@Component
|
||||
public class ApplicationValidator {
|
||||
|
||||
public void validate(Application application) {
|
||||
if (application == null) {
|
||||
throw new ValidationException("Application is null.");
|
||||
}
|
||||
if (application.getName() == null) {
|
||||
throw new ValidationException("Application name is mandatory.");
|
||||
}
|
||||
if (application.getServiceName() == null) {
|
||||
throw new ValidationException("Application service name is mandatory.");
|
||||
}
|
||||
if (application.getServiceType() == null) {
|
||||
throw new ValidationException("Application service type is mandatory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/main/resources/application.yml
Normal file
15
src/main/resources/application.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
server:
|
||||
error:
|
||||
whitelabel:
|
||||
enabled: false # Disable html error responses.
|
||||
include-stacktrace: never
|
||||
|
||||
spring:
|
||||
# -------------------------------------------------
|
||||
# Database configuration
|
||||
# -------------------------------------------------
|
||||
datasource:
|
||||
driverClassName: org.postgresql.Driver
|
||||
url: jdbc:postgresql://localhost:50001/cerberus
|
||||
username: h23
|
||||
password: P@ssword1
|
||||
9
src/main/sql/ddl.sql
Normal file
9
src/main/sql/ddl.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE IF NOT EXISTS application (
|
||||
id UUID DEFAULT uuid_generate_v4() NOT NULL,
|
||||
name VARCHAR NOT NULL,
|
||||
service_name VARCHAR NOT NULL,
|
||||
service_type SMALLINT NOT NULL,
|
||||
CONSTRAINT application_pk PRIMARY KEY (id)
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.takiguchi.cerberus.cerberusapp;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class CerberusApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.*;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.servicemanager.ServiceManagerProvider;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.validator.ApplicationValidator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.Application.anApplication;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ApplicationServiceTest {
|
||||
private ApplicationService service;
|
||||
@Mock
|
||||
private ApplicationRepositoryPort applicationRepositoryPort;
|
||||
@Mock
|
||||
private ApplicationValidator applicationValidator;
|
||||
@Mock
|
||||
private ServiceManagerProvider serviceManagerProvider;
|
||||
@Captor
|
||||
private ArgumentCaptor<Application> applicationCaptor;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new ApplicationService(applicationValidator, applicationRepositoryPort, serviceManagerProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getById_should_return_the_application() {
|
||||
// given
|
||||
UUID applicationId = UUID.randomUUID();
|
||||
|
||||
Application application = anApplication().build();
|
||||
given(applicationRepositoryPort.getById(applicationId)).willReturn(Optional.of(application));
|
||||
|
||||
// when
|
||||
Optional<Application> result = service.getById(applicationId);
|
||||
|
||||
// then
|
||||
assertThat(result).contains(application);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAll_should_return_all_applications() {
|
||||
// given
|
||||
Application application1 = anApplication().build();
|
||||
Application application2 = anApplication().build();
|
||||
given(applicationRepositoryPort.getAll()).willReturn(List.of(application1, application2));
|
||||
|
||||
// when
|
||||
List<Application> result = service.getAll();
|
||||
|
||||
// then
|
||||
assertThat(result).contains(application1, application2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void add_should_validate_the_application_then_add_it() {
|
||||
// given
|
||||
Application application = anApplication().build();
|
||||
|
||||
InOrder inOrder = inOrder(applicationValidator, applicationRepositoryPort);
|
||||
|
||||
Application addedApplication = anApplication().build();
|
||||
given(applicationRepositoryPort.add(application)).willReturn(addedApplication);
|
||||
|
||||
// when
|
||||
Application result = service.add(application);
|
||||
|
||||
// then
|
||||
assertThat(result).isEqualTo(addedApplication);
|
||||
inOrder.verify(applicationValidator).validate(application);
|
||||
inOrder.verify(applicationRepositoryPort).add(application);
|
||||
}
|
||||
|
||||
@Test
|
||||
void update_should_retrieve_application_then_edit_some_fields_then_validate_it__then_update_it() {
|
||||
// given
|
||||
UUID newId = UUID.randomUUID();
|
||||
String newName = "new name";
|
||||
String newServiceName = "new service name";
|
||||
Application application = anApplication()
|
||||
.withId(newId)
|
||||
.withName(newName)
|
||||
.withServiceName(newServiceName)
|
||||
.build();
|
||||
|
||||
InOrder inOrder = inOrder(applicationRepositoryPort, applicationValidator, applicationRepositoryPort);
|
||||
|
||||
UUID oldId = UUID.randomUUID();
|
||||
Application existingApplication = anApplication()
|
||||
.withId(oldId)
|
||||
.build();
|
||||
given(applicationRepositoryPort.getById(newId)).willReturn(Optional.of(existingApplication));
|
||||
|
||||
// when
|
||||
service.update(application);
|
||||
|
||||
// then
|
||||
inOrder.verify(applicationRepositoryPort).getById(newId);
|
||||
inOrder.verify(applicationValidator).validate(applicationCaptor.capture());
|
||||
Application updatedApplication = applicationCaptor.getValue();
|
||||
inOrder.verify(applicationRepositoryPort).update(updatedApplication);
|
||||
|
||||
assertThat(updatedApplication).isNotNull()
|
||||
.extracting(
|
||||
Application::getId,
|
||||
Application::getName,
|
||||
Application::getServiceName
|
||||
)
|
||||
.contains(
|
||||
oldId,
|
||||
newName,
|
||||
newServiceName
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.takiguchi.cerberus.cerberusapp.service.validator;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.Application;
|
||||
import org.takiguchi.cerberus.cerberusapp.model.ServiceType;
|
||||
import org.takiguchi.cerberus.cerberusapp.service.exception.ValidationException;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowableOfType;
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.Application.anApplication;
|
||||
import static org.takiguchi.cerberus.cerberusapp.model.ServiceType.FAKE;
|
||||
|
||||
class ApplicationValidatorTest {
|
||||
private ApplicationValidator validator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
validator = new ApplicationValidator();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("should_throw_a_validation_exception_data_provider")
|
||||
void should_throw_a_validation_exception(Application application, String errorMessage) {
|
||||
// when
|
||||
ValidationException exception = catchThrowableOfType(() -> validator.validate(application), ValidationException.class);
|
||||
|
||||
// then
|
||||
assertThat(exception).isNotNull()
|
||||
.hasMessage(errorMessage);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> should_throw_a_validation_exception_data_provider() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
null,
|
||||
"Application is null."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication().build(),
|
||||
"Application name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withId(UUID.randomUUID())
|
||||
.build(),
|
||||
"Application name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withName("name")
|
||||
.build(),
|
||||
"Application service name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withServiceName("serviceName")
|
||||
.build(),
|
||||
"Application name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withServiceType(FAKE)
|
||||
.build(),
|
||||
"Application name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withId(UUID.randomUUID())
|
||||
.withName("name")
|
||||
.build(),
|
||||
"Application service name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withId(UUID.randomUUID())
|
||||
.withServiceName("serviceName")
|
||||
.build(),
|
||||
"Application name is mandatory."
|
||||
),
|
||||
Arguments.of(
|
||||
anApplication()
|
||||
.withId(UUID.randomUUID())
|
||||
.withName("name")
|
||||
.withServiceName("serviceName")
|
||||
.build(),
|
||||
"Application service type is mandatory."
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user