diff --git a/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java index 8f69d140c8386a5f0f6ca1df4b392c5610b7c98e..207fb9778d9547661d0c17b23fde27f67c55d4e8 100644 --- a/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java +++ b/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java @@ -153,11 +153,21 @@ public class ContainerEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<ContainerDto> findById(@NotNull @PathVariable("id") Long containerId) throws DockerClientException, - ContainerNotFoundException, ContainerNotRunningException { + ContainerNotFoundException { log.debug("endpoint find container, id={}", containerId); - final Container container = containerService.inspect(containerId); + final Container container = containerService.find(containerId); final ContainerDto dto = containerMapper.containerToContainerDto(container); - dto.setState(ContainerStateDto.RUNNING); + final ContainerDto inspect; + try { + inspect = containerService.inspect(container.getHash()); + dto.setIpAddress(inspect.getIpAddress()); + dto.setRunning(inspect.getRunning()); + dto.setState(inspect.getState()); + } catch (ContainerNotRunningException e) { + /* ignore */ + dto.setRunning(false); + dto.setState(ContainerStateDto.EXITED); + } log.trace("find container resulted in container {}", dto); return ResponseEntity.ok() .body(dto); diff --git a/dbrepo-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointIntegrationTest.java b/dbrepo-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointIntegrationTest.java index c49b7ea08ee1de453788526a3017c11816cdd277..a1a199c00ecf54e4d2c5509e342feaa7fca6108a 100644 --- a/dbrepo-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointIntegrationTest.java +++ b/dbrepo-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointIntegrationTest.java @@ -51,7 +51,7 @@ public class ContainerEndpointIntegrationTest extends BaseUnitTest { ContainerNotRunningException { /* test */ - findById_generic(CONTAINER_1_ID, CONTAINER_1); + findById_generic(CONTAINER_1_ID, CONTAINER_1_HASH, CONTAINER_1, CONTAINER_1_DTO); } @Test @@ -64,7 +64,7 @@ public class ContainerEndpointIntegrationTest extends BaseUnitTest { .thenReturn(Optional.of(USER_1)); /* test */ - findById_generic(CONTAINER_1_ID, CONTAINER_1); + findById_generic(CONTAINER_1_ID, CONTAINER_1_HASH, CONTAINER_1, CONTAINER_1_DTO); } @Test @@ -77,7 +77,7 @@ public class ContainerEndpointIntegrationTest extends BaseUnitTest { .thenReturn(Optional.of(USER_4)); /* test */ - findById_generic(CONTAINER_1_ID, CONTAINER_1); + findById_generic(CONTAINER_1_ID, CONTAINER_1_HASH, CONTAINER_1, CONTAINER_1_DTO); } @Test @@ -270,14 +270,14 @@ public class ContainerEndpointIntegrationTest extends BaseUnitTest { /* ## GENERIC TEST CASES ## */ /* ################################################################################################### */ - public void findById_generic(Long containerId, Container container) throws DockerClientException, - ContainerNotFoundException, ContainerNotRunningException { + public void findById_generic(Long containerId, String containerHash, Container container, ContainerDto containerDto) + throws DockerClientException, ContainerNotFoundException, ContainerNotRunningException { /* mock */ when(containerService.find(containerId)) .thenReturn(container); - when(containerService.inspect(containerId)) - .thenReturn(container); + when(containerService.inspect(containerHash)) + .thenReturn(containerDto); /* test */ final ResponseEntity<ContainerDto> response = containerEndpoint.findById(containerId); @@ -288,7 +288,7 @@ public class ContainerEndpointIntegrationTest extends BaseUnitTest { } public void delete_generic(Long containerId, Container container, Principal principal) throws ContainerNotFoundException, - ContainerStillRunningException, ContainerAlreadyRemovedException, DockerClientException { + ContainerStillRunningException, ContainerAlreadyRemovedException { /* mock */ when(containerService.find(containerId)) @@ -340,7 +340,7 @@ public class ContainerEndpointIntegrationTest extends BaseUnitTest { public void modify_generic(ContainerActionTypeDto data, Long containerId, Container container, Principal principal) throws ContainerAlreadyRunningException, ContainerNotFoundException, ContainerAlreadyStoppedException, - UserNotFoundException, NotAllowedException, DockerClientException { + UserNotFoundException, NotAllowedException { final ContainerChangeDto request = ContainerChangeDto.builder() .action(data) .build(); diff --git a/dbrepo-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/dbrepo-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java index 88e2943220db0ad616e7534e2658e4c22744756b..23e8ed753219f23e8040573eca5ac43da97d311f 100644 --- a/dbrepo-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java +++ b/dbrepo-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -2,6 +2,7 @@ package at.tuwien.service; import at.tuwien.BaseUnitTest; import at.tuwien.api.container.ContainerCreateRequestDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.config.DockerConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.container.Container; @@ -318,7 +319,7 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { containerRepository.save(CONTAINER_1_SIMPLE); /* test */ - final Container response = containerService.inspect(CONTAINER_1_ID); + final ContainerDto response = containerService.inspect(CONTAINER_1_HASH); assertEquals(CONTAINER_1_ID, response.getId()); assertEquals(CONTAINER_1_NAME, response.getName()); assertEquals(CONTAINER_1_INTERNALNAME, response.getInternalName()); @@ -330,7 +331,7 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { /* test */ assertThrows(ContainerNotFoundException.class, () -> { - containerService.inspect(CONTAINER_2_ID); + containerService.inspect(CONTAINER_2_HASH); }); } @@ -343,7 +344,7 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { /* test */ assertThrows(ContainerNotRunningException.class, () -> { - containerService.inspect(CONTAINER_1_ID); + containerService.inspect(CONTAINER_1_HASH); }); } } diff --git a/dbrepo-container-service/services/src/main/java/at/tuwien/service/ContainerService.java b/dbrepo-container-service/services/src/main/java/at/tuwien/service/ContainerService.java index badd04c8db0c8a4a6370a5312c0aa6dbde546a57..c66cd56e5b76811e30e268848fa4275b2dc26d76 100644 --- a/dbrepo-container-service/services/src/main/java/at/tuwien/service/ContainerService.java +++ b/dbrepo-container-service/services/src/main/java/at/tuwien/service/ContainerService.java @@ -1,8 +1,10 @@ package at.tuwien.service; import at.tuwien.api.container.ContainerCreateRequestDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.entities.container.Container; import at.tuwien.exception.*; +import org.springframework.transaction.annotation.Transactional; import java.security.Principal; import java.util.List; @@ -46,13 +48,12 @@ public interface ContainerService { Container find(Long id) throws ContainerNotFoundException; /** - * @param id + * @param hash * @return - * @throws ContainerNotFoundException * @throws DockerClientException * @throws ContainerNotRunningException */ - Container inspect(Long id) throws ContainerNotFoundException, DockerClientException, ContainerNotRunningException; + ContainerDto inspect(String hash) throws DockerClientException, ContainerNotRunningException; /** * Retrieve a list of all containers from the metadata database diff --git a/dbrepo-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java b/dbrepo-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java index 8c18fc898711887755d17b4661182df37f2f6cc1..bed82910cab1d59d8ad016225bb8b5d9612eb93c 100644 --- a/dbrepo-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java +++ b/dbrepo-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java @@ -1,6 +1,8 @@ package at.tuwien.service.impl; import at.tuwien.api.container.ContainerCreateRequestDto; +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.container.ContainerStateDto; import at.tuwien.config.DockerDaemonConfig; import at.tuwien.entities.container.Container; import at.tuwien.entities.container.image.ContainerImage; @@ -184,12 +186,10 @@ public class ContainerServiceImpl implements ContainerService { @Override @Transactional - public Container inspect(Long id) throws ContainerNotFoundException, DockerClientException, - ContainerNotRunningException { - final Container container = find(id); + public ContainerDto inspect(String hash) throws DockerClientException, ContainerNotRunningException { final InspectContainerResponse response; try { - response = dockerClient.inspectContainerCmd(container.getHash()) + response = dockerClient.inspectContainerCmd(hash) .withSize(true) .exec(); } catch (NotFoundException e) { @@ -207,6 +207,11 @@ public class ContainerServiceImpl implements ContainerService { log.error("Failed to inspect container state: container is not running"); throw new ContainerNotRunningException("Failed to inspect container state"); } + final ContainerDto container = ContainerDto.builder() + .hash(hash) + .running(response.getState().getRunning()) + .state(containerMapper.containerStateToContainerStateDto(response.getState())) + .build(); /* now we only support one network */ response.getNetworkSettings() .getNetworks() @@ -214,7 +219,7 @@ public class ContainerServiceImpl implements ContainerService { log.trace("key {} network {}", key, network); container.setIpAddress(network.getIpAddress()); }); - log.info("Inspect container with id {}", id); + log.info("Inspected container with hash {}", hash); return container; } diff --git a/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 63114d8ccee3353eca4e90fc34f36dfb98e65f3a..e07a29bf0b2012e4910602939f8b4125f1607976 100644 --- a/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -1,5 +1,6 @@ package at.tuwien.endpoints; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.*; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.entities.container.Container; @@ -257,7 +258,7 @@ public class DatabaseEndpoint { mediaType = "application/json", schema = @Schema(implementation = DatabaseDto.class))}), @ApiResponse(responseCode = "404", - description = "Database could not be found", + description = "Database or container could not be found", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @@ -270,7 +271,7 @@ public class DatabaseEndpoint { public ResponseEntity<DatabaseDto> findById(@NotNull @PathVariable("id") Long containerId, @NotNull @PathVariable Long databaseId, Principal principal) - throws DatabaseNotFoundException, AccessDeniedException { + throws DatabaseNotFoundException, AccessDeniedException, ContainerNotFoundException { log.debug("endpoint find database, containerId={}, databaseId={}", containerId, databaseId); final Database database = databaseService.findById(containerId, databaseId); final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(database); @@ -281,7 +282,9 @@ public class DatabaseEndpoint { .map(databaseMapper::databaseAccessToDatabaseAccessDto) .collect(Collectors.toList())); } - log.trace("find database resulted in database {}", database); + final ContainerDto containerDto = containerService.inspect(containerId); + dto.setContainer(containerDto); + log.trace("find database resulted in dto {}", dto); return ResponseEntity.ok(dto); } diff --git a/dbrepo-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java b/dbrepo-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java index c32330b76d496cf6232be98a237062ee37f156d4..4cf0fc3edb0a0dc7898a07e5de495c825be5456b 100644 --- a/dbrepo-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java +++ b/dbrepo-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java @@ -350,7 +350,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @Test @WithAnonymousUser - public void findById_anonymous_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + public void findById_anonymous_succeeds() throws AccessDeniedException, DatabaseNotFoundException, ContainerNotFoundException { /* test */ findById_generic(CONTAINER_1_ID, CONTAINER_1, DATABASE_1_ID, DATABASE_1, null); @@ -368,7 +368,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"find-database"}) - public void findById_hasRole_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + public void findById_hasRole_succeeds() throws AccessDeniedException, DatabaseNotFoundException, ContainerNotFoundException { /* pre-condition */ assertTrue(DATABASE_3_PUBLIC); @@ -380,7 +380,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"find-database"}) public void findById_hasRoleForeign_succeeds() throws AccessDeniedException, - DatabaseNotFoundException { + DatabaseNotFoundException, ContainerNotFoundException { /* pre-condition */ assertTrue(DATABASE_3_PUBLIC); @@ -392,7 +392,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"find-database"}) public void findById_ownerSeesAccessRights_succeeds() throws AccessDeniedException, - DatabaseNotFoundException { + DatabaseNotFoundException, ContainerNotFoundException { /* mock */ when(accessService.list(DATABASE_1_ID)) @@ -506,7 +506,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { } public DatabaseDto findById_generic(Long containerId, Container container, Long databaseId, Database database, - Principal principal) throws DatabaseNotFoundException, AccessDeniedException { + Principal principal) throws DatabaseNotFoundException, AccessDeniedException, ContainerNotFoundException { /* mock */ if (database != null) { diff --git a/dbrepo-database-service/services/src/main/java/at/tuwien/config/GatewayConfig.java b/dbrepo-database-service/services/src/main/java/at/tuwien/config/GatewayConfig.java index b30f9a567ca1abd794c1f630e633815606c14d00..f3db3e030a05b7e91135fd8a0968d29eb9b6d2d5 100644 --- a/dbrepo-database-service/services/src/main/java/at/tuwien/config/GatewayConfig.java +++ b/dbrepo-database-service/services/src/main/java/at/tuwien/config/GatewayConfig.java @@ -27,8 +27,8 @@ public class GatewayConfig { @Value("${spring.rabbitmq.password}") private String brokerPassword; - @Bean("authenticationRestTemplate") - public RestTemplate authenticationRestTemplate() { + @Bean("gatewayRestTemplate") + public RestTemplate gatewayRestTemplate() { final RestTemplate restTemplate = new RestTemplate(); restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(gatewayEndpoint)); return restTemplate; diff --git a/dbrepo-database-service/services/src/main/java/at/tuwien/gateway/ContainerServiceGateway.java b/dbrepo-database-service/services/src/main/java/at/tuwien/gateway/ContainerServiceGateway.java new file mode 100644 index 0000000000000000000000000000000000000000..8fcd57a66e49f5c73a44e41e9958c2606344819c --- /dev/null +++ b/dbrepo-database-service/services/src/main/java/at/tuwien/gateway/ContainerServiceGateway.java @@ -0,0 +1,14 @@ +package at.tuwien.gateway; + +import at.tuwien.api.container.ContainerDto; +import at.tuwien.exception.ContainerNotFoundException; + +public interface ContainerServiceGateway { + + /** + * @param id + * @return + * @throws ContainerNotFoundException + */ + ContainerDto find(Long id) throws ContainerNotFoundException; +} diff --git a/dbrepo-database-service/services/src/main/java/at/tuwien/gateway/impl/ContainerServiceGatewayImpl.java b/dbrepo-database-service/services/src/main/java/at/tuwien/gateway/impl/ContainerServiceGatewayImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8e366fdf984df58824a2fead84c68a535e6b2a41 --- /dev/null +++ b/dbrepo-database-service/services/src/main/java/at/tuwien/gateway/impl/ContainerServiceGatewayImpl.java @@ -0,0 +1,41 @@ +package at.tuwien.gateway.impl; + +import at.tuwien.api.container.ContainerDto; +import at.tuwien.config.GatewayConfig; +import at.tuwien.exception.ContainerNotFoundException; +import at.tuwien.gateway.ContainerServiceGateway; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Service +public class ContainerServiceGatewayImpl implements ContainerServiceGateway { + + private final RestTemplate restTemplate; + private final GatewayConfig gatewayConfig; + + @Autowired + public ContainerServiceGatewayImpl(@Qualifier("gatewayRestTemplate") RestTemplate restTemplate, + GatewayConfig gatewayConfig) { + this.restTemplate = restTemplate; + this.gatewayConfig = gatewayConfig; + } + + @Override + public ContainerDto find(Long id) throws ContainerNotFoundException { + final String url = gatewayConfig.getGatewayEndpoint() + "/api/container/" + id; + final ResponseEntity<ContainerDto> response = restTemplate.exchange(url, HttpMethod.GET, null, ContainerDto.class); + if (!response.getStatusCode().equals(HttpStatus.OK)) { + log.error("Failed to find container: {}", response.getStatusCode()); + throw new ContainerNotFoundException("Failed to find container"); + } + return response.getBody(); + } + +} diff --git a/dbrepo-database-service/services/src/main/java/at/tuwien/service/ContainerService.java b/dbrepo-database-service/services/src/main/java/at/tuwien/service/ContainerService.java index ee70355c6d596650f7b0896dba30882cd760eeae..d58e11d4416cb62727f6a12688c2ff419fab2add 100644 --- a/dbrepo-database-service/services/src/main/java/at/tuwien/service/ContainerService.java +++ b/dbrepo-database-service/services/src/main/java/at/tuwien/service/ContainerService.java @@ -1,8 +1,11 @@ package at.tuwien.service; +import at.tuwien.api.container.ContainerDto; import at.tuwien.entities.container.Container; import at.tuwien.exception.ContainerNotFoundException; public interface ContainerService { Container find(Long id) throws ContainerNotFoundException; + + ContainerDto inspect(Long id) throws ContainerNotFoundException; } diff --git a/dbrepo-database-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java b/dbrepo-database-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java index bb657b1812fc208a85020c84a28856c084273ac6..76c29263d4b42461ac01274551f5650a5b6397ba 100644 --- a/dbrepo-database-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java +++ b/dbrepo-database-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java @@ -1,7 +1,9 @@ package at.tuwien.service.impl; +import at.tuwien.api.container.ContainerDto; import at.tuwien.entities.container.Container; import at.tuwien.exception.ContainerNotFoundException; +import at.tuwien.gateway.ContainerServiceGateway; import at.tuwien.repository.jpa.ContainerRepository; import at.tuwien.service.ContainerService; import lombok.extern.log4j.Log4j2; @@ -15,10 +17,13 @@ import java.util.Optional; public class ContainerServiceImpl implements ContainerService { private final ContainerRepository containerRepository; + private final ContainerServiceGateway containerServiceGateway; @Autowired - public ContainerServiceImpl(ContainerRepository containerRepository) { + public ContainerServiceImpl(ContainerRepository containerRepository, + ContainerServiceGateway containerServiceGateway) { this.containerRepository = containerRepository; + this.containerServiceGateway = containerServiceGateway; } @Override @@ -30,4 +35,9 @@ public class ContainerServiceImpl implements ContainerService { } return optional.get(); } + + @Override + public ContainerDto inspect(Long id) throws ContainerNotFoundException { + return containerServiceGateway.find(id); + } } diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageCreateDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageCreateDto.java index 8bfe43b2b109cab3c43c5bb46f7df1d9c870d1c8..c2278f343f1186875b83510b9ea906c53930ce02 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageCreateDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageCreateDto.java @@ -34,11 +34,11 @@ public class BannerMessageCreateDto { private String linkText; @JsonProperty("display_start") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant displayStart; @JsonProperty("display_end") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant displayEnd; } diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageUpdateDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageUpdateDto.java index 26a2ac68adc97f17d5969f3fb3a948f099bc5e37..107c2405ca76a791dbf8684eb8a7f510be3589f4 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageUpdateDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/maintenance/BannerMessageUpdateDto.java @@ -34,11 +34,11 @@ public class BannerMessageUpdateDto { private String linkText; @JsonProperty("display_start") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant displayStart; @JsonProperty("display_end") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant displayEnd; } diff --git a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java index 2d91e8ac304ec00b5c851dc14599803c6b65a85d..e0135a0715100e5a6bd4b7e27bb80a12d9ef30bf 100644 --- a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java @@ -3,8 +3,8 @@ package at.tuwien.test; import at.tuwien.api.amqp.CreateVirtualHostDto; import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.container.image.ImageEnvItemDto; -import at.tuwien.api.container.image.ImageEnvItemTypeDto; +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.container.image.*; import at.tuwien.api.database.DatabaseCreateDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.LicenseDto; @@ -304,6 +304,14 @@ public abstract class BaseTest { .attributes(USER_1_ATTRIBUTES_DTO) .build(); + public final static UserBriefDto USER_1_BRIEF_DTO = UserBriefDto.builder() + .id(USER_1_ID) + .username(USER_1_USERNAME) + .firstname(USER_1_FIRSTNAME) + .lastname(USER_1_LASTNAME) + .emailVerified(USER_1_VERIFIED) + .build(); + public final static UserDetails USER_1_DETAILS = UserDetailsDto.builder() .username(USER_1_USERNAME) .email(USER_1_EMAIL) @@ -701,6 +709,14 @@ public abstract class BaseTest { .hasTime(IMAGE_DATE_1_HAS_TIME) .build(); + public final static ImageDateDto IMAGE_DATE_1_DTO = ImageDateDto.builder() + .id(IMAGE_DATE_1_ID) + .unixFormat(IMAGE_DATE_1_UNIX_FORMAT) + .databaseFormat(IMAGE_DATE_1_DATABASE_FORMAT) + .example(IMAGE_DATE_1_EXAMPLE) + .hasTime(IMAGE_DATE_1_HAS_TIME) + .build(); + public final static Long IMAGE_DATE_2_ID = 2L; public final static Long IMAGE_DATE_2_IMAGE_ID = IMAGE_1_ID; public final static String IMAGE_DATE_2_UNIX_FORMAT = "dd.MM.yy"; @@ -717,6 +733,14 @@ public abstract class BaseTest { .hasTime(IMAGE_DATE_2_HAS_TIME) .build(); + public final static ImageDateDto IMAGE_DATE_2_DTO = ImageDateDto.builder() + .id(IMAGE_DATE_2_ID) + .unixFormat(IMAGE_DATE_2_UNIX_FORMAT) + .databaseFormat(IMAGE_DATE_2_DATABASE_FORMAT) + .example(IMAGE_DATE_2_EXAMPLE) + .hasTime(IMAGE_DATE_2_HAS_TIME) + .build(); + public final static Long IMAGE_DATE_3_ID = 3L; public final static Long IMAGE_DATE_3_IMAGE_ID = IMAGE_1_ID; public final static String IMAGE_DATE_3_UNIX_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS"; @@ -733,6 +757,14 @@ public abstract class BaseTest { .hasTime(IMAGE_DATE_3_HAS_TIME) .build(); + public final static ImageDateDto IMAGE_DATE_3_DTO = ImageDateDto.builder() + .id(IMAGE_DATE_3_ID) + .unixFormat(IMAGE_DATE_3_UNIX_FORMAT) + .databaseFormat(IMAGE_DATE_3_DATABASE_FORMAT) + .example(IMAGE_DATE_3_EXAMPLE) + .hasTime(IMAGE_DATE_3_HAS_TIME) + .build(); + public final static ContainerImage IMAGE_1 = ContainerImage.builder() .id(IMAGE_1_ID) .repository(IMAGE_1_REPOSITORY) @@ -763,6 +795,27 @@ public abstract class BaseTest { .environment(List.of() /* for jpa */) .build(); + public final static ImageDto IMAGE_1_DTO = ImageDto.builder() + .id(IMAGE_1_ID) + .repository(IMAGE_1_REPOSITORY) + .tag(IMAGE_1_TAG) + .hash(IMAGE_1_HASH) + .compiled(IMAGE_1_BUILT) + .dialect(IMAGE_1_DIALECT) + .jdbcMethod(IMAGE_1_JDBC) + .driverClass(IMAGE_1_DRIVER) + .size(BigInteger.valueOf(IMAGE_1_SIZE)) + .environment(IMAGE_1_ENV_DTO) + .defaultPort(IMAGE_1_PORT) + .dateFormats(List.of(IMAGE_DATE_1_DTO, IMAGE_DATE_2_DTO, IMAGE_DATE_3_DTO)) + .build(); + + public final static ImageBriefDto IMAGE_1_BRIEF_DTO = ImageBriefDto.builder() + .id(IMAGE_1_ID) + .repository(IMAGE_1_REPOSITORY) + .tag(IMAGE_1_TAG) + .build(); + public final static Long IMAGE_2_ID = 2L; public final static String IMAGE_2_REPOSITORY = "mysql"; public final static String IMAGE_2_TAG = "8.0"; @@ -824,6 +877,7 @@ public abstract class BaseTest { public final static Long CONTAINER_1_ID = 1L; public final static String CONTAINER_1_HASH = "deadbeef"; public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1; + public final static ImageBriefDto CONTAINER_1_IMAGE_BRIEF_DTO = IMAGE_1_BRIEF_DTO; public final static String CONTAINER_1_NAME = "u01"; public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-u01"; public final static String CONTAINER_1_IP = "172.30.0.5"; @@ -862,6 +916,17 @@ public abstract class BaseTest { .owner(null /* for jpa */) .build(); + public final static ContainerDto CONTAINER_1_DTO = ContainerDto.builder() + .id(CONTAINER_1_ID) + .name(CONTAINER_1_NAME) + .internalName(CONTAINER_1_INTERNALNAME) + .image(CONTAINER_1_IMAGE_BRIEF_DTO) + .hash(CONTAINER_1_HASH) + .created(CONTAINER_1_CREATED) + .ipAddress(CONTAINER_1_IP) + .owner(USER_1_BRIEF_DTO) + .build(); + public final static Long CONTAINER_2_ID = 2L; public final static String CONTAINER_2_HASH = "deadbeef"; public final static ContainerImage CONTAINER_2_IMAGE = IMAGE_1; diff --git a/dbrepo-ui/components/DatabaseList.vue b/dbrepo-ui/components/DatabaseList.vue index bd1c65046079da684299a1b76c0dc7e91e93c9e2..bdca982e74d2a9902c1fe37f35af6c39a3733441 100644 --- a/dbrepo-ui/components/DatabaseList.vue +++ b/dbrepo-ui/components/DatabaseList.vue @@ -1,7 +1,7 @@ <template> <div> <v-progress-linear v-if="loadingContainers || loadingDatabases" :indeterminate="!error" /> - <v-card v-if="!$vuetify.theme.dark && containers.list > 0" flat tile> + <v-card v-if="!$vuetify.theme.dark && containers.length> 0" flat tile> <v-divider class="mx-4" /> </v-card> <v-card @@ -32,18 +32,8 @@ v-text="container.database.identifier.publisher" /> </div> <div v-text="identifierDescription(container)" /> - </v-card-text> - <v-card-text v-if="needsStart(container) || needsDatabase(container)" class="db-buttons"> - <v-btn - v-if="needsStart(container)" - small - secondary - :loading="container?.loading" - @click.stop="startContainer(container).then(() => createDatabase(container))"> - Start - </v-btn> <v-btn - v-else-if="needsDatabase(container)" + v-if="needsDatabase(container)" small secondary :loading="container?.loading" @@ -101,15 +91,6 @@ export default { formatCreators (container) { return ContainerMapper.containerToCreator(container) }, - needsStart (container) { - if (!this.user) { - return false - } - if (container.creator.username !== this.user.username) { - return false - } - return container.running === false - }, needsDatabase (container) { if (!this.user) { return false @@ -147,16 +128,6 @@ export default { }) this.loadingContainers = false }, - startContainer (container) { - container.loading = true - return new Promise((resolve, reject) => { - ContainerService.modify(container.id, 'start') - .then(() => resolve()) - .finally(() => { - container.loading = false - }) - }) - }, createDatabase (container) { container.loading = true DatabaseService.create(container.id, { name: container.name, is_public: true }) diff --git a/dbrepo-ui/components/dialogs/EditMaintenanceMessage.vue b/dbrepo-ui/components/dialogs/EditMaintenanceMessage.vue index 421512389542cf47cb92622235d7e1476a7d135a..f3090c1e139dfc4dd0768afd59e4f78150c9687e 100644 --- a/dbrepo-ui/components/dialogs/EditMaintenanceMessage.vue +++ b/dbrepo-ui/components/dialogs/EditMaintenanceMessage.vue @@ -2,7 +2,6 @@ <div> <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit"> <v-card> - <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> <v-card-title v-text="title" /> <v-card-text> <v-row dense> @@ -31,31 +30,17 @@ <v-text-field v-model="localMessage.display_start" clearable + hint="YYYY-MM-dd HH:mm:ss" label="Start timestamp" /> </v-col> <v-col cols="6"> <v-text-field v-model="localMessage.display_end" clearable + hint="YYYY-MM-dd HH:mm:ss" label="End timestamp" /> </v-col> </v-row> - <v-row dense> - <v-col> - <v-text-field - v-model="localMessage.link" - clearable - label="Link" /> - </v-col> - </v-row> - <v-row dense> - <v-col> - <v-text-field - v-model="localMessage.link_text" - clearable - label="Link Text" /> - </v-col> - </v-row> </v-card-text> <v-card-actions> <v-btn @@ -89,6 +74,7 @@ <script> import MetadataService from '@/api/metadata.service' +import { timestampToTimeZonedTimestamp, formatTimestampUTC } from '@/utils' export default { props: { @@ -113,9 +99,7 @@ export default { type: null, message: null, display_start: null, - display_end: null, - link: null, - link_text: null + display_end: null }, modify: { username: null, @@ -124,9 +108,6 @@ export default { } }, computed: { - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, database () { return this.$store.state.database }, @@ -167,9 +148,7 @@ export default { type: null, message: null, display_start: null, - display_end: null, - link: null, - link_text: null + display_end: null } } else { this.loadMessage(this.id) @@ -178,6 +157,8 @@ export default { loadMessage (id) { MetadataService.findMessage(id) .then((message) => { + message.display_start = formatTimestampUTC(message.display_start) + message.display_end = formatTimestampUTC(message.display_end) this.localMessage = message }) }, @@ -190,7 +171,14 @@ export default { }, createMessage () { this.loading = true - MetadataService.createMessage(this.localMessage) + const payload = Object.assign({}, this.localMessage) + if (payload.display_start) { + payload.display_start = timestampToTimeZonedTimestamp(payload.display_start) + } + if (payload.display_end) { + payload.display_end = timestampToTimeZonedTimestamp(payload.display_end) + } + MetadataService.createMessage(payload) .then(() => { this.$emit('close-dialog', { success: true }) this.$emit('reload-messages', { success: true }) @@ -203,6 +191,12 @@ export default { this.loading = true const payload = Object.assign({}, this.localMessage) delete payload.id + if (payload.display_start) { + payload.display_start = timestampToTimeZonedTimestamp(payload.display_start) + } + if (payload.display_end) { + payload.display_end = timestampToTimeZonedTimestamp(payload.display_end) + } MetadataService.updateMessage(this.localMessage.id, payload) .then(() => { this.$emit('close-dialog', { success: true }) diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index 061bfc620d35cfe216c176ef18453c41a3c98951..198c87bb14fa6e6109dc256a354448028a93a785 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -46,11 +46,8 @@ class="banner" border="left" tile - :type="message.type"> - {{ message.message }} - <span v-if="message.link">‐</span> - <a v-if="message.link" :href="message.link">{{ message.link_text ? message.link_text : message.link }}</a> - </v-alert> + :type="message.type" + v-text="message.messages" /> </div> </v-navigation-drawer> <v-form ref="form" @submit.prevent="submit"> diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue index 36e9bcea2483b8cc10c8a5456b6be0242c8d154c..102deb0544a2a3d1389c0e1d49ab729a4ba885a0 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue @@ -213,9 +213,34 @@ <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> <span v-if="!loading" v-text="container_internal_name" /> </v-list-item-content> + <v-list-item-title class="mt-2"> + Container IP + </v-list-item-title> + <v-list-item-content> + <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> + <span v-if="!loading" v-text="container_ip" /> + </v-list-item-content> + <v-list-item-title class="mt-2"> + Container State + </v-list-item-title> + <v-list-item-content> + <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> + <span v-if="!loading" v-text="container_state" /> + </v-list-item-content> </v-list-item-content> </v-list-item> </v-list> + <v-card-actions> + <v-btn + v-if="needsStart" + small + secondary + color="secondary" + :loading="loadingStart" + @click.stop="startContainer"> + Start + </v-btn> + </v-card-actions> </v-card-text> </v-card> </v-tab-item> @@ -245,6 +270,7 @@ import { formatTimestampUTCLabel } from '@/utils' import Banner from '@/components/identifier/Banner' import DatabaseMapper from '@/api/database.mapper' import DeleteIdentifier from '@/components/dialogs/DeleteIdentifier.vue' +import ContainerService from '@/api/container.service' export default { components: { @@ -259,6 +285,7 @@ export default { return { loading: false, loadingDelete: false, + loadingStart: false, editDialog: false, deleteDialog: false, persistDialog: false, @@ -330,6 +357,12 @@ export default { container_internal_name () { return this.database.container.internal_name }, + container_state () { + return this.database.container.state + }, + container_ip () { + return this.database.container.ip_address + }, showIdentifierCard () { if (this.hasIdentifier) { return true @@ -410,6 +443,15 @@ export default { return false } return this.database.owner.username === this.user.username + }, + needsStart () { + if (!this.user) { + return false + } + if (this.database.container.owner.username !== this.user.username) { + return false + } + return !this.database.container.running } }, methods: { @@ -425,6 +467,18 @@ export default { await this.$store.dispatch('reloadDatabase') } this.deleteDialog = false + }, + startContainer () { + this.loadingStart = true + return new Promise(() => { + ContainerService.modify(this.database.container.id, 'start') + .then(() => { + this.$store.dispatch('reloadDatabase') + }) + .finally(() => { + this.loadingStart = false + }) + }) } } } diff --git a/dbrepo-ui/utils/index.js b/dbrepo-ui/utils/index.js index 0afe772e100d5ef8bceee76e3e712924a2133aee..f7b9f553b55076aa9a5846993a4ffc3376386299 100644 --- a/dbrepo-ui/utils/index.js +++ b/dbrepo-ui/utils/index.js @@ -118,6 +118,13 @@ function isActiveMessage (message) { return false } +function timestampToTimeZonedTimestamp (str) { + if (str === null) { + return null + } + return format(new Date(str), 'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'') +} + module.exports = { notEmpty, formatTimestamp, @@ -129,5 +136,6 @@ module.exports = { formatMonthUTC, formatDayUTC, isOrcid, - isActiveMessage + isActiveMessage, + timestampToTimeZonedTimestamp }