diff --git a/dbrepo-metadata-db/1_setup-schema.sql b/dbrepo-metadata-db/1_setup-schema.sql index 62dc5c3095529d74349a88725724c02c95c15710..f83d7d68964c5400a14542683f6866e69a02362b 100644 --- a/dbrepo-metadata-db/1_setup-schema.sql +++ b/dbrepo-metadata-db/1_setup-schema.sql @@ -64,6 +64,7 @@ CREATE TABLE IF NOT EXISTS `mdb_containers` last_modified timestamp, privileged_username character varying(255) NOT NULL, privileged_password character varying(255) NOT NULL, + quota integer NOT NULL DEFAULT 50, PRIMARY KEY (id), FOREIGN KEY (image_id) REFERENCES mdb_images (id) ) WITH SYSTEM VERSIONING; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java index aa3b1ad91ff5fb8608885a2a41a3f2656969dd66..7acd4fc3ce7d225eb38de8a369dc34c60d185c49 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java @@ -40,8 +40,12 @@ public class ContainerBriefDto { private ImageBriefDto image; @NotNull - @Schema(example = "true") - private Boolean running; + @Schema(example = "50") + private Integer quota; + + @NotNull + @Schema(example = "10") + private Integer count; @NotNull @Schema(example = "2021-03-12T15:26:21Z") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java index d7c6727be71331d51e967d04d008f7b7ba70ad99..7e46b80c1cfc745b158f6bffffd02bdd617a1987 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java @@ -54,6 +54,14 @@ public class ContainerDto { @NotNull private ImageDto image; + @NotNull + @Schema(example = "50") + private Long quota; + + @NotNull + @Schema(example = "10") + private Long count; + @NotNull @Schema(example = "2021-03-12T15:26:21Z") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/Container.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/Container.java index 302046d035813aa42f43fe211bcafb6a4ca18eed..7545260ce1a83f2a39b7900e9b29de5e0a4bedde 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/Container.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/Container.java @@ -55,6 +55,9 @@ public class Container { @Column private Integer uiPort; + @Column(nullable = false, columnDefinition = "INT DEFAULT 50") + private Integer quota = 50; + @Column private String uiAdditionalFlags; diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/ContainerQuotaException.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/ContainerQuotaException.java new file mode 100644 index 0000000000000000000000000000000000000000..6679775f00473632a2e2e28cd17f3d3303edd0a6 --- /dev/null +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/ContainerQuotaException.java @@ -0,0 +1,21 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.LOCKED, reason = "error.container.quota") +public class ContainerQuotaException extends Exception { + + public ContainerQuotaException(String message) { + super(message); + } + + public ContainerQuotaException(String message, Throwable thr) { + super(message, thr); + } + + public ContainerQuotaException(Throwable thr) { + super(thr); + } + +} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java index fe267fe35ab4c1b7f2cd7e658baa4b765d4d28e2..68c3a370b41c301132671d52ed79310e920aaf81 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java @@ -104,7 +104,8 @@ public interface MetadataMapper { ContainerDto containerToContainerDto(Container data); @Mappings({ - @Mapping(target = "id", source = "id") + @Mapping(target = "id", source = "id"), + @Mapping(target = "count", expression = "java(data.getDatabases().size())"), }) ContainerBriefDto containerToDatabaseContainerBriefDto(Container data); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java index 294d471e8fefe4ccafcf06f5aac47e23fce6f757..3c6967b22c08f06fb368579ba7577c17edd1686b 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java @@ -27,7 +27,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 8be62ea5c400719b31764b0b931a329481b75018..e89a38b6f6fbc9a021bc62b5a31be44fa0d86040 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -2,6 +2,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.*; import at.tuwien.api.error.ApiErrorDto; +import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.user.User; @@ -45,15 +46,18 @@ public class DatabaseEndpoint { private final MetadataMapper databaseMapper; private final StorageService storageService; private final DatabaseService databaseService; + private final ContainerService containerService; @Autowired public DatabaseEndpoint(UserService userService, AccessService accessService, MetadataMapper databaseMapper, - StorageService storageService, DatabaseService databaseService) { + StorageService storageService, DatabaseService databaseService, + ContainerService containerService) { this.userService = userService; this.accessService = accessService; this.databaseMapper = databaseMapper; this.storageService = storageService; this.databaseService = databaseService; + this.containerService = containerService; } @RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}) @@ -127,6 +131,11 @@ public class DatabaseEndpoint { content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), + @ApiResponse(responseCode = "423", + description = "Database quota exceeded", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), @ApiResponse(responseCode = "502", description = "Connection to search service failed", content = {@Content( @@ -141,10 +150,15 @@ public class DatabaseEndpoint { public ResponseEntity<DatabaseDto> create(@Valid @RequestBody DatabaseCreateDto data, @NotNull Principal principal) throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, - SearchServiceException, SearchServiceConnectionException { + SearchServiceException, SearchServiceConnectionException, ContainerQuotaException { log.debug("endpoint create database, data.name={}", data.getName()); + final Container container = containerService.find(data.getCid()); + if (container.getDatabases().size() + 1 > container.getQuota()) { + log.error("Failed to create database: quota of {} exceeded", container.getQuota()); + throw new ContainerQuotaException("Failed to create database: quota of " + container.getQuota() + " exceeded"); + } final User user = userService.findByUsername(principal.getName()); - final Database database = databaseService.create(data, user); + final Database database = databaseService.create(container, data, user); final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java index f6764895556ac07fe0321ae3e4a580f17e873d54..bf655f071abee0fdd5fc390b76402e3165d04c37 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java @@ -79,6 +79,13 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return generic_handle(e.getClass(), e.getLocalizedMessage()); } + @Hidden + @ResponseStatus(code = HttpStatus.LOCKED) + @ExceptionHandler(ContainerQuotaException.class) + public ResponseEntity<ApiErrorDto> handle(ContainerQuotaException e) { + return generic_handle(e.getClass(), e.getLocalizedMessage()); + } + @Hidden @ResponseStatus(code = HttpStatus.FORBIDDEN) @ExceptionHandler(CredentialsInvalidException.class) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java index 02ba52ecaa930f1747ee55f80cff2ea36379ffc1..15cdb1b03b2c8096377af9b0f206e70d66d87bd5 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java @@ -97,7 +97,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { public void create_succeeds() throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException, AuthServiceException, AuthServiceConnectionException, - CredentialsInvalidException, BrokerServiceException, BrokerServiceConnectionException { + CredentialsInvalidException, BrokerServiceException, BrokerServiceConnectionException, ContainerQuotaException { final DatabaseCreateDto request = DatabaseCreateDto.builder() .cid(CONTAINER_1_ID) .name(DATABASE_1_NAME) @@ -107,7 +107,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(containerService.find(CONTAINER_1_ID)) .thenReturn(CONTAINER_1); - when(databaseService.create(request, USER_1)) + when(databaseService.create(CONTAINER_1, request, USER_1)) .thenReturn(DATABASE_1); doNothing() .when(messageQueueService) @@ -433,7 +433,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { public void create_generic(DatabaseCreateDto data, Principal principal, User user) throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException, - BrokerServiceException, BrokerServiceConnectionException { + BrokerServiceException, BrokerServiceConnectionException, ContainerQuotaException { /* mock */ doNothing() diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java index ea58ae16e4ac35931bb5f713b197bacdc37b6958..f8d2b7dfb96e4d7d3b1bc14ab2fd2699d2be05a2 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java @@ -105,7 +105,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { /* test */ assertThrows(ContainerNotFoundException.class, () -> { - databaseService.create(request, USER_1); + databaseService.create(CONTAINER_1, request, USER_1); }); } @@ -315,7 +315,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { .thenReturn(database); /* test */ - final Database response = databaseService.create(createDto, USER_1); + final Database response = databaseService.create(CONTAINER_1, createDto, USER_1); assertEquals(database.getName(), response.getName()); assertEquals(database.getIsPublic(), response.getIsPublic()); assertTrue(response.getInternalName().startsWith(database.getInternalName())); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java index aa25ee1362f825ddf060ce9c90dde9e4c965b6bf..d7c036049b84cfe9d028fa60fa532aa6ba00dc75 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java @@ -2,6 +2,7 @@ package at.tuwien.service; import at.tuwien.api.database.DatabaseCreateDto; import at.tuwien.api.database.DatabaseModifyVisibilityDto; +import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; import at.tuwien.entities.user.User; import at.tuwien.exception.*; @@ -47,15 +48,17 @@ public interface DatabaseService { /** * Creates a new database with minimal metadata in the metadata database and creates a new database on the container. * + * @param container The container. * @param createDto The metadata. * @param user The user. * @return The database, if successful. - * @throws UserNotFoundException If the container/user was not found in the metadata database. + * @throws UserNotFoundException If the container/user was not found in the metadata database. * @throws DataServiceException If the data service returned non-successfully. * @throws DataServiceConnectionException If failing to connect to the data service/search service. */ - Database create(DatabaseCreateDto createDto, User user) throws UserNotFoundException, ContainerNotFoundException, - DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException; + Database create(Container container, DatabaseCreateDto createDto, User user) throws UserNotFoundException, + ContainerNotFoundException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, + SearchServiceException, SearchServiceConnectionException; /** * Updates the user's password. @@ -73,7 +76,7 @@ public interface DatabaseService { * @param database The database. * @param data The visibility * @return The database, if successful. - * @throws NotFoundException The database was not found in the metadata database. + * @throws NotFoundException The database was not found in the metadata database. * @throws DataServiceConnectionException If failing to connect to the search service. */ Database modifyVisibility(Database database, DatabaseModifyVisibilityDto data) throws DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java index 8c835864db5ed4bf69bd2bd6b29adfe4d409899d..7342f5bd2dcbe50ef8b0dcfafde46abd3ff8fdc6 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java @@ -84,10 +84,9 @@ public class DatabaseServiceImpl implements DatabaseService { @Override @Transactional - public Database create(DatabaseCreateDto data, User user) throws UserNotFoundException, + public Database create(Container container, DatabaseCreateDto data, User user) throws UserNotFoundException, ContainerNotFoundException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException { - final Container container = containerService.find(data.getCid()); Database database = Database.builder() .isPublic(data.getIsPublic()) .name(data.getName()) diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java index dfaf58d995e8e57ef8fcc017c551ff0720bc8a4a..a6b227cf99cf8e516e7d337700a80fff6f8d6bc4 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java @@ -1056,7 +1056,8 @@ public abstract class BaseTest { public final static String CONTAINER_1_UI_HOST = "localhost"; public final static Integer CONTAINER_1_UI_PORT = 3306; public final static String CONTAINER_1_UI_ADDITIONAL_FLAGS = "?sslMode=disable"; - public final static Boolean CONTAINER_1_RUNNING = true; + public final static Integer CONTAINER_1_QUOTA = 4; + public final static Integer CONTAINER_1_COUNT = 3; public final static String CONTAINER_1_HOST = "localhost"; public final static Integer CONTAINER_1_PORT = 3308; public final static String CONTAINER_1_SIDECAR_HOST = "localhost"; @@ -1097,7 +1098,8 @@ public abstract class BaseTest { .name(CONTAINER_1_NAME) .internalName(CONTAINER_1_INTERNALNAME) .created(CONTAINER_1_CREATED) - .running(CONTAINER_1_RUNNING) + .quota(CONTAINER_1_QUOTA) + .count(CONTAINER_1_COUNT) .build(); public final static PrivilegedContainerDto CONTAINER_1_PRIVILEGED_DTO = PrivilegedContainerDto.builder() @@ -1124,7 +1126,8 @@ public abstract class BaseTest { public final static Integer CONTAINER_2_PORT = 3309; public final static String CONTAINER_2_SIDECAR_HOST = "localhost"; public final static Integer CONTAINER_2_SIDECAR_PORT = 33091; - public final static Boolean CONTAINER_2_RUNNING = true; + public final static Integer CONTAINER_2_QUOTA = 3; + public final static Integer CONTAINER_2_COUNT = 3; public final static String CONTAINER_2_PRIVILEGED_USERNAME = "root"; public final static String CONTAINER_2_PRIVILEGED_PASSWORD = "dbrepo"; public final static Instant CONTAINER_2_CREATED = Instant.ofEpochSecond(1677399655L) /* 2023-02-26 08:20:55 (UTC) */; @@ -1156,7 +1159,7 @@ public abstract class BaseTest { .name(CONTAINER_2_NAME) .internalName(CONTAINER_2_INTERNALNAME) .created(CONTAINER_2_CREATED) - .running(CONTAINER_2_RUNNING) + .quota(CONTAINER_2_QUOTA) .build(); public final static Long CONTAINER_3_ID = 3L; @@ -2638,8 +2641,6 @@ public abstract class BaseTest { public final static Long QUERY_2_ID = 2L; public final static String QUERY_2_STATEMENT = "SELECT `location` FROM `weather_aus`"; public final static String QUERY_2_QUERY_HASH = "a2d2dd94ebc7653bb5a3b55dd8ed5e91d3d13c225c6855a1eb4eb7ca14c36ced"; - public final static Long QUERY_2_CONTAINER_ID = CONTAINER_2_ID; - public final static Long QUERY_2_DATABASE_ID = DATABASE_2_ID; public final static Long QUERY_2_RESULT_NUMBER = 2L; public final static String QUERY_2_RESULT_HASH = "ff3f7cbe1b96d296957f6e39e55b8b1b577fa3d205d4795af99594cfd20cb80d"; public final static Instant QUERY_2_CREATED = Instant.now().minus(2, MINUTES); @@ -2651,7 +2652,7 @@ public abstract class BaseTest { public final static QueryDto QUERY_2_DTO = QueryDto.builder() .id(QUERY_2_ID) - .databaseId(QUERY_2_DATABASE_ID) + .databaseId(DATABASE_2_ID) .query(QUERY_2_STATEMENT) .queryNormalized(QUERY_2_STATEMENT) .resultNumber(QUERY_2_RESULT_NUMBER) @@ -2817,8 +2818,6 @@ public abstract class BaseTest { public final static Long QUERY_6_ID = 6L; public final static String QUERY_6_STATEMENT = "SELECT `location` FROM `weather_aus` WHERE `id` = 1"; public final static String QUERY_6_QUERY_HASH = "6d6dc48b12cdfd959d39a62887334a6bbd529b93eed4f211f3f671bd9e7d6225"; - public final static Long QUERY_6_CONTAINER_ID = CONTAINER_2_ID; - public final static Long QUERY_6_DATABASE_ID = DATABASE_2_ID; public final static String QUERY_6_RESULT_HASH = "ff5f7cbe1b96d596957f6e59e55b8b1b577fa5d505d5795af99595cfd50cb80d"; public final static Instant QUERY_6_CREATED = Instant.now().minus(5, MINUTES); public final static Instant QUERY_6_EXECUTION = Instant.now().minus(1, MINUTES); @@ -2830,7 +2829,7 @@ public abstract class BaseTest { public final static QueryDto QUERY_6_DTO = QueryDto.builder() .id(QUERY_6_ID) - .databaseId(QUERY_6_DATABASE_ID) + .databaseId(DATABASE_2_ID) .query(QUERY_6_STATEMENT) .queryNormalized(QUERY_6_STATEMENT) .resultNumber(QUERY_6_RESULT_NUMBER) diff --git a/dbrepo-ui/components/container/ContainerCard.vue b/dbrepo-ui/components/container/ContainerCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..f10e6c27834fcea0d82e0a39440bb7bea9ea78e7 --- /dev/null +++ b/dbrepo-ui/components/container/ContainerCard.vue @@ -0,0 +1,53 @@ +<template> + <v-card + v-if="container" + variant="flat" + rounded="0"> + <v-divider class="mx-4" /> + <v-card-title + v-text="container.name" /> + <v-card-subtitle>Container</v-card-subtitle> + <v-card-text> + <v-progress-linear + v-model="utilization" + :color="colorVariant" + height="20" + class="font-small"> + <template v-slot:default> + {{ container.count }} / {{ container.quota }} + </template> + </v-progress-linear> + </v-card-text> + </v-card> +</template> + +<script> +export default { + data() { + return { + loading: false + } + }, + props: { + container: { + default: () => { + return null + } + } + }, + computed: { + utilization () { + return this.container.count * 100.0 / this.container.quota + }, + colorVariant () { + return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary') + }, + isContrastTheme () { + return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') + }, + isDarkTheme () { + return this.$vuetify.theme.global.name.toLowerCase().startsWith('dark') + } + } +} +</script> diff --git a/dbrepo-ui/components/container/ContainerList.vue b/dbrepo-ui/components/container/ContainerList.vue new file mode 100644 index 0000000000000000000000000000000000000000..567ee4eec4fdb361157a8dd8d7d0d3e0f6e77d7b --- /dev/null +++ b/dbrepo-ui/components/container/ContainerList.vue @@ -0,0 +1,42 @@ +<template> + <div> + <v-card + variant="flat" + rounded="0"> + <v-list-item + v-if="loading" + lines="two"> + <Loading /> + </v-list-item> + </v-card> + <ContainerCard + v-for="(container, idx) in containers" + :container="container" + :key="idx"/> + </div> +</template> + +<script> +import ContainerCard from '@/components/container/ContainerCard.vue' + +export default { + components: { + ContainerCard + }, + props: { + containers: { + type: Array, + default: () => { + return [] + } + }, + loading: { + type: Boolean, + default: () => { + return true + } + } + } +} +</script> + diff --git a/dbrepo-ui/components/database/DatabaseCreate.vue b/dbrepo-ui/components/database/DatabaseCreate.vue index 275b7198311f6edc62aeefb07311e2d01fea771c..1ae15aa766b57bd7fa3c3fb81f1669cfeac59259 100644 --- a/dbrepo-ui/components/database/DatabaseCreate.vue +++ b/dbrepo-ui/components/database/DatabaseCreate.vue @@ -106,7 +106,7 @@ export default { this.loadingContainers = true containerService.findAll() .then((containers) => { - this.engines = containers + this.engines = containers.filter(c => c.count < c.quota) if (this.engines.length > 0) { this.engine = this.engines[0] } diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index fb4c40266b6dcc1f6f15a340cd400b8d4a0b3cd4..2c11a99e78fca08bec4b0b67f4724419fcc5403d 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -35,6 +35,11 @@ to="/semantic" prepend-icon="mdi-share-variant" :title="$t('navigation.semantics')" /> + <v-list-item + v-if="canListContainers" + to="/container" + prepend-icon="mdi-docker" + :title="$t('navigation.container')" /> </v-list> <template v-slot:append> <v-alert @@ -216,6 +221,12 @@ export default { } return this.roles.includes('list-ontologies') }, + canListContainers () { + if (!this.roles) { + return false + } + return this.roles.includes('list-containers') + }, logo () { return this.$config.public.logo }, diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index c07ee7ac48b99bcd3ce0a9055fc1846c686c5789..809a65289ab072294aaa50b5b0fea6f93158cd14 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -2,6 +2,7 @@ "navigation": { "information": "Information", "search": "Search", + "container": "Containers", "ontologies": "Ontologies", "logout": "Logout", "login": "Login", @@ -1033,7 +1034,7 @@ } }, "container": { - "title": "Container", + "title": "Containers", "name": { "title": "Name" }, diff --git a/dbrepo-ui/pages/container/index.vue b/dbrepo-ui/pages/container/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..360ce1543fdf086d60d568ed066ccca1f7c04537 --- /dev/null +++ b/dbrepo-ui/pages/container/index.vue @@ -0,0 +1,43 @@ +<template> + <div> + <v-toolbar + flat + :title="$t('pages.container.title')"> + </v-toolbar> + <ContainerList + v-cloak + :loading="loading" + :containers="containers" /> + </div> +</template> + +<script> +import ContainerList from '@/components/container/ContainerList.vue' + +export default { + components: { + ContainerList + }, + data () { + return { + loading: true, + dialog: null, + containers: [] + } + }, + computed: { + roles () { + return this.userStore.getRoles + }, + }, + mounted () { + this.loading = true + const containerService = useContainerService(); + containerService.findAll() + .then((containers) => { + this.containers = containers + this.loading = false + }) + } +} +</script>