diff --git a/.env.unix.example b/.env.unix.example index 5aa6d4572b6f5970616d3dbe61e3c929fab55cdc..b352e9a482a5f582dd36e11aa32700e81ebcd824 100644 --- a/.env.unix.example +++ b/.env.unix.example @@ -19,4 +19,5 @@ JWT_EXPIRATION=86400000 SHARED_FILESYSTEM=/tmp LOG_LEVEL=trace # error, warning, info, debug, trace DEFAULT_ROLES=ROLE_RESEARCHER -SUPERUSERS=user1,user2 \ No newline at end of file +SUPERUSERS=user1,user2 +ELASTIC_PASSWORD=elastic \ No newline at end of file diff --git a/.env.win.example b/.env.win.example index 440b4a6deb78a6fb56b554996dce3d7cff176784..cf27aca493364a3665bd177230f9028384a2413a 100644 --- a/.env.win.example +++ b/.env.win.example @@ -19,4 +19,5 @@ JWT_EXPIRATION=86400000 SHARED_FILESYSTEM=C:\tmp LOG_LEVEL=trace # error, warning, info, debug, trace DEFAULT_ROLES=ROLE_RESEARCHER -SUPERUSERS=user1,user2 \ No newline at end of file +SUPERUSERS=user1,user2 +ELASTIC_PASSWORD=elastic \ No newline at end of file diff --git a/Makefile b/Makefile index cae96d297152b10182dcc143c3986e196905f598..6baefeff642565adade869b3e9e1c390cd994cf0 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ build-frontend: build-clients: bash ./.gitlab/swagger/generate.sh -tag: tag-identifier tag-container tag-database tag-discovery tag-gateway tag-query tag-table tag-analyse tag-authentication tag-metadata-db tag-ui tag-units tag-broker tag-ui-proxy tag-metadata +tag: tag-identifier tag-search tag-container tag-database tag-discovery tag-gateway tag-query tag-table tag-analyse tag-authentication tag-metadata-db tag-ui tag-units tag-broker tag-ui-proxy tag-metadata tag-analyse: docker tag fda-analyse-service:latest "dbrepo/analyse-service:${TAG}" @@ -103,7 +103,10 @@ tag-units: tag-broker: docker tag fda-broker-service:latest "dbrepo/broker-service:${TAG}" -release: build-docker tag release-identifier release-container release-database release-discovery release-gateway release-query release-table release-analyse release-authentication release-metadata-db release-ui release-units release-broker release-ui-proxy release-metadata +tag-search: + docker tag fda-search-service:latest "dbrepo/search-service:${TAG}" + +release: build-docker tag release-identifier release-search release-container release-database release-discovery release-gateway release-query release-table release-analyse release-authentication release-metadata-db release-ui release-units release-broker release-ui-proxy release-metadata release-analyse: docker push "dbrepo/analyse-service:${TAG}" @@ -147,10 +150,13 @@ release-units: release-broker: docker push "dbrepo/broker-service:${TAG}" +release-search: + docker push "dbrepo/search-service:${TAG}" + release-metadata: docker push "dbrepo/metadata-service:${TAG}" -pull: pull-identifier pull-container pull-database pull-discovery pull-gateway pull-query pull-table pull-analyse pull-authentication pull-metadata-db pull-ui pull-units pull-broker pull-ui-proxy pull-metadata +pull: pull-identifier pull-container pull-search pull-database pull-discovery pull-gateway pull-query pull-table pull-analyse pull-authentication pull-metadata-db pull-ui pull-units pull-broker pull-ui-proxy pull-metadata pull-analyse: docker pull "dbrepo/analyse-service:${TAG}" @@ -197,6 +203,9 @@ pull-broker: pull-metadata: docker pull "dbrepo/metadata-service:${TAG}" +pull-search: + docker pull "dbrepo/search-service:${TAG}" + test-backend: test-authentication-service test-container-service test-database-service test-discovery-service test-gateway-service test-query-service test-table-service test-identifier-service test-metadata-service test-semantics-service test-analyse-service test-authentication-service: build-backend-metadata-db diff --git a/docker-compose.yml b/docker-compose.yml index 98c072409e7e16219b4aa933639767d0d7f49450..fd864bd166bfed2443e866de714ba9c4ce269c49 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -308,14 +308,10 @@ services: restart: always container_name: dbrepo-search-service hostname: search-service - image: elasticsearch:7.13.4 - command: [ "elasticsearch" ] + build: ./fda-search-service + image: fda-search-service networks: core: - environment: - - discovery.type=single-node - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - - logger.level=WARN depends_on: fda-discovery-service: condition: service_healthy diff --git a/fda-database-service/rest-service/src/main/resources/application-docker.yml b/fda-database-service/rest-service/src/main/resources/application-docker.yml index 3c1663a1721394c088588d107d904ee7cffd94c1..fec1d1fc8ad15cdebd436637a5415d3545b2e721 100644 --- a/fda-database-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-database-service/rest-service/src/main/resources/application-docker.yml @@ -39,8 +39,8 @@ eureka: client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ fda: elastic: - endpoint: "${SEARCH_ENDPOINT}" - username: "${SEARCH_USERNAME}" - password: "${SEARCH_PASSWORD}" + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" ready.path: /ready gateway.endpoint: "${GATEWAY_ENDPOINT}" \ No newline at end of file diff --git a/fda-database-service/rest-service/src/main/resources/application-local.yml b/fda-database-service/rest-service/src/main/resources/application-local.yml index 84337e9e82b2fdf47434bd41220ae5b8f4301b90..87b44ff33b77c9ca0787b1c082c64d6be9542df6 100644 --- a/fda-database-service/rest-service/src/main/resources/application-local.yml +++ b/fda-database-service/rest-service/src/main/resources/application-local.yml @@ -39,7 +39,7 @@ eureka: client.serviceUrl.defaultZone: http://localhost:9090/eureka/ fda: elastic: - endpoint: localhost:9200 + endpoint: search-service:9200 username: elastic password: elastic ready.path: ./ready diff --git a/fda-database-service/rest-service/src/main/resources/application.yml b/fda-database-service/rest-service/src/main/resources/application.yml index 65c912faef5b4697b0004decbfcaf24584b4de72..a27d1eda9f4b9936b6b5f5723cdc44583c59e968 100644 --- a/fda-database-service/rest-service/src/main/resources/application.yml +++ b/fda-database-service/rest-service/src/main/resources/application.yml @@ -39,8 +39,8 @@ eureka: client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ fda: elastic: - endpoint: "${SEARCH_ENDPOINT}" - username: "${SEARCH_USERNAME}" - password: "${SEARCH_PASSWORD}" + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" ready.path: /ready gateway.endpoint: http://gateway-service:9095 \ No newline at end of file diff --git a/fda-database-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java b/fda-database-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java index 6c2063c90a017524bcc1d554c1ddad3c4e15c019..03a3bec82e949ab6934eb61853351b2c3538c39f 100644 --- a/fda-database-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java +++ b/fda-database-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java @@ -25,6 +25,7 @@ public class ElasticsearchConfig { public RestHighLevelClient client() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(elasticEndpoint) + .withBasicAuth(elasticUsername, elasticPassword) .build(); return RestClients.create(clientConfiguration) .rest(); diff --git a/fda-database-service/services/src/main/java/at/tuwien/config/IndexConfig.java b/fda-database-service/services/src/main/java/at/tuwien/config/IndexConfig.java index e4481fce82944e92f77946e72d3fac11812708f1..dc0e46355d1aa2cc10ed1c2137bba891a95ad4e2 100644 --- a/fda-database-service/services/src/main/java/at/tuwien/config/IndexConfig.java +++ b/fda-database-service/services/src/main/java/at/tuwien/config/IndexConfig.java @@ -12,6 +12,7 @@ import org.springframework.core.env.Environment; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @@ -38,6 +39,7 @@ public class IndexConfig { this.applicationEventPublisher = applicationEventPublisher; } + @Transactional @EventListener(ApplicationReadyEvent.class) public void initIndex() { log.debug("creating databaseindex"); diff --git a/fda-identifier-service/rest-service/src/main/java/at/tuwien/config/WebConfig.java b/fda-identifier-service/rest-service/src/main/java/at/tuwien/config/WebConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..9b8509553e1a4048ea4de0d766ceea93e734a5db --- /dev/null +++ b/fda-identifier-service/rest-service/src/main/java/at/tuwien/config/WebConfig.java @@ -0,0 +1,16 @@ +package at.tuwien.config; + +import at.tuwien.converters.IdentifierTypeConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new IdentifierTypeConverter()); + } + +} diff --git a/fda-identifier-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeConverter.java b/fda-identifier-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..96356874703a5a6c831caf4c6a892b40c6138fb5 --- /dev/null +++ b/fda-identifier-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeConverter.java @@ -0,0 +1,12 @@ +package at.tuwien.converters; + +import at.tuwien.api.identifier.IdentifierTypeDto; +import org.springframework.core.convert.converter.Converter; + +public class IdentifierTypeConverter implements Converter<String, IdentifierTypeDto> { + + @Override + public IdentifierTypeDto convert(String source) { + return IdentifierTypeDto.valueOf(source.toUpperCase()); + } +} 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 9da1acd45793d03796cfe2ffb5a4e92f673c132b..deee3fb5a70301f5ebff002ad92eaaa20bc4c711 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 @@ -1,5 +1,6 @@ package at.tuwien.endpoints; +import at.tuwien.api.database.query.QueryTypeDto; import at.tuwien.api.identifier.IdentifierCreateDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.identifier.IdentifierTypeDto; @@ -48,11 +49,18 @@ public class IdentifierEndpoint extends AbstractEndpoint { @Timed(value = "identifier.list", description = "Time needed to list the identifiers") @Operation(summary = "Find identifiers") public ResponseEntity<List<IdentifierDto>> list(@RequestParam(required = false) Long dbid, - @RequestParam(required = false) Long qid) { - log.debug("endpoint find identifiers, dbid={}, qid={}", dbid, qid); + @RequestParam(required = false) Long qid, + @RequestParam(required = false) IdentifierTypeDto type) { + log.debug("endpoint find identifiers, dbid={}, qid={}, type={}", dbid, qid, type); final List<Identifier> identifiers = identifierService.findAll(dbid, qid); final List<IdentifierDto> dto = identifiers.stream() .map(identifierMapper::identifierToIdentifierDto) + .filter(i -> { + if (type != null) { + return i.getType().equals(type); + } + return true; + }) .collect(Collectors.toList()); log.info("Find identifiers resulted in {} identifiers", identifiers.size()); log.trace("endpoint find identifiers, list={}", dto); diff --git a/fda-identifier-service/rest-service/src/main/resources/application-docker.yml b/fda-identifier-service/rest-service/src/main/resources/application-docker.yml index 44488ca5950877b978288bc1fa63a9f4140e21a2..418f38ddab6c7d012ffd0ea70f42369aa5f8a527 100644 --- a/fda-identifier-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-identifier-service/rest-service/src/main/resources/application-docker.yml @@ -37,4 +37,7 @@ fda: ready.path: /ready gateway.endpoint: "${GATEWAY_ENDPOINT}" website: "${WEBSITE}" - elastic.endpoint: search-service:9200 \ No newline at end of file + elastic: + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" \ No newline at end of file diff --git a/fda-identifier-service/rest-service/src/main/resources/application-local.yml b/fda-identifier-service/rest-service/src/main/resources/application-local.yml index 7a01b1753963792fa6a870575032d7b5f4e03d95..9a6ca19a334bc74d1666f417cc2e10c275dc9e3c 100644 --- a/fda-identifier-service/rest-service/src/main/resources/application-local.yml +++ b/fda-identifier-service/rest-service/src/main/resources/application-local.yml @@ -19,7 +19,7 @@ spring: jdbc: time_zone: UTC application: - name: authentication-service + name: identifier-service cloud: loadbalancer.ribbon.enabled: false server.port: 9096 @@ -31,10 +31,13 @@ logging: at.tuwien.auth.UserPermissionEvaluator: trace org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug eureka: - instance.hostname: authentication-service + instance.hostname: identifier-service client.serviceUrl.defaultZone: http://localhost:9090/eureka/ fda: ready.path: ./ready - website: http://localhost:3000 gateway.endpoint: http://localhost:9095 - elastic.endpoint: localhost:9200 \ No newline at end of file + website: http://localhost:3000 + elastic: + endpoint: search-service:9200 + username: elastic + password: elastic \ No newline at end of file diff --git a/fda-identifier-service/rest-service/src/main/resources/application.yml b/fda-identifier-service/rest-service/src/main/resources/application.yml index f4c76f03410269c19891de5c81bb5b89a591ad2d..e0c7ffd06fc194f5412d161c26674c59d9d18297 100644 --- a/fda-identifier-service/rest-service/src/main/resources/application.yml +++ b/fda-identifier-service/rest-service/src/main/resources/application.yml @@ -37,4 +37,7 @@ fda: ready.path: /ready gateway.endpoint: "${GATEWAY_ENDPOINT}" website: "${WEBSITE}" - elastic.endpoint: search-service:9200 \ No newline at end of file + elastic: + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" \ No newline at end of file diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java b/fda-identifier-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java index 81e2852f4e6677861acdaa2ea88664716777dfe5..b8de474aed6d651c497abe044fbd3368e9efbf83 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java @@ -15,10 +15,17 @@ public class ElasticsearchConfig { @Value("${fda.elastic.endpoint}") private String elasticEndpoint; + @Value("${fda.elastic.username}") + private String elasticUsername; + + @Value("${fda.elastic.password}") + private String elasticPassword; + @Bean public RestHighLevelClient client() { ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(elasticEndpoint) + .withBasicAuth(elasticUsername, elasticPassword) .build(); return RestClients.create(clientConfiguration).rest(); diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java b/fda-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java index 7f50a0ced16219662f89c82e6bd3bdf2fa78253a..0592364d2de1925f1f1298e635c3ddc58d0e0e06 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java @@ -1,39 +1,54 @@ package at.tuwien.config; import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.mapper.IdentifierMapper; +import at.tuwien.repository.elastic.IdentifierIdxRepository; +import at.tuwien.repository.jpa.IdentifierRepository; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; -import org.springframework.core.env.Environment; -import org.springframework.core.env.Profiles; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; @Component @Log4j2 public class IndexInitializer { - private final Environment environment; + private final IdentifierMapper identifierMapper; + private final IdentifierRepository identifierRepository; + private final IdentifierIdxRepository identifierIdxRepository; private final ElasticsearchOperations elasticsearchOperations; @Autowired - public IndexInitializer(Environment environment, ElasticsearchOperations elasticsearchOperations) { - this.environment = environment; + public IndexInitializer(IdentifierMapper identifierMapper, IdentifierRepository identifierRepository, + IdentifierIdxRepository identifierIdxRepository, + ElasticsearchOperations elasticsearchOperations) { + this.identifierMapper = identifierMapper; + this.identifierRepository = identifierRepository; + this.identifierIdxRepository = identifierIdxRepository; this.elasticsearchOperations = elasticsearchOperations; } + @Transactional @EventListener(ApplicationReadyEvent.class) public void initIndex() { - if (environment.acceptsProfiles(Profiles.of("test-noelastic"))) { - return; - } log.debug("creating identifierindex"); final IndexCoordinates identifierIndex = IndexCoordinates.of("identifierindex"); if (!elasticsearchOperations.indexOps(identifierIndex).exists()) { elasticsearchOperations.indexOps(identifierIndex).create(); elasticsearchOperations.indexOps(identifierIndex).createMapping(IdentifierDto.class); } + final List<IdentifierDto> identifiers = identifierRepository.findAll() + .stream() + .map(identifierMapper::identifierToIdentifierDto) + .collect(Collectors.toList()); + log.debug("add {} identifiers to elastic search index", identifiers.size()); + identifierIdxRepository.saveAll(identifiers); } } diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java index e6b0bc45ae8e69982d2e51819c43652904dd09ec..cc78c3cb2d450f044dbb8be89a7d92f27d9bba3a 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java @@ -1,10 +1,7 @@ package at.tuwien.service; -import at.tuwien.api.identifier.BibliographyTypeDto; -import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.*; import at.tuwien.ExportResource; -import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.api.identifier.VisibilityTypeDto; import at.tuwien.entities.identifier.Identifier; import at.tuwien.exception.*; import org.springframework.core.io.InputStreamResource; diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java index 7dab5a76adf784edd7a08098c204ee6bc7041698..a5837412a7bb1c5d6d917d19ed96e4564108410d 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java @@ -1,5 +1,7 @@ package at.tuwien.api.database.query; +import at.tuwien.api.identifier.IdentifierBriefDto; +import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserDto; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -65,6 +67,8 @@ public class QueryBriefDto { @Schema(example = "query") private QueryTypeDto type; + private IdentifierBriefDto identifier; + @NotNull(message = "created timestamp is required") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant created; diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryDto.java index f39af4ee7b302650ba31b46a711577e70cb60d57..72aaa0c7425b64b2a228775b73285116e1244a0b 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/query/QueryDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.query; +import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserDto; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -55,18 +56,20 @@ public class QueryDto { @Schema(example = "query") private QueryTypeDto type; + private IdentifierDto identifier; + @NotBlank(message = "query hash is required") @JsonProperty("query_hash") - @Parameter(example = "17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76") + @Schema(example = "17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76") private String queryHash; @NotNull @JsonProperty("is_persisted") - @Parameter(example = "true") + @Schema(example = "true") private Boolean isPersisted; @JsonProperty("result_hash") - @Parameter(example = "17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76") + @Schema(example = "17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76") private String resultHash; @JsonProperty("result_number") diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java index 35e72e605245dc1b4d81cec28c40a14079f52d9e..3575c2cb7740928505a21a5bab241963ea219e65 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java @@ -3,7 +3,9 @@ package at.tuwien.endpoint; import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.config.QueryConfig; +import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.user.User; +import at.tuwien.mapper.IdentifierMapper; import at.tuwien.mapper.UserMapper; import at.tuwien.querystore.Query; import at.tuwien.exception.*; @@ -34,16 +36,21 @@ public class StoreEndpoint extends AbstractEndpoint { private final QueryMapper queryMapper; private final UserService userService; private final StoreService storeService; + private final IdentifierMapper identifierMapper; + private final IdentifierService identifierService; @Autowired public StoreEndpoint(QueryConfig queryConfig, UserMapper userMapper, QueryMapper queryMapper, UserService userService, StoreService storeService, DatabaseService databaseService, - IdentifierService identifierService, TableService tableService, AccessService accessService) { + IdentifierService identifierService, TableService tableService, AccessService accessService, + IdentifierMapper identifierMapper, IdentifierService identifierService1) { super(tableService, accessService, databaseService, identifierService, queryConfig); this.userMapper = userMapper; this.queryMapper = queryMapper; this.userService = userService; this.storeService = storeService; + this.identifierMapper = identifierMapper; + this.identifierService = identifierService1; } @GetMapping @@ -63,16 +70,17 @@ public class StoreEndpoint extends AbstractEndpoint { throw new NotAllowedException("Missing view all queries permission"); } final List<Query> queries = storeService.findAll(containerId, databaseId, persisted, principal); + final List<Identifier> identifiers = identifierService.findAll(); final List<User> users = userService.findAll(); final List<QueryBriefDto> dto = queries.stream() .map(q -> { final QueryBriefDto brief = queryMapper.queryToQueryBriefDto(q); - final Optional<User> optional = users.stream().filter(u -> { - u.getId(); - q.getCreatedBy(); - return false; - }).findFirst(); - optional.ifPresent(user -> brief.setCreator(userMapper.userToUserDto(user))); + final Optional<User> optional1 = users.stream().filter(u -> u.getUsername().equals(q.getCreatedBy())) + .findFirst(); + final Optional<Identifier> optional2 = identifiers.stream().filter(i -> i.getDatabaseId().equals(databaseId) && i.getQueryId().equals(q.getId())) + .findFirst(); + optional1.ifPresent(user -> brief.setCreator(userMapper.userToUserDto(user))); + optional2.ifPresent(identifier -> brief.setIdentifier(identifierMapper.identifierToIdentifierBriefDto(identifier))); return brief; }) .collect(Collectors.toList()); diff --git a/fda-query-service/rest-service/src/main/resources/application-docker.yml b/fda-query-service/rest-service/src/main/resources/application-docker.yml index 495b7b81d9628ebba192a25d57f419fcc430390d..4eb5d1238ba6c39390e8dce542a1e742fbd917f5 100644 --- a/fda-query-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-query-service/rest-service/src/main/resources/application-docker.yml @@ -42,4 +42,7 @@ fda: ready.path: /ready consumers: 2 unsupported: "${NOT_SUPPORTED_KEYWORDS}" - elastic.endpoint: search-service:9200 + elastic: + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" diff --git a/fda-query-service/rest-service/src/main/resources/application-local.yml b/fda-query-service/rest-service/src/main/resources/application-local.yml index 1a3815581e032eec3e56ba013d1e80a0ed78dc5e..7bab626242b49172eaec7a433993b2fa159a9725 100644 --- a/fda-query-service/rest-service/src/main/resources/application-local.yml +++ b/fda-query-service/rest-service/src/main/resources/application-local.yml @@ -42,4 +42,7 @@ fda: ready.path: ./ready consumers: 2 unsupported: \*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,-- - elastic.endpoint: localhost:9200 \ No newline at end of file + elastic: + endpoint: search-service:9200 + username: elastic + password: elastic \ No newline at end of file diff --git a/fda-query-service/rest-service/src/main/resources/application.yml b/fda-query-service/rest-service/src/main/resources/application.yml index ff7076d88c7a4ecd6cc6ba904e9117106e46e907..3821bd12aff7814e7ef52a7aab158719b4f6acea 100644 --- a/fda-query-service/rest-service/src/main/resources/application.yml +++ b/fda-query-service/rest-service/src/main/resources/application.yml @@ -42,4 +42,7 @@ fda: ready.path: /ready consumers: "${BROKER_CONSUMERS}" unsupported: "${NOT_SUPPORTED_KEYWORDS}" - elastic.endpoint: search-service:9200 + elastic: + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index 641b1fe0494f7bfca9d0be610ec150e14a5d88d4..5ba610d23c01dfed6557fc07731ec15b810a9aca 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -5,6 +5,9 @@ import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryResultDto; import at.tuwien.api.database.table.TableCsvDto; +import at.tuwien.api.identifier.CreatorDto; +import at.tuwien.api.identifier.IdentifierTypeDto; +import at.tuwien.api.identifier.VisibilityTypeDto; import at.tuwien.api.user.UserDetailsDto; import at.tuwien.api.user.UserDto; import at.tuwien.entities.container.image.ContainerImageDate; @@ -12,6 +15,10 @@ import at.tuwien.entities.database.AccessType; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.database.View; import at.tuwien.entities.database.table.columns.TableColumnConcept; +import at.tuwien.entities.identifier.Creator; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.entities.identifier.IdentifierType; +import at.tuwien.entities.identifier.VisibilityType; import at.tuwien.entities.user.RoleType; import at.tuwien.entities.user.User; import at.tuwien.querystore.Query; @@ -2331,5 +2338,51 @@ public abstract class BaseUnitTest { }}) .build(); + public final static Long IDENTIFIER_1_ID = 1L; + public final static Long IDENTIFIER_1_QUERY_ID = QUERY_1_ID; + public final static Long IDENTIFIER_1_CONTAINER_ID = CONTAINER_1_ID; + public final static Long IDENTIFIER_1_DATABASE_ID = DATABASE_1_ID; + public final static String IDENTIFIER_1_DESCRIPTION = "Selecting all from the weather Australia table"; + public final static String IDENTIFIER_1_TITLE = "Australia weather data"; + public final static String IDENTIFIER_1_DOI = "10.1000/182"; + public final static VisibilityType IDENTIFIER_1_VISIBILITY = VisibilityType.EVERYONE; + public final static VisibilityTypeDto IDENTIFIER_1_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE; + public final static Instant IDENTIFIER_1_CREATED = Instant.ofEpochSecond(1641588352); + public final static Instant IDENTIFIER_1_MODIFIED = Instant.ofEpochSecond(1541588352); + public final static Instant IDENTIFIER_1_EXECUTION = Instant.ofEpochSecond(1541588352); + public final static Integer IDENTIFIER_1_PUBLICATION_MONTH = 5; + public final static Integer IDENTIFIER_1_PUBLICATION_YEAR = 2022; + public final static String IDENTIFIER_1_QUERY_HASH = "abc"; + public final static String IDENTIFIER_1_RESULT_HASH = "def"; + public final static String IDENTIFIER_1_QUERY = "SELECT `id` FROM `foobar`"; + public final static String IDENTIFIER_1_NORMALIZED = "SELECT `id` FROM `foobar`"; + public final static Long IDENTIFIER_1_RESULT_NUMBER = 2L; + public final static String IDENTIFIER_1_PUBLISHER = "Australian Government"; + public final static IdentifierType IDENTIFIER_1_TYPE = IdentifierType.SUBSET; + public final static IdentifierTypeDto IDENTIFIER_1_TYPE_DTO = IdentifierTypeDto.DATABASE; + + public final static Identifier IDENTIFIER_1 = Identifier.builder() + .id(IDENTIFIER_1_ID) + .containerId(IDENTIFIER_1_CONTAINER_ID) + .databaseId(IDENTIFIER_1_DATABASE_ID) + .queryId(IDENTIFIER_1_QUERY_ID) + .description(IDENTIFIER_1_DESCRIPTION) + .title(IDENTIFIER_1_TITLE) + .doi(IDENTIFIER_1_DOI) + .visibility(IDENTIFIER_1_VISIBILITY) + .created(IDENTIFIER_1_CREATED) + .lastModified(IDENTIFIER_1_MODIFIED) + .execution(IDENTIFIER_1_EXECUTION) + .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) + .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) + .queryHash(IDENTIFIER_1_QUERY_HASH) + .resultHash(IDENTIFIER_1_RESULT_HASH) + .query(IDENTIFIER_1_QUERY) + .queryNormalized(IDENTIFIER_1_NORMALIZED) + .resultNumber(IDENTIFIER_1_RESULT_NUMBER) + .publisher(IDENTIFIER_1_PUBLISHER) + .type(IDENTIFIER_1_TYPE) + .creators(List.of()) + .build(); } diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java index bb752e7b53ed0b00ce5a473a2b005177c4c8a22b..618840079ea67e28c16885dbeb04f7889e5d2112 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java @@ -3,12 +3,14 @@ package at.tuwien.endpoint; import at.tuwien.BaseUnitTest; import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; +import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.config.IndexConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.exception.*; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.DatabaseAccessRepository; +import at.tuwien.repository.jpa.IdentifierRepository; import at.tuwien.service.DatabaseService; import at.tuwien.service.QueryService; import at.tuwien.service.UserService; @@ -65,6 +67,9 @@ public class StoreEndpointUnitTest extends BaseUnitTest { @MockBean private UserService userService; + @MockBean + private IdentifierRepository identifierRepository; + @MockBean private DatabaseService databaseService; @@ -84,6 +89,35 @@ public class StoreEndpointUnitTest extends BaseUnitTest { .findAll(); doReturn(DATABASE_1, DATABASE_1).when(databaseService) .find(CONTAINER_1_ID, DATABASE_1_ID); + when(identifierRepository.findAll()) + .thenReturn(List.of()); + + /* test */ + final ResponseEntity<List<QueryBriefDto>> response = storeEndpoint.findAll(CONTAINER_1_ID, DATABASE_1_ID, true, principal); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals(1, response.getBody().size()); + final QueryBriefDto query = response.getBody().get(0); + assertEquals(QUERY_1_ID, query.getId()); + assertEquals(QUERY_1_STATEMENT, query.getQuery()); + assertNull(query.getIdentifier()); + } + + @Test + @WithMockUser(username = USER_1_USERNAME) + public void findAll_withIdentifier_succeeds() throws QueryStoreException, DatabaseNotFoundException, ImageNotSupportedException, + ContainerNotFoundException, NotAllowedException, DatabaseConnectionException, TableMalformedException, UserNotFoundException { + final Principal principal = SecurityContextHolder.getContext().getAuthentication(); + + /* mock */ + doReturn(List.of(QUERY_1)).when(storeService) + .findAll(CONTAINER_1_ID, DATABASE_1_ID, true, principal); + doReturn(Collections.singletonList(USER_1)).when(userService) + .findAll(); + doReturn(DATABASE_1, DATABASE_1).when(databaseService) + .find(CONTAINER_1_ID, DATABASE_1_ID); + when(identifierRepository.findAll()) + .thenReturn(List.of(IDENTIFIER_1)); /* test */ final ResponseEntity<List<QueryBriefDto>> response = storeEndpoint.findAll(CONTAINER_1_ID, DATABASE_1_ID, true, principal); @@ -93,6 +127,12 @@ public class StoreEndpointUnitTest extends BaseUnitTest { final QueryBriefDto query = response.getBody().get(0); assertEquals(QUERY_1_ID, query.getId()); assertEquals(QUERY_1_STATEMENT, query.getQuery()); + final IdentifierBriefDto identifier = query.getIdentifier(); + assertNotNull(identifier); + assertEquals(IDENTIFIER_1_ID, identifier.getId()); + assertEquals(IDENTIFIER_1_TITLE, identifier.getTitle()); + assertEquals(QUERY_1_ID, identifier.getQueryId()); + assertEquals(DATABASE_1_ID, identifier.getDatabaseId()); } @Test diff --git a/fda-query-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java b/fda-query-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java index 81e2852f4e6677861acdaa2ea88664716777dfe5..b8de474aed6d651c497abe044fbd3368e9efbf83 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java +++ b/fda-query-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java @@ -15,10 +15,17 @@ public class ElasticsearchConfig { @Value("${fda.elastic.endpoint}") private String elasticEndpoint; + @Value("${fda.elastic.username}") + private String elasticUsername; + + @Value("${fda.elastic.password}") + private String elasticPassword; + @Bean public RestHighLevelClient client() { ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(elasticEndpoint) + .withBasicAuth(elasticUsername, elasticPassword) .build(); return RestClients.create(clientConfiguration).rest(); diff --git a/fda-query-service/services/src/main/java/at/tuwien/config/IndexConfig.java b/fda-query-service/services/src/main/java/at/tuwien/config/IndexConfig.java index 459222b540b181eb03aa2be0f15a3aff77f8eaf4..eeb61eac82301fc0030619cfa3a3c6d53759109d 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/config/IndexConfig.java +++ b/fda-query-service/services/src/main/java/at/tuwien/config/IndexConfig.java @@ -13,6 +13,7 @@ import org.springframework.core.env.Profiles; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @@ -37,6 +38,7 @@ public class IndexConfig { this.elasticsearchOperations = elasticsearchOperations; } + @Transactional @EventListener(ApplicationReadyEvent.class) public void initIndex() { if (environment.acceptsProfiles(Profiles.of("test-noelastic"))) { diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java index 428497e54ce284dcb3a2425da3cb83edf44732ca..7c73b2bb72765717d68be9a7336883c8e22da0c8 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java @@ -1,5 +1,6 @@ package at.tuwien.mapper; +import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.GrantedAuthorityDto; import at.tuwien.api.user.UserDetailsDto; @@ -16,4 +17,6 @@ public interface IdentifierMapper { Identifier identifierDtoToIdentifier(IdentifierDto data); IdentifierDto identifierToIdentifierDto(Identifier data); + + IdentifierBriefDto identifierToIdentifierBriefDto(Identifier data); } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/fda-query-service/services/src/main/java/at/tuwien/service/IdentifierService.java index de3d500f613ed4605ff0f9d01f835906e00ff8d4..89d572a0ab40709cec1db7b22877f79b083cc03c 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/IdentifierService.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/IdentifierService.java @@ -3,6 +3,8 @@ package at.tuwien.service; import at.tuwien.entities.identifier.Identifier; import at.tuwien.exception.IdentifierNotFoundException; +import java.util.List; + public interface IdentifierService { /** @@ -13,4 +15,6 @@ public interface IdentifierService { * @return The identifier. */ Identifier findByDatabaseIdAndQueryId(Long databaseId, Long queryId) throws IdentifierNotFoundException; + + List<Identifier> findAll(); } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java index 73c586e944695377b2e0fa1b46258a32f01a7b32..abb26a9c3b6465993af1f99e080e263234644d38 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java @@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.List; import java.util.Optional; @Slf4j @@ -30,4 +31,9 @@ public class IdentifierServiceImpl implements IdentifierService { } return optional.get(); } + + @Override + public List<Identifier> findAll() { + return identifierRepository.findAll(); + } } diff --git a/fda-search-service/Dockerfile b/fda-search-service/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..667774bd61e1630fea44e41a1dbf872600a1f1d9 --- /dev/null +++ b/fda-search-service/Dockerfile @@ -0,0 +1,9 @@ +FROM elasticsearch:7.13.4 AS runtime +MAINTAINER Martin Weise <martin.weise@tuwien.ac.at> + +ENV discovery.type=single-node +ENV ES_JAVA_OPTS="-Xms2g -Xmx2g" +ENV logger.level=WARN +ENV bootstrap.memory_lock=true +ENV xpack.security.enabled=true +ENV ELASTIC_PASSWORD=elastic \ No newline at end of file diff --git a/fda-semantics-service/app.py b/fda-semantics-service/app.py index ace6659bdfe1afb7a21f9239ad5d6cc4bf2d5a39..0a59f1f46e597c96d9a98540055db77c486aea4e 100644 --- a/fda-semantics-service/app.py +++ b/fda-semantics-service/app.py @@ -10,7 +10,7 @@ from list import List from validate import validator from gevent.pywsgi import WSGIServer from save import insert_mdb_concepts, insert_mdb_units -from onto_feat import search_ontologies, list_ontologies, get_ontology +from onto_feat import list_ontologies, get_ontology from prometheus_flask_exporter import PrometheusMetrics dictConfig({ @@ -140,14 +140,34 @@ def validate(concept): return jsonify(res), 500 +@app.route('/api/semantics/concept', methods=['PUT'], endpoint='concepts_label') +@swag_from('us-yml/put_concept.yml') +def get_concept_label(): + input_json = request.get_json() + logging.debug('endpoint read concept, body=%s', input_json) + try: + uri = input_json['uri'] + res = list.get_concept_label(uri) + logging.info('suggest concept result: %s', res) + return jsonify(res), 200 + except Exception as e: + logging.error('Failed to suggest concept: %s', e) + res = {'success': False, 'message': str(e), 'status': 500} + return jsonify(res), 500 + + @app.route('/api/semantics/concept', methods=['POST'], endpoint='concepts_save') -@swag_from('post_concept.yml') +@swag_from('us-yml/post_concept.yml') def save_concept(): input_json = request.get_json() logging.debug('endpoint save concept, body=%s', input_json) try: - uri = str(input_json['uri']) - name = str(input_json['name']) + uri = input_json['uri'] + name = input_json['name'] + if uri is None: + return jsonify({'status': 'error', 'message': 'uri is null'}), 400 + if name is None: + return jsonify({'status': 'error', 'message': 'name is null'}), 400 if insert_mdb_concepts(uri, name) > 0: return jsonify({'uri': uri}), 201 else: @@ -159,13 +179,17 @@ def save_concept(): @app.route('/api/semantics/unit', methods=['POST'], endpoint='units_save') -@swag_from('post_unit.yml') +@swag_from('us-yml/post_unit.yml') def save_concept(): input_json = request.get_json() logging.debug('endpoint save unit, body=%s', input_json) try: - uri = str(input_json['uri']) - name = str(input_json['name']) + uri = input_json['uri'] + name = input_json['name'] + if uri is None: + return jsonify({'status': 'error', 'message': 'uri is null'}), 400 + if name is None: + return jsonify({'status': 'error', 'message': 'name is null'}), 400 if insert_mdb_units(uri, name) > 0: return jsonify({'uri': uri}), 201 else: @@ -177,7 +201,7 @@ def save_concept(): @app.route('/api/semantics/ontology', methods=['GET'], endpoint='ontologies_get') -@swag_from('get_ontologies.yml') +@swag_from('us-yml/get_ontologies.yml') def get_ontologies(): ontologies = list_ontologies() logging.info('Get ontologies resulted in list %d', len(ontologies)) @@ -185,7 +209,7 @@ def get_ontologies(): @app.route('/api/semantics/ontology/<name>', methods=['GET'], endpoint='ontologies_get_ontology') -@swag_from('get_ontology.yml') +@swag_from('us-yml/get_ontology.yml') def get_ontologies(name): ontology = get_ontology(name) if ontology is None: diff --git a/fda-semantics-service/list.py b/fda-semantics-service/list.py index 7f3480d270e04641dd3b23f6540975e98c511216..cd6f9786381506b744426a6e0d70366a0fa18729 100644 --- a/fda-semantics-service/list.py +++ b/fda-semantics-service/list.py @@ -8,6 +8,7 @@ Created on Sat Dec 4 11:37:19 2021 """ import logging import rdflib +import re import requests as rq @@ -41,7 +42,7 @@ class List: def list_units(self, name, offset=0) -> []: name = name.lower() logging.info(f"list units for unit name {name}") - l_query = """SELECT DISTINCT ?item ?symbol ?name ?comment + l_query = """SELECT DISTINCT ?unit ?symbol ?name ?comment WHERE { ?unit om:symbol ?symbol . ?unit rdfs:label ?name . @@ -53,7 +54,8 @@ class List: qres = self.u.query(l_query) units = list() for row in qres: - units.append({"uri": str(row.item), "symbol": str(row.symbol), "name": str(row.name), "comment": str(row.comment)}) + units.append( + {"uri": str(row.unit), "symbol": str(row.symbol), "name": str(row.name), "comment": str(row.comment)}) return units def list_concepts(self, name) -> []: @@ -64,7 +66,7 @@ class List: SERVICE <https://query.wikidata.org/sparql> { SELECT ?item ?name ?comment WHERE { - ?item wdt:P279* wd:Q1183543 . + ?item wdt:P31/wdt:P279* wd:Q3054889 . ?item rdfs:label ?name . ?item schema:description ?comment . FILTER(LANG(?comment) = "en") . @@ -94,21 +96,21 @@ class List: print(f"res: uri={row.uri}") return {"uri": str(row.uri)} - def get_concept_uri(self, name) -> {}: - name = name.lower() - logging.info(f"get url for concept name {name}") + def get_concept_label(self, uri) -> {}: + m = re.search('https://www.wikidata.org/entity/(.+?)', uri) + if not m: + raise Exception("Did not match wikidata uri") + entity = m.group(1) + logging.info(f"get label for entity {entity}") uri_query = """SELECT DISTINCT ?item ?name ?comment WHERE { SERVICE <https://query.wikidata.org/sparql> { - SELECT ?item ?name ?comment + SELECT ?label WHERE { - ?item wdt:P279* wd:Q1183543 . - ?item rdfs:label ?name . - ?item schema:description ?comment . - FILTER(LANG(?comment) = "en") . - FILTER(LANG(?name) = "en") . - FILTER regex(?name, \"^""" + name + """$\", "i") . - } + wd:""" + entity + """ rdfs:label ?label . + FILTER (langMatches(lang(?label), "EN" ) ) + } + LIMIT 1 } }""" qres = self.c.query(uri_query) diff --git a/fda-semantics-service/us-yml/put_concept.yml b/fda-semantics-service/us-yml/put_concept.yml new file mode 100644 index 0000000000000000000000000000000000000000..649f8159b78a9bd1a445503a1790cb582995f978 --- /dev/null +++ b/fda-semantics-service/us-yml/put_concept.yml @@ -0,0 +1,29 @@ +summary: +description: This is a simple API for saving concept +consumes: + - application/json +produces: + - application/json +requestBody: + content: + application/json: + schema: + required: + - uri + - name + type: object + properties: + uri: + type: string + example: https://www.wikidata.org/wiki/Q35120 +responses: + 200: + description: OK + 201: + description: Created + 405: + description: Invalid input + 409: + description: Concept already present +tags: + - concepts-endpoint diff --git a/fda-table-service/rest-service/src/main/resources/application-docker.yml b/fda-table-service/rest-service/src/main/resources/application-docker.yml index b2a1fbe26e1a853c208c31d778d2a6bcbc84b149..1a03468a50069a412235b9b26ed01012883bceef 100644 --- a/fda-table-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-table-service/rest-service/src/main/resources/application-docker.yml @@ -40,4 +40,7 @@ eureka: fda: ready.path: /ready gateway.endpoint: "${GATEWAY_ENDPOINT}" - elastic.endpoint: "${SEARCH_ENDPOINT}" \ No newline at end of file + elastic: + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" \ No newline at end of file diff --git a/fda-table-service/rest-service/src/main/resources/application-local.yml b/fda-table-service/rest-service/src/main/resources/application-local.yml index 6e720cd3f502efae0422e417a7bb2048f19edb1e..1852e935c8b90af294290146bd094110f8915c3d 100644 --- a/fda-table-service/rest-service/src/main/resources/application-local.yml +++ b/fda-table-service/rest-service/src/main/resources/application-local.yml @@ -40,4 +40,7 @@ eureka: fda: ready.path: ./ready gateway.endpoint: http://localhost:9095 - elastic.endpoint: localhost:9200 \ No newline at end of file + elastic: + endpoint: search-service:9200 + username: elastic + password: elastic \ No newline at end of file diff --git a/fda-table-service/rest-service/src/main/resources/application.yml b/fda-table-service/rest-service/src/main/resources/application.yml index 5428d2c0fbd7f2737545d4c869f163e751759629..c0853c68f4e0e8595aceb60cb839a20903f18133 100644 --- a/fda-table-service/rest-service/src/main/resources/application.yml +++ b/fda-table-service/rest-service/src/main/resources/application.yml @@ -40,4 +40,7 @@ eureka: fda: ready.path: ./ready gateway.endpoint: http://gateway-service:9095 - elastic.endpoint: search-service:9200 \ No newline at end of file + elastic: + endpoint: search-service:9200 + username: elastic + password: "${ELASTIC_PASSWORD}" \ No newline at end of file diff --git a/fda-table-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java b/fda-table-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java index a510fc7b42add41f43c8d79fe8ca3334660025dc..1f447e2c839977c02443090cdfb6568c5e04e79b 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java +++ b/fda-table-service/services/src/main/java/at/tuwien/config/ElasticsearchConfig.java @@ -19,10 +19,17 @@ public class ElasticsearchConfig { @Value("${fda.elastic.endpoint}") private String elasticEndpoint; + @Value("${fda.elastic.username}") + private String elasticUsername; + + @Value("${fda.elastic.password}") + private String elasticPassword; + @Bean public RestHighLevelClient client() { ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(elasticEndpoint) + .withBasicAuth(elasticUsername, elasticPassword) .build(); return RestClients.create(clientConfiguration).rest(); diff --git a/fda-table-service/services/src/main/java/at/tuwien/config/IndexConfig.java b/fda-table-service/services/src/main/java/at/tuwien/config/IndexConfig.java index 344c0891f9d7a90b5c7aa488a88cdbe2de4e4e1a..e6f97043de1c912ac605dc79840f1a0728b50e0c 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/config/IndexConfig.java +++ b/fda-table-service/services/src/main/java/at/tuwien/config/IndexConfig.java @@ -16,6 +16,7 @@ import org.springframework.core.env.Profiles; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @@ -46,6 +47,7 @@ public class IndexConfig { this.tableColumnIdxRepository = tableColumnIdxRepository; } + @Transactional @EventListener(ApplicationReadyEvent.class) public void initIndex() { if (environment.acceptsProfiles(Profiles.of("test-noelastic"))) { diff --git a/fda-ui/Dockerfile b/fda-ui/Dockerfile index ed851716b40bdd592bc85024db3ee205442b4e1c..bf77684481d4eb70cc6e9e3bb44090bc88d46a69 100644 --- a/fda-ui/Dockerfile +++ b/fda-ui/Dockerfile @@ -43,8 +43,9 @@ ENV SEARCH=http://search-service:9200 ENV SHARED_FILESYSTEM=/tmp ENV LOGO=/logo.png ENV MAIL_VERIFY=false +ENV ELASTIC_PASSWORD=elastic COPY ./docker-entrypoint.sh /app/docker-entrypoint.sh RUN chmod +x /app/docker-entrypoint.sh -ENTRYPOINT [ "bash", "VERSION=${TAG}", "/app/docker-entrypoint.sh" ] +ENTRYPOINT [ "sh", "VERSION=${TAG}", "/app/docker-entrypoint.sh" ] diff --git a/fda-ui/components/QueryList.vue b/fda-ui/components/QueryList.vue index 56b97350237f3f228ce85c1c76cef2b20485f57f..d8230d06f496f6d0755f1dca8436a54cfb11ea6c 100644 --- a/fda-ui/components/QueryList.vue +++ b/fda-ui/components/QueryList.vue @@ -2,83 +2,47 @@ <div> <v-progress-linear v-if="loading || error" :color="loadingColor" :value="loadProgress" /> <v-tabs-items> - <v-card v-if="!loading && queries.length === 0" flat> + <v-card v-if="!loading && queries.length === 0 && !error" flat> <v-card-text v-text="emptyMessage" /> </v-card> - <v-expansion-panels v-if="queries.length > 0" accordion> - <v-expansion-panel v-for="(item, i) in queries" :key="i" @click="details(item)"> - <v-expansion-panel-header> - <pre>{{ item.query }}</pre> - <v-icon v-if="item.type === 'view'" title="Query from a view" class="pid-icon">mdi-gauge</v-icon> - <v-icon v-if="item.identifier" color="primary" title="Query with metadata" class="pid-icon">mdi-lock-clock</v-icon> - </v-expansion-panel-header> - <v-expansion-panel-content> - <v-row dense> - <v-col> - <v-list dense> - <v-list-item v-if="queryDetails.identifier"> - <v-list-item-icon> - <v-icon>mdi-lock-clock</v-icon> - </v-list-item-icon> - <v-list-item-content v-if="queryDetails.identifier"> - <v-list-item-title> - Persistent Identifier - </v-list-item-title> - <v-list-item-content> - <a :href="`${baseUrl}/pid/${queryDetails.identifier.id}`">{{ baseUrl }}/pid/{{ queryDetails.identifier.id }}</a> - </v-list-item-content> - <v-list-item-title class="mt-2"> - Title - </v-list-item-title> - <v-list-item-content> - {{ queryDetails.identifier.title }} - </v-list-item-content> - </v-list-item-content> - </v-list-item> - <v-list-item> - <v-list-item-icon> - <v-icon>mdi-text-short</v-icon> - </v-list-item-icon> - <v-list-item-content> - <v-list-item-title> - Query Statement - </v-list-item-title> - <v-list-item-content> - <pre>{{ queryDetails.query }}</pre> - </v-list-item-content> - <v-list-item-title class="mt-2"> - Execution Timestamp - </v-list-item-title> - <v-list-item-content> - {{ createdTime }} - </v-list-item-content> - <v-list-item-title class="mt-2"> - Type - </v-list-item-title> - <v-list-item-content> - {{ queryType }} - </v-list-item-content> - </v-list-item-content> - </v-list-item> - </v-list> - </v-col> - </v-row> - <v-row dense> - <v-col> - <v-btn small color="secondary" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/query/${item.id}`"> - More - </v-btn> - </v-col> - </v-row> - </v-expansion-panel-content> - </v-expansion-panel> - </v-expansion-panels> + <div v-if="!loading && !error"> + <div v-for="(item,i) in queries" :key="i"> + <v-divider v-if="i !== 0" class="mx-4" /> + <v-list-item-group> + <v-list-item two-line :to="link(item)"> + <v-list-item-content> + <v-list-item-title v-text="created(item)" /> + <v-list-item-subtitle class="mt-2"> + <pre>{{ item.query }}</pre> + </v-list-item-subtitle> + </v-list-item-content> + </v-list-item> + </v-list-item-group> + </div> + </div> + <div v-if="!loading && error"> + <!-- show identifiers when error --> + <div v-for="(item,i) in identifiers" :key="i"> + <v-divider v-if="i !== 0" class="mx-4" /> + <v-list-item-group> + <v-list-item two-line :to="link(item)"> + <v-list-item-content> + <v-list-item-title v-text="item.title" /> + <v-list-item-subtitle class="mt-2"> + <pre>{{ item.query }}</pre> + </v-list-item-subtitle> + </v-list-item-content> + </v-list-item> + </v-list-item-group> + </div> + </div> + <pre>{{ identifiers }}</pre> </v-tabs-items> </div> </template> <script> -import { formatTimestampUTCLabel } from '@/utils' +import { formatTimestampUTCLabel, formatUser } from '@/utils' export default { data () { @@ -87,15 +51,7 @@ export default { loadProgress: 0, error: false, queries: [], - queryDetails: { - id: null, - doi: null, - queryHash: null, - execution: null, - created: null, - columns: [], - type: null - } + identifiers: [] } }, computed: { @@ -122,15 +78,9 @@ export default { loadingColor () { return this.error ? 'error' : 'primary' }, - createdTime () { - return formatTimestampUTCLabel(this.queryDetails.created) - }, creator () { return this.queryDetails.creator }, - queryType () { - return 'Query' + (this.queryDetails.type === 'view' ? ' was executed by a view' : '') - }, emptyMessage () { if (this.isPublicOrOwner()) { return '(no subsets)' @@ -139,11 +89,28 @@ export default { } }, mounted () { - this.loadIdentifiers() this.loadQueries() + this.loadIdentifiers() this.simulateProgress() }, methods: { + formatCreator (creator) { + return formatUser(creator) + }, + async loadIdentifiers () { + try { + this.loading = true + const res = await this.$axios.get(`/api/identifier?dbid=${this.$route.params.database_id}&type=subset`, this.config) + this.identifiers = res.data + console.debug('identifiers', this.identifiers) + } catch (error) { + this.error = true + console.error('Failed to load identifiers', error) + const { message } = error.response + this.$toast.error(`Failed to load identifiers: ${message}`) + } + this.loading = false + }, async loadQueries () { if (!this.isPublicOrOwner()) { return @@ -151,12 +118,7 @@ export default { try { this.loading = true const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query?persisted=true`, this.config) - res.data.forEach((query) => { - if (this.queries.filter(q => q.id === query.id).length > 0) { - return - } - this.queries.push(query) - }) + this.queries = res.data console.debug('queries', this.queries) } catch (err) { this.error = true @@ -165,37 +127,11 @@ export default { } this.loading = false }, - async loadIdentifiers () { - if (!this.isPublicOrOwner()) { - return - } - try { - this.loading = true - const res = await this.$axios.get(`/api/identifier?dbid=${this.$route.params.database_id}`, this.config) - const identifiers = res.data.filter(i => i.type === 'subset') - const queries = identifiers.map((identifier) => { - const query = { - id: identifier.query_id, - identifier, - type: identifier.type, - query: identifier.query, - query_hash: identifier.query_hash, - result_hash: identifier.result_hash, - created: identifier.created, - execution: identifier.execution - } - return query - }) - this.queries = queries - console.debug('identifier queries', queries) - } catch (err) { - console.error('Failed to load identifiers', err.response.index) - this.$toast.error('Failed to load identifiers') - } - this.loading = false + created (query) { + return formatTimestampUTCLabel(query.created) }, - details (query) { - this.queryDetails = query + link (query) { + return `${this.baseUrl}/pid/${query.id}` }, isPublicOrOwner () { if (!this.database) { diff --git a/fda-ui/components/TableToolbar.vue b/fda-ui/components/TableToolbar.vue index 1dfda924803af5acc00cb74db801ce186b86bc32..23797c9de9aa7e043e4ecba28db08fea49a465b3 100644 --- a/fda-ui/components/TableToolbar.vue +++ b/fda-ui/components/TableToolbar.vue @@ -199,13 +199,18 @@ export default { this.$toast.success('Deleted ' + this.selection.length + ' rows(s)') this.selection = [] /* reload */ - await this.loadData() + this.$emit('reload', { + success: true + }) }, close (event) { console.debug('closed edit/create tuple dialog', event) this.editTupleDialog = false + this.selection = [] if (event.success) { - this.loadData() + this.$emit('reload', { + success: true + }) } } } diff --git a/fda-ui/components/ViewList.vue b/fda-ui/components/ViewList.vue index afad65d7517eec3b94b9f02206f497c42bd7f9c3..e8533a067ef8313815d124982d8e76588cd08c30 100644 --- a/fda-ui/components/ViewList.vue +++ b/fda-ui/components/ViewList.vue @@ -6,59 +6,25 @@ (no views) </v-card-text> </v-card> - <v-expansion-panels v-if="!loading && views.length > 0" v-model="panel" accordion> - <v-expansion-panel v-for="(item,i) in views" :key="i" @click="details(item)"> - <v-expansion-panel-header> - {{ item.name }} - </v-expansion-panel-header> - <v-expansion-panel-content class="mb-2"> - <v-row dense> - <v-col> - <v-list dense> - <v-list-item> - <v-list-item-icon> - <v-icon>mdi-text-short</v-icon> - </v-list-item-icon> - <v-list-item-content> - <v-list-item-title> - View ID - </v-list-item-title> - <v-list-item-content v-text="viewDetails.id" /> - <v-list-item-title class="mt-2"> - View Query - </v-list-item-title> - <v-list-item-content> - <pre v-text="viewDetails.query" /> - </v-list-item-content> - <v-list-item-title class="mt-2"> - View Visibility - </v-list-item-title> - <v-list-item-content> - {{ viewVisibility }} - </v-list-item-content> - </v-list-item-content> - </v-list-item> - </v-list> - </v-col> - </v-row> - <v-row dense> - <v-col> - <v-btn small color="secondary" class="mr-2" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/view/${viewDetails.id}`"> - View Data - </v-btn> - <v-btn v-if="isOwner" small color="error" @click="deleteView(viewDetails)"> - Delete - </v-btn> - </v-col> - </v-row> - </v-expansion-panel-content> - </v-expansion-panel> - </v-expansion-panels> + <div v-for="(item,i) in views" :key="i"> + <v-divider v-if="i !== 0" class="mx-4" /> + <v-list-item-group> + <v-list-item three-line :to="`/api/container/${$route.params.container_id}/database/${$route.params.database_id}/view/${item.id}`"> + <v-list-item-content> + <v-list-item-title v-text="item.name" /> + <v-list-item-subtitle v-text="formatCreator(item.creator)" /> + <v-list-item-subtitle class="mt-2"> + <pre>{{ item.query }}</pre> + </v-list-item-subtitle> + </v-list-item-content> + </v-list-item> + </v-list-item-group> + </div> </div> </template> <script> -import { formatTimestampUTCLabel } from '@/utils' +import { formatTimestampUTCLabel, formatUser } from '@/utils' export default { data () { @@ -127,6 +93,9 @@ export default { mounted () { }, methods: { + formatCreator (creator) { + return formatUser(creator) + }, async details (table) { if (table.id === this.viewDetails.id) { /* prevent weird glitch of opening and collapsing simultaneously */ diff --git a/fda-ui/components/dialogs/Semantics.vue b/fda-ui/components/dialogs/Semantics.vue index 116bfe81aa7ad231c0ecf6886f85e663b7d3a3ee..6b92b4f5466e35617f1c72bfe464750237e1dcd2 100644 --- a/fda-ui/components/dialogs/Semantics.vue +++ b/fda-ui/components/dialogs/Semantics.vue @@ -19,10 +19,12 @@ solo flat dense + clearable single-line hide-details - placeholder="Search or provide URI..." /> - <v-btn icon small class="ml-2" type="submit" @click="retrieveConcept"> + placeholder="Search or provide URI..." + @click:clear="concept = null" /> + <v-btn icon class="ml-2" type="submit" @click="retrieveConcepts"> <v-icon>mdi-magnify</v-icon> </v-btn> </v-toolbar> @@ -51,10 +53,12 @@ solo flat dense + clearable single-line hide-details - placeholder="Search or provide URI..." /> - <v-btn icon small class="ml-2" type="submit" @click="retrieveUnit"> + placeholder="Search or provide URI..." + @click:clear="unit = null" /> + <v-btn icon class="ml-2" type="submit" @click="retrieveUnits"> <v-icon>mdi-magnify</v-icon> </v-btn> </v-toolbar> @@ -76,13 +80,6 @@ </v-list-item-group> </v-card-text> <v-card-actions> - <v-btn - v-if="canRemove" - class="mb-2 ml-2" - color="error" - @click="remove"> - Remove - </v-btn> <v-spacer /> <v-btn class="mb-2" @@ -92,7 +89,6 @@ <v-btn color="primary" class="mb-2 mr-2" - :disabled="!canSave" @click="save"> Save </v-btn> @@ -169,36 +165,15 @@ export default { value: entry } }) - }, - canRemove () { - return this.column[this.mode] !== null - }, - canSave () { - return ('uri' in this.unit || 'uri' in this.concept) } }, watch: { - async search (val) { - if (!val || this.selected) { - return - } - this.searchTerm = val - this.isLoading = true - await new Promise(resolve => setTimeout(resolve, 2000)) - if (val !== this.searchTerm) { - return - } - try { - const res = await this.$axios.get(`/api/semantics/${this.mode}?q=${val}`, this.config) - this.entries = res.data - console.debug('suggest', res.data) - } catch (err) { - console.error('suggest', err) - } - this.isLoading = false + column () { + this.reset() } }, mounted () { + this.reset() }, methods: { cancel () { @@ -229,18 +204,15 @@ export default { this.concepts = [] console.debug('selected concept', this.concept) }, - async remove () { - if (!this.database) { - return - } + async remove (mode) { /* update column */ let payload - if (this.mode === 'unit') { + if (mode === 'unit') { payload = { unit_uri: null, concept_uri: (this.column.concept ? this.column.concept.uri : null) } - } else if (this.mode === 'concept') { + } else if (mode === 'concept') { payload = { unit_uri: (this.column.unit ? this.column.unit.uri : null), concept_uri: null @@ -249,37 +221,21 @@ export default { try { await this.$axios.put(`/api/container/${this.database.id}/database/${this.database.id}/table/${this.tableId}/column/${this.column.id}`, payload, this.config) if (payload.unit_uri === null) { - this.column[this.mode] = null + this.column[mode] = null } if (payload.concept_uri === null) { - this.column[this.mode] = null + this.column[mode] = null } - this.dialog = false - this.saved = true - console.info(`Removed semantics of column ${this.column.name}`) - this.$toast.success(`Removed semantics of column ${this.column.name}`) - this.$emit('close', { - success: true, - action: 'remove', - mode: this.mode - }) + console.info(`Removed ${mode} of column ${this.column.name}`) + this.$toast.success(`Removed ${mode} of column ${this.column.name}`) console.debug('column', this.column) } catch (error) { - console.error('Failed to save column semantics', error) + console.error(`Failed to save column ${mode}`, error) const { message } = error.response - this.$toast.error('Failed to save column semantics: ' + message) + this.$toast.error(`Failed to save column ${mode}: ` + message) } }, - resetModel (name, uri) { - this.selected = false - this.model = { - name, - uri, - symbol: null, - comment: null - } - }, - async retrieveConcept () { + async retrieveConcepts () { this.loadingConcept = true try { const res = await this.$axios.get(`/api/semantics/concept?q=${this.searchConcept}`, this.config) @@ -292,7 +248,7 @@ export default { } this.loadingConcept = false }, - async retrieveUnit () { + async retrieveUnits () { this.loadingUnit = true try { const res = await this.$axios.get(`/api/semantics/unit?q=${this.searchUnit}`, this.config) @@ -307,8 +263,10 @@ export default { }, async save () { for (const mode in { unit: 0, concept: 0 }) { - if (this[mode].name == null || this[mode].uri == null) { - return + if (this[mode] === null || this[mode].name === null || this[mode].uri === null) { + console.warn(`Delete ${mode} because object, name or uri is null`) + await this.remove(mode) + continue } try { const res = await this.$axios.post(`/api/semantics/${mode}`, { @@ -328,44 +286,37 @@ export default { this.$toast.error(`Failed to save ${mode}: ` + message) } } - await this.update(mode) + } + await this.update() + }, + reset () { + if (this.column.concept) { + this.searchConcept = this.column.concept.uri + this.concept = this.column.concept + } else { + this.searchConcept = null + this.concept = null + } + if (this.column.unit) { + this.searchUnit = this.column.unit.uri + this.unit = this.column.unit + } else { + this.searchUnit = null + this.unit = null } }, - async update (mode) { + async update () { try { const payload = { - concept_uri: this.concept.uri, - unit_uri: this.unit.uri + concept_uri: this.concept === null ? null : this.concept.uri, + unit_uri: this.unit === null ? null : this.unit.uri } const res = await this.$axios.put(`/api/container/${this.database.id}/database/${this.database.id}/table/${this.tableId}/column/${this.column.id}`, payload, this.config) - if (mode === 'unit') { - if (res.data.unit_uri === null) { - this.column[mode] = null - } else { - this.column[mode] = { - name: this[mode].name, - uri: res.data.unit_uri - } - } - } - if (mode === 'concept') { - if (res.data.concept_uri === null) { - this.column[mode] = null - } else { - this.column[mode] = { - name: this[mode].name, - uri: res.data.concept_uri - } - } - } - this.dialog = false - this.saved = true - console.info(`Updated semantics of column ${this.column.name}`) - this.$toast.success(`Updated semantics of column ${this.column.name}`) + this.column.concept = (payload.concept_uri === null ? null : this.concept) + this.column.unit = (payload.unit_uri === null ? null : this.unit) this.$emit('close', { success: true, action: 'assign', - mode, data: res.data }) console.debug('column', this.column) diff --git a/fda-ui/nuxt.config.js b/fda-ui/nuxt.config.js index 55b3ebacee1de392118f894ad4d74eeabc1df362..b79e02d2c7f74976d543203214408b58d37833df 100644 --- a/fda-ui/nuxt.config.js +++ b/fda-ui/nuxt.config.js @@ -78,7 +78,8 @@ export default { version: process.env.VERSION || 'latest', logo: process.env.LOGO || '/logo.png', mailVerify: process.env.MAIL_VERIFY || false, - tokenMax: process.env.TOKEN_MAX || 5 + tokenMax: process.env.TOKEN_MAX || 5, + elasticPassword: process.env.ELASTIC_PASSWORD || 'elastic' }, proxy: { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue index a06149e87e53094974d438bbc612777b45236920..d4394ce92da7625acf2d92a202a7f8d072260409 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue @@ -1,6 +1,6 @@ <template> <div> - <TableToolbar :table="table" :selection="selection" /> + <TableToolbar :table="table" :selection="selection" @reload="loadData" /> <v-toolbar :color="versionColor" flat> <v-toolbar-title> <strong>Versioning</strong> diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue index 9dc6488505e53b27524f0b4dac7bc09a1d0fe4aa..8cb00e231b87905ffa141cf8c665cd1bac254ebb 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue @@ -67,11 +67,10 @@ </v-list-item-title> <v-list-item-content class="amqp-consumer"> <span v-if="justCreated">Creating consumers ...</span> - <v-skeleton-loader v-if="loadingConsumers" type="text" class="skeleton-small" /> - <div v-if="!loadingConsumers"> - <span v-if="!justCreated" v-text="`${consumersUp}/${consumersTotal}`" /> + <v-skeleton-loader v-if="!justCreated && loadingConsumers" type="text" class="skeleton-small" /> + <div v-if="!justCreated && !loadingConsumers"> + <span v-text="`${consumersUp}/${consumersTotal}`" /> <v-badge - v-if="!justCreated" class="ml-1" :color="consumersState.color" :content="consumersState.text" /> 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 629774e1f2c0de218f6d46692d883c97d51b3355..f8899888914a07c30838c165b5429f3ba5545020 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 @@ -21,10 +21,12 @@ </v-row> <v-row dense> <v-col cols="8"> - <v-text-field + <v-textarea v-model="tableCreate.description" :rules="[v => notEmpty(v) || $t('Required')]" autocomplete="off" + rows="3" + name="description" label="Description *" /> </v-col> </v-row> diff --git a/fda-ui/pages/search/index.vue b/fda-ui/pages/search/index.vue index 792be123ce194c48cfefb7786ce9bd6037cca5ba..a6d5489897dd90fa00ffa80b667cf9e2454b3dd5 100644 --- a/fda-ui/pages/search/index.vue +++ b/fda-ui/pages/search/index.vue @@ -51,6 +51,14 @@ export default { return `${this.results.length} results` } return `${this.results.length} result` + }, + elasticConfig () { + return { + auth: { + username: 'elastic', + password: this.$config.elasticPassword + } + } } }, watch: { @@ -74,7 +82,7 @@ export default { } this.loading = true try { - const res = await this.$axios.get(`/retrieve/databaseindex,tableindex,columnindex,identifierindex,viewindex/_search?q=${v}*&terminate_after=50`) + const res = await this.$axios.get(`/retrieve/databaseindex,tableindex,columnindex,identifierindex,viewindex/_search?q=${v}*&terminate_after=50`, this.elasticConfig) console.info('search results', res.data.hits.total.value) console.debug('search results for', this.$route.query.q, 'are', res.data.hits.hits) this.results = res.data.hits.hits.map(h => h._source)