diff --git a/docker-compose.yml b/docker-compose.yml index dd2b86173c43123f9ad46d7b3c1d83597d8460f7..adc2fca0ef258bf4f6bba3fd010bd86c2fb42f13 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,16 +11,12 @@ networks: ipam: config: - subnet: 172.29.0.0/16 - driver_opts: - com.docker.network.driver.mtu: 1450 fda-userdb: name: fda-userdb driver: bridge ipam: config: - subnet: 172.28.0.0/16 - driver_opts: - com.docker.network.driver.mtu: 1450 services: @@ -120,7 +116,7 @@ services: fda-public: ipv4_address: 172.29.0.8 environment: - SPRING_PROFILES_ACTIVE: docker,seeder + SPRING_PROFILES_ACTIVE: docker TZ: Europe/Vienna ports: - "9091:9091" diff --git a/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java index d9dda2af81deba78cb503dfa018b4d4fe7c0128c..1b18db40c67a56bc2752afef7ab38ed0e7bb8c38 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java @@ -1,6 +1,6 @@ package at.tuwien.config; -import at.tuwien.seeder.impl.SeederImpl; +import at.tuwien.seeder.Seeder; import com.google.common.io.Files; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; @@ -8,9 +8,11 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; import java.io.File; import java.io.IOException; +import java.util.Arrays; @Log4j2 @Configuration @@ -19,8 +21,16 @@ public class ReadyConfig { @Value("${fda.ready.path}") private String readyPath; + private final Seeder seederImpl; + + @Autowired + public ReadyConfig(Seeder seederImpl) { + this.seederImpl = seederImpl; + } + @EventListener(ApplicationReadyEvent.class) public void init() throws IOException { + seederImpl.seed(); Files.touch(new File(readyPath)); } diff --git a/fda-container-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java b/fda-container-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java index 9869b170fd2df47f128526fcbd9f0cd0e72924b0..f317cf48cd7cf262aeff7233108d540a9b1b314e 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java +++ b/fda-container-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java @@ -2,8 +2,6 @@ package at.tuwien.seeder.impl; import at.tuwien.seeder.Seeder; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.event.EventListener; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @@ -25,7 +23,6 @@ public class SeederImpl implements Seeder { } @Override - @EventListener(ApplicationReadyEvent.class) public void seed() { imageSeederImpl.seed(); if (Arrays.asList(environment.getActiveProfiles()).contains("seeder")) { diff --git a/fda-database-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-database-service/services/src/main/java/at/tuwien/config/ReadyConfig.java index 0bee3b961edd4ca456f0243c8eede630a4a54716..b1838b972d91c254f2341429e13f3cf4d2b95c24 100644 --- a/fda-database-service/services/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-database-service/services/src/main/java/at/tuwien/config/ReadyConfig.java @@ -1,13 +1,17 @@ package at.tuwien.config; +import at.tuwien.seeder.Seeder; import com.google.common.io.Files; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; import java.io.File; import java.io.IOException; +import java.util.Arrays; @Configuration public class ReadyConfig { @@ -15,8 +19,20 @@ public class ReadyConfig { @Value("${fda.ready.path}") private String readyPath; + private final Seeder seederImpl; + private final Environment environment; + + @Autowired + public ReadyConfig(Seeder seederImpl, Environment environment) { + this.seederImpl = seederImpl; + this.environment = environment; + } + @EventListener(ApplicationReadyEvent.class) public void init() throws IOException { + if (Arrays.asList(environment.getActiveProfiles()).contains("seeder")) { + seederImpl.seed(); + } Files.touch(new File(readyPath)); } diff --git a/fda-database-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java b/fda-database-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java index 3c116262809e6b5295904c4ea500c787612fc9a6..66f117d178c1f003d6d9dd7accfae8a1cc281be8 100644 --- a/fda-database-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java +++ b/fda-database-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java @@ -2,14 +2,11 @@ package at.tuwien.seeder.impl; import at.tuwien.seeder.Seeder; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; -import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; @Slf4j -@Profile("seeder") @Service public class SeederImpl implements Seeder { @@ -20,7 +17,6 @@ public class SeederImpl implements Seeder { } @Override - @EventListener(ApplicationReadyEvent.class) public void seed() { databaseSeederImpl.seed(); } diff --git a/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java index d41e15dd48980a44c36f406f6d8532c70fedcdf5..fc37ae6185c72a42c344f0a04875056b30385a0a 100644 --- a/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java +++ b/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java @@ -45,7 +45,7 @@ public class IdentifierEndpoint { }) public ResponseEntity<List<IdentifierDto>> findAll(@NotNull @PathVariable("id") Long id, @NotNull @PathVariable("databaseId") Long databaseId, - @RequestParam Long qid) + @RequestParam(required = false) Long qid) throws IdentifierNotFoundException { if (qid != null) { final Identifier identifier = identifierService.find(id, databaseId, qid); diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-identifier-service/services/src/main/java/at/tuwien/config/ReadyConfig.java index 1c52a4bc38d502512281920cd32b223aa5c5bf4e..1cbc678807ec69dc46d0cb2d38f61f77d5045464 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/config/ReadyConfig.java @@ -1,16 +1,18 @@ package at.tuwien.config; +import at.tuwien.seeder.Seeder; import com.google.common.io.Files; -import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; import javax.validation.constraints.NotNull; import java.io.File; import java.io.IOException; +import java.util.Arrays; @Configuration public class ReadyConfig { @@ -19,8 +21,20 @@ public class ReadyConfig { @Value("${fda.ready.path}") private String readyPath; + private final Seeder seederImpl; + private final Environment environment; + + @Autowired + public ReadyConfig(Seeder seederImpl, Environment environment) { + this.seederImpl = seederImpl; + this.environment = environment; + } + @EventListener(ApplicationReadyEvent.class) public void init() throws IOException { + if (Arrays.asList(this.environment.getActiveProfiles()).contains("seeder")) { + seederImpl.seed(); + } Files.touch(new File(readyPath)); } diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java b/fda-identifier-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java index 714c103dbd62ef27a194da12a7794167a0524b55..d7d1e29fce3d3a60d458a90f33a362cfe3dab475 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java @@ -2,25 +2,22 @@ package at.tuwien.seeder.impl; import at.tuwien.seeder.Seeder; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.annotation.Profile; -import org.springframework.context.event.EventListener; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j -@Profile("seeder") @Service public class SeederImpl implements Seeder { - private final Seeder databaseSeederImpl; + private final Seeder identifierSeederImpl; - public SeederImpl(Seeder databaseSeederImpl) { - this.databaseSeederImpl = databaseSeederImpl; + @Autowired + public SeederImpl(Seeder identifierSeederImpl) { + this.identifierSeederImpl = identifierSeederImpl; } @Override - @EventListener(ApplicationReadyEvent.class) public void seed() { - databaseSeederImpl.seed(); + identifierSeederImpl.seed(); } } diff --git a/fda-query-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-query-service/services/src/main/java/at/tuwien/config/ReadyConfig.java index 0bee3b961edd4ca456f0243c8eede630a4a54716..282e94554f2fa2cab5834a8c24dbfa9c80761986 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-query-service/services/src/main/java/at/tuwien/config/ReadyConfig.java @@ -1,13 +1,17 @@ package at.tuwien.config; +import at.tuwien.seeder.Seeder; import com.google.common.io.Files; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; import java.io.File; import java.io.IOException; +import java.util.Arrays; @Configuration public class ReadyConfig { @@ -15,8 +19,20 @@ public class ReadyConfig { @Value("${fda.ready.path}") private String readyPath; + private final Seeder seederImpl; + private final Environment environment; + + @Autowired + public ReadyConfig(Seeder seederImpl, Environment environment) { + this.seederImpl = seederImpl; + this.environment = environment; + } + @EventListener(ApplicationReadyEvent.class) public void init() throws IOException { + if (Arrays.asList(this.environment.getActiveProfiles()).contains("seeder")) { + seederImpl.seed(); + } Files.touch(new File(readyPath)); } diff --git a/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/AbstractSeeder.java b/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/AbstractSeeder.java index d8825ef426b0add9b0ae5f6e779a4733126a8219..e2c7f89189a01bb15e5a7c5ce9c9c7ee83da0cb6 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/AbstractSeeder.java +++ b/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/AbstractSeeder.java @@ -1,5 +1,6 @@ package at.tuwien.seeder.impl; +import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.query.SaveStatementDto; public abstract class AbstractSeeder { @@ -9,12 +10,24 @@ public abstract class AbstractSeeder { public final static Long DATABASE_1_ID = 1L; public final static Long TABLE_1_ID = 1L; + public final static String TABLE_1_LOCATION = "/tmp/traffic.csv"; public final static Long QUERY_1_ID = 1L; public final static String QUERY_1_STATEMENT = "select `linie`,`betriebsdatum`,`fahrzeug`,`fahrt_id` from `fahrzeiten_soll-ist_2017`"; + public final static ImportDto QUERY_1_IMPORT_DTO = ImportDto.builder() + .location(TABLE_1_LOCATION) + .build(); + + public final static Long QUERY_2_ID = 2L; + public final static String QUERY_2_STATEMENT = "select `linie`, count(`id`) from `fahrzeiten_soll-ist_2017` group by `linie`"; + public final static SaveStatementDto QUERY_1_SAVE_DTO = SaveStatementDto.builder() .statement(QUERY_1_STATEMENT) .build(); + public final static SaveStatementDto QUERY_2_SAVE_DTO = SaveStatementDto.builder() + .statement(QUERY_2_STATEMENT) + .build(); + } diff --git a/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/QuerySeederImpl.java b/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/QuerySeederImpl.java index 98479436783c42a1346781bcd407c4afac98cadd..46bd1c7eaff06b5bd8f18a6c8133fb037ccc74cd 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/QuerySeederImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/QuerySeederImpl.java @@ -29,8 +29,12 @@ public class QuerySeederImpl extends AbstractSeeder implements Seeder { log.warn("Already seeded. Skip."); return; } + final Integer import1 = queryService.insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, QUERY_1_IMPORT_DTO); + log.info("Imported {} rows into table id {}", import1, TABLE_1_ID); final Query query1 = storeService.insert(CONTAINER_1_ID, DATABASE_1_ID, null, QUERY_1_SAVE_DTO); log.info("Saved query id {}", query1.getId()); + final Query query2 = storeService.insert(CONTAINER_1_ID, DATABASE_1_ID, null, QUERY_2_SAVE_DTO); + log.info("Saved query id {}", query2.getId()); } } diff --git a/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java b/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java index 714c103dbd62ef27a194da12a7794167a0524b55..ae7eda709738b9fca0c3acfb2b6aa41db2e818bd 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/seeder/impl/SeederImpl.java @@ -2,25 +2,20 @@ package at.tuwien.seeder.impl; import at.tuwien.seeder.Seeder; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.annotation.Profile; -import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; @Slf4j -@Profile("seeder") @Service public class SeederImpl implements Seeder { - private final Seeder databaseSeederImpl; + private final Seeder querySeederImpl; - public SeederImpl(Seeder databaseSeederImpl) { - this.databaseSeederImpl = databaseSeederImpl; + public SeederImpl(Seeder querySeederImpl) { + this.querySeederImpl = querySeederImpl; } @Override - @EventListener(ApplicationReadyEvent.class) public void seed() { - databaseSeederImpl.seed(); + querySeederImpl.seed(); } } diff --git a/fda-ui/components/DBToolbar.vue b/fda-ui/components/DBToolbar.vue index ea1e9bc84e5145f80b75add94ffbdb8caa28aa9a..fb55105b4b6ab0c091c71f9521a5899e0f9d4b4f 100644 --- a/fda-ui/components/DBToolbar.vue +++ b/fda-ui/components/DBToolbar.vue @@ -8,13 +8,13 @@ </v-toolbar-title> <v-spacer /> <v-toolbar-title> - <v-btn :to="`/container/${$route.params.container_id}/database/${databaseId}/table/import`" class="mr-2"> + <v-btn class="mr-2" :disabled="!token" :to="`/container/${$route.params.container_id}/database/${databaseId}/table/import`"> <v-icon left>mdi-cloud-upload</v-icon> Import CSV </v-btn> - <v-btn color="blue-grey" :to="`/container/${$route.params.container_id}/database/${databaseId}/query/create`" class="mr-2 white--text"> + <v-btn color="blue-grey" class="mr-2 white--text" :disabled="!token" :to="`/container/${$route.params.container_id}/database/${databaseId}/query/create`"> <v-icon left>mdi-wrench</v-icon> Query Builder </v-btn> - <v-btn color="primary" :to="`/container/${$route.params.container_id}/database/${databaseId}/table/create`"> + <v-btn color="primary" :disabled="!token" :to="`/container/${$route.params.container_id}/database/${databaseId}/table/create`"> <v-icon left>mdi-table-large-plus</v-icon> Create Table </v-btn> </v-toolbar-title> @@ -29,9 +29,9 @@ <v-tab :to="`/container/${$route.params.container_id}/database/${databaseId}/query`"> Queries </v-tab> - <v-tab :to="`/container/${$route.params.container_id}/database/${databaseId}/admin`"> - Admin - </v-tab> +<!-- <v-tab :to="`/container/${$route.params.container_id}/database/${databaseId}/admin`">--> +<!-- Admin--> +<!-- </v-tab>--> </v-tabs> </template> </v-toolbar> @@ -56,6 +56,9 @@ export default { }, loadingColor () { return 'primary' + }, + token () { + return this.$store.state.token } }, mounted () { diff --git a/fda-ui/components/QueryList.vue b/fda-ui/components/QueryList.vue index 0c1899892b355e4745512079fbb4b97fcc3620ff..c87533941fc5dab2a6e12f397667d654823ca022 100644 --- a/fda-ui/components/QueryList.vue +++ b/fda-ui/components/QueryList.vue @@ -10,7 +10,7 @@ <v-expansion-panels v-if="!loading && queries.length > 0" accordion> <v-expansion-panel v-for="(item, i) in queries" :key="i" @click="details(item)"> <v-expansion-panel-header> - Query {{ item.id }} + <span v-bind:class="{'font-weight-black': item.identifier !== undefined}">{{ title(item) }}</span> </v-expansion-panel-header> <v-expansion-panel-content> <v-row dense> @@ -22,18 +22,30 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - ID: {{ queryDetails.id }} + ID </v-list-item-title> + <v-list-item-content> + {{ queryDetails.id }} + </v-list-item-content> + <v-list-item-title v-if="queryDetails.identifier !== undefined"> + Persistent ID + </v-list-item-title> + <v-list-item-content v-if="queryDetails.identifier !== undefined"> + https://dbrepo.ossdip.at/pid/{{ queryDetails.identifier.id }} + </v-list-item-content> </v-list-item-content> </v-list-item> - <v-list-item> + <v-list-item v-if="queryDetails.identifier !== undefined"> <v-list-item-icon> - <v-icon>mdi-table-of-contents</v-icon> + <v-icon>mdi-text</v-icon> </v-list-item-icon> <v-list-item-content> <v-list-item-title> - Result Number: <code>{{ queryDetails.result_number }}</code> + Description </v-list-item-title> + <v-list-item-content> + {{ queryDetails.identifier.description }} + </v-list-item-content> </v-list-item-content> </v-list-item> <v-list-item> @@ -42,8 +54,11 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - Execution Timestamp: <code>{{ queryDetails.execution }}</code> + Execution Timestamp </v-list-item-title> + <v-list-item-content> + {{ queryDetails.execution }} + </v-list-item-content> </v-list-item-content> </v-list-item> <v-list-item> @@ -52,8 +67,11 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - Query: <code>{{ queryDetails.query }}</code> + Query </v-list-item-title> + <v-list-item-content> + <code>{{ queryDetails.query }}</code> + </v-list-item-content> </v-list-item-content> </v-list-item> </v-list> @@ -79,6 +97,7 @@ export default { return { loading: false, queries: [], + identifiers: [], queryDetails: { id: null, doi: null, @@ -106,11 +125,32 @@ export default { res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/query`) this.queries = res.data console.debug('queries', this.queries) + try { + this.loading = true + const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/identifier`) + this.identifiers = res.data + console.debug('identifiers', this.identifiers) + this.queries.forEach((query) => { + const id = this.identifiers.find(id => id.qid === query.id) + console.debug('id', id) + if (id !== undefined) { + query.identifier = id + } + }) + } catch (err) { + console.error('Failed to get identifiers', err) + } this.loading = false } catch (err) { this.$toast.error('Could not list queries.') } }, + title (query) { + if (query.identifier !== undefined) { + return query.identifier.title + } + return `Query ${query.id}` + }, details (query) { this.queryDetails = query } diff --git a/fda-ui/components/TableList.vue b/fda-ui/components/TableList.vue index 95887fb33dda9682bd3bc92a647b21d714c37382..baaa648456f345dffde968b1383b9103ad4e0725 100644 --- a/fda-ui/components/TableList.vue +++ b/fda-ui/components/TableList.vue @@ -21,8 +21,11 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - ID: {{ tableDetails.id }} + ID </v-list-item-title> + <v-list-item-content> + {{ tableDetails.id }} + </v-list-item-content> </v-list-item-content> </v-list-item> <v-list-item> @@ -31,8 +34,11 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - Internal Name: <code>{{ tableDetails.internal_name }}</code> + Internal Name </v-list-item-title> + <v-list-item-content> + {{ tableDetails.internal_name }} + </v-list-item-content> </v-list-item-content> </v-list-item> <v-list-item> @@ -41,8 +47,24 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - AMQP Routing Key: <code>{{ tableDetails.topic }}</code> + AMQP Routing Key </v-list-item-title> + <v-list-item-content> + {{ tableDetails.topic }} + </v-list-item-content> + </v-list-item-content> + </v-list-item> + <v-list-item> + <v-list-item-icon> + <v-icon>mdi-table</v-icon> + </v-list-item-icon> + <v-list-item-content> + <v-list-item-title> + Columns + </v-list-item-title> + <v-list-item-content> + {{ tableDetails.columns.length }} + </v-list-item-content> </v-list-item-content> </v-list-item> <v-list-item> @@ -51,8 +73,11 @@ </v-list-item-icon> <v-list-item-content> <v-list-item-title> - Description: {{ tableDetails.description }} + Description </v-list-item-title> + <v-list-item-content> + {{ tableDetails.description }} + </v-list-item-content> </v-list-item-content> </v-list-item> </v-list> @@ -134,7 +159,13 @@ export default { return { loading: false, tables: [], - tableDetails: { id: 0 }, + tableDetails: { + id: null, + internal_name: null, + description: null, + topic: null, + columns: [] + }, dialogDelete: false } }, diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue index dca090de21f0e825abb6218cb2edb0c9500b34e3..a56278bbe747652313c36565b8f676352309a9dd 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue @@ -7,10 +7,10 @@ </v-toolbar-title> <v-spacer /> <v-toolbar-title> - <v-btn class="mr-2" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/import`"> + <v-btn class="mr-2" :disabled="!token" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/import`"> <v-icon left>mdi-cloud-upload</v-icon> Import csv </v-btn> - <v-btn color="primary" :href="`/api/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/data/export`" target="_blank"> + <v-btn color="primary" :disabled="!token" :href="`/api/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/data/export`" target="_blank"> <v-icon left>mdi-download</v-icon> Download </v-btn> </v-toolbar-title> @@ -36,6 +36,7 @@ <v-data-table :headers="headers" :items="rows" + :loading="loadingData" :options.sync="options" :server-items-length="total" :footer-props="footerProps" @@ -67,7 +68,8 @@ export default { data () { return { loading: true, - total: null, + loadingData: true, + total: 0, footerProps: { 'items-per-page-options': [10, 20, 30, 40, 50] }, @@ -97,6 +99,9 @@ export default { loadingColor () { return this.error ? 'red lighten-2' : 'primary' }, + token () { + return this.$store.state.token + }, versionColor () { console.debug('version', this.version) if (this.version === null) { @@ -120,7 +125,6 @@ export default { mounted () { this.loadProperties() this.loadData() - this.loadDataCount() }, methods: { async loadProperties () { @@ -136,12 +140,12 @@ export default { }) } catch (err) { this.$toast.error('Could not get table details.') - this.loading = false } + this.loading = false }, async loadData () { try { - this.loading = true + this.loadingData = true let url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data?page=${this.options.page - 1}&size=${this.options.itemsPerPage}` if (this.version !== null) { console.info('versioning active', this.version) @@ -149,25 +153,13 @@ export default { } const res = await this.$axios.get(url) console.debug('version', this.datetime, 'table data', res.data) + this.total = res.headers['fda-count'] this.rows = res.data.result } catch (err) { console.error('failed to load data', err) this.$toast.error('Could not load table data.') } - this.loading = false - }, - loadDataCount () { // TODO - // try { - // this.loading = true - // const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data` - // const res = await this.$axios.head(url) - // console.debug('data count', res.data) - // this.total = res.data.count - // } catch (err) { - // console.error('failed to load total count', err) - // } - this.total = 1000000 - this.loading = false + this.loadingData = false }, columnAddition (column) { if (column.is_primary_key) { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/create.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/create.vue index 358667084ecdbc9fe7901e6c43468009668e445d..2ca9a7029a7b7a225faa628debde224e104923e7 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/create.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/create.vue @@ -1,5 +1,5 @@ <template> - <form id="create_table" action="/" method="post" @submit="checkForm"> + <div> <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> <v-toolbar flat> <v-toolbar-title> @@ -12,68 +12,108 @@ </v-btn> </v-toolbar-title> </v-toolbar> - <v-card flat> - <v-card-text> - <v-text-field - v-model="name" - name="name" - label="Table Name" - :rules="[v => !!v || $t('Required')]" - required /> - <v-text-field - v-model="description" - name="description" - label="Description" /> - </v-card-text> - <v-card-text> - <v-row v-for="(c, idx) in columns" :key="idx" class="row-border mt-2"> - <v-col cols="3"> - <v-text-field v-model="c.name" required label="Name" /> - </v-col> - <v-col cols="3"> - <v-select - v-model="c.type" - :items="columnTypes" - item-value="value" - required - label="Data Type" /> - </v-col> - <v-col cols="2"> - <v-checkbox - v-model="c.primary_key" - label="Primary Key" - @click="setOthers(c)" - @change="(x) => onChange(idx, x, 'primary_key')" /> - </v-col> - <v-col cols="1"> - <v-checkbox - v-model="c.unique" - label="Unique" - :disabled="c.primary_key" - @change="(x) => onChange(idx, x, 'unique')" /> - </v-col> - <v-col cols="2"> - <v-checkbox - v-model="c.null_allowed" - label="NULL Allowed" - :disabled="c.primary_key" - @change="(x) => onChange(idx, x, 'null_Allowed')" /> - </v-col> - <v-spacer /> - <v-btn title="Remove column" outlined icon @click="removeColumn(idx)"> - <v-icon>mdi-minus</v-icon> - </v-btn> - </v-row> - <v-row> - <v-col> - <v-btn @click="addColumn"> - Add Column + <v-stepper v-model="step" vertical flat> + <v-stepper-step :complete="step > 1" step="1"> + Table Information + </v-stepper-step> + + <v-stepper-content class="pt-0 pb-1" step="1"> + <v-form ref="form" v-model="valid" @submit.prevent="submit"> + <v-row dense> + <v-col cols="8"> + <v-text-field + v-model="tableCreate.name" + name="name" + label="Table Name" + :rules="[v => !!v || $t('Required')]" + required /> + </v-col> + </v-row> + <v-row dense> + <v-col cols="8"> + <v-text-field + v-model="tableCreate.description" + name="description" + label="Description" /> + </v-col> + </v-row> + <v-row dense> + <v-col cols="8"> + <v-btn :disabled="!step1Valid" color="primary" type="submit" @click="step = 2"> + Continue + </v-btn> + </v-col> + </v-row> + </v-form> + </v-stepper-content> + + <v-stepper-step :complete="step > 2" step="2"> + Table Schema + </v-stepper-step> + + <v-stepper-content class="pt-0 pb-1" step="2"> + <v-form ref="form" v-model="valid" @submit.prevent="submit"> + <div v-for="(c, idx) in tableCreate.columns" :key="idx"> + <v-row dense class="column pa-2 mb-2"> + <v-col cols="2"> + <v-text-field v-model="c.name" required label="Name" /> + </v-col> + <v-col cols="2"> + <v-select + v-model="c.type" + :items="columnTypes" + item-value="value" + required + label="Data Type" /> + </v-col> + <v-col cols="2" :hidden="c.type !== 'ENUM'"> + <v-select + v-model="c.enum_values" + :disabled="c.type !== 'ENUM'" + :menu-props="{ maxHeight: '400' }" + label="Enumeration" + multiple /> + </v-col> + <v-col cols="2" class="pl-10" :hidden="c.type !== 'DATE'"> + <v-select + v-model="c.dfid" + :disabled="c.type !== 'DATE'" + :items="dateFormats" + item-text="example" + item-value="id" /> + </v-col> + <v-col cols="auto" class="pl-10" :hidden="c.type !== 'STRING' || c.type !== 'VARCHAR'"> + <v-text-field v-model="c.check_expression" label="Check Expression" /> + </v-col> + <v-col cols="auto" class="pl-2"> + <v-checkbox v-model="c.primary_key" label="Primary Key" @click="setOthers(c)" /> + </v-col> + <v-col cols="auto" class="pl-10"> + <v-checkbox v-model="c.null_allowed" :disabled="c.primary_key" label="Null Allowed" /> + </v-col> + <v-col cols="auto" class="pl-10"> + <v-checkbox v-model="c.unique" :hidden="c.primary_key" label="Unique" /> + </v-col> + <v-col cols="auto" class="pl-10"> + <v-text-field v-model="c.foreign_key" hidden required label="Foreign Key" /> + </v-col> + <v-col cols="auto" class="pl-10"> + <v-text-field v-model="c.references" hidden required label="References" /> + </v-col> + <v-col> + <v-btn @click.stop="removeColumn(idx)">Remove</v-btn> + </v-col> + </v-row> + </div> + <div> + <v-btn class="mt-2" color="primary" :loading="loading" @click="addColumn()"> + Add </v-btn> - </v-col> - </v-row> - </v-card-text> - </v-card> - </form> + </div> + </v-form> + </v-stepper-content> + </v-stepper> + </div> </template> <script> @@ -82,8 +122,11 @@ export default { return { columns: [], name: null, + valid: false, description: null, + dateFormats: [], loading: false, + step: 1, error: false, columnTypes: [ { value: 'ENUM', text: 'ENUM' }, @@ -93,13 +136,26 @@ export default { { value: 'DATE', text: 'DATE' }, { value: 'STRING', text: 'STRING' }, { value: 'TEXT', text: 'TEXT' } - ] + ], + tableCreate: { + name: null, + description: null, + false_element: null, + true_element: null, + null_element: null, + columns: [], + separator: ',', + skip_lines: 0 + } } }, computed: { databaseId () { return this.$route.params.database_id }, + step1Valid () { + return this.tableCreate.name !== null && this.tableCreate.name.length > 0 && this.tableCreate.description !== null && this.tableCreate.description.length > 0 + }, token () { return this.$store.state.token }, @@ -107,24 +163,20 @@ export default { return this.error ? 'red lighten-2' : 'primary' }, canCreateTable () { - if (this.name === null) { return false } - if (this.description === '') { return false } - if (!this.columns.length) { return false } - for (let i = 0; i < this.columns.length; i++) { - const col = this.columns[i] - if (col.name === '') { return false } - if (col.type === '') { return false } - if (col.name === 'id' && (!col.primary_key)) { - return false - } + if (!this.step1Valid || this.step !== 2) { + return false } - return true + return this.tableCreate.columns.length >= 1 } }, mounted () { + this.loadDateFormats() this.addColumn('id', 'NUMBER', false, true, true) }, methods: { + submit () { + this.$refs.form.validate() + }, setOthers (column) { column.null_allowed = false column.unique = true @@ -132,8 +184,22 @@ export default { checkForm (e) { e.preventDefault() }, + async loadDateFormats () { + const getUrl = `/api/container/${this.$route.params.container_id}` + let getResult + try { + this.loading = true + getResult = await this.$axios.get(getUrl) + this.dateFormats = getResult.data.image.date_formats + console.debug('retrieve image date formats', this.dateFormats) + this.loading = false + } catch (err) { + this.loading = false + console.error('retrieve image date formats failed', err) + } + }, onChange (idx, val, name) { - const c = this.columns[idx] + const c = this.tableCreate.columns[idx] if (name === 'null_allowed' && val === true) { if (c.primary_key) { c.primary_key = false @@ -144,11 +210,10 @@ export default { c.null_allowed = false } } - this.columns[idx] = c + this.tableCreate.columns[idx] = c }, addColumn (name = '', type = '', null_allowed = true, primary_key = false, unique = true) { - this.columns.push({ - // default column + this.tableCreate.columns.push({ name, type, null_allowed, @@ -161,17 +226,12 @@ export default { }) }, removeColumn (idx) { - this.columns.splice(idx, 1) + this.tableCreate.columns.splice(idx, 1) }, async createTable () { - const data = { - name: this.name, - description: this.description, - columns: this.columns - } try { this.loading = true - const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table`, data, { + const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table`, this.tableCreate, { headers: { Authorization: `Bearer ${this.token}` } }) if (res.status === 201) { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue index eb1cedc2fbf303da8bc4bc6a78d079248fdbe6f0..438f24ca4ecdd37c5a5e63ed423a3f757c255737 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue @@ -269,7 +269,7 @@ export default { { value: 'STRING', text: 'Character Varying' }, { value: 'TEXT', text: 'Text' } ], - newTableId: 42 + newTableId: 42 // FIXME ??? } }, computed: { diff --git a/fda-ui/pages/container/index.vue b/fda-ui/pages/container/index.vue index ac075e92711c04d3a8d427a8bd15cb494393ed2d..ef77bd366441fb983e171e147a6eda8aa09ba0da 100644 --- a/fda-ui/pages/container/index.vue +++ b/fda-ui/pages/container/index.vue @@ -36,7 +36,7 @@ @click="loadDatabase(item)"> <td>{{ item.name }}</td> <td>{{ item.engine }}</td> - <td></td> + <td /> <td>{{ formatDate(item.created) }}</td> </tr> </tbody>