From 7019a7b98bf5250809268a97d12669ca5813013c Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 29 Jan 2025 14:33:52 +0100 Subject: [PATCH] Fixed UI Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- dbrepo-data-service/Dockerfile | 4 +- dbrepo-metadata-service/Dockerfile | 4 +- .../api/identifier/IdentifierBriefDto.java | 3 + .../java/at/tuwien/mapper/MetadataMapper.java | 5 +- .../main/java/at/tuwien/config/MvcConfig.java | 16 + .../IdentifierStatusTypeDtoConverter.java | 14 + ...r.java => IdentifierTypeDtoConverter.java} | 2 +- .../tuwien/endpoints/IdentifierEndpoint.java | 67 ++-- .../IdentifierTypeConverterUnitTest.java | 2 +- .../endpoints/IdentifierEndpointUnitTest.java | 260 ++++++++++---- .../tuwien/mvc/PrometheusEndpointMvcTest.java | 3 +- dbrepo-ui/components/ResourceStatus.vue | 17 +- dbrepo-ui/components/identifier/Banner.vue | 2 +- dbrepo-ui/components/identifier/Persist.vue | 19 +- dbrepo-ui/components/identifier/Select.vue | 23 +- dbrepo-ui/components/subset/Builder.vue | 9 +- dbrepo-ui/components/subset/SubsetList.vue | 25 +- dbrepo-ui/components/subset/SubsetToolbar.vue | 4 +- dbrepo-ui/components/table/TableList.vue | 10 - dbrepo-ui/components/view/ViewList.vue | 10 - dbrepo-ui/composables/identifier-service.ts | 340 +++--------------- dbrepo-ui/dto/index.ts | 75 +++- dbrepo-ui/layouts/default.vue | 32 +- dbrepo-ui/locales/en-US.json | 4 +- .../pages/database/[database_id]/info.vue | 77 ++-- .../persist/[identifier_id]/index.vue | 23 +- .../database/[database_id]/persist/index.vue | 8 +- .../pages/database/[database_id]/settings.vue | 2 +- .../[database_id]/subset/[subset_id]/data.vue | 22 +- .../[database_id]/subset/[subset_id]/info.vue | 64 ++-- .../persist/[identifier_id]/index.vue | 26 +- .../subset/[subset_id]/persist/index.vue | 43 +-- .../database/[database_id]/subset/create.vue | 3 + .../[database_id]/table/[table_id]/import.vue | 3 + .../[database_id]/table/[table_id]/info.vue | 55 ++- .../persist/[identifier_id]/index.vue | 26 +- .../table/[table_id]/persist/index.vue | 14 +- .../[database_id]/table/[table_id]/schema.vue | 2 - .../table/[table_id]/settings.vue | 9 +- .../[database_id]/table/create/dataset.vue | 3 - .../[database_id]/table/create/schema.vue | 3 - .../[database_id]/view/[view_id]/data.vue | 6 - .../[database_id]/view/[view_id]/info.vue | 59 ++- .../persist/[identifier_id]/index.vue | 26 +- .../view/[view_id]/persist/index.vue | 11 +- .../database/[database_id]/view/create.vue | 8 +- dbrepo-ui/stores/cache.js | 5 + docker-compose.yml | 2 +- make/build.mk | 4 + make/dev.mk | 2 +- 50 files changed, 724 insertions(+), 732 deletions(-) create mode 100644 dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/config/MvcConfig.java create mode 100644 dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierStatusTypeDtoConverter.java rename dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/{IdentifierTypeConverter.java => IdentifierTypeDtoConverter.java} (79%) diff --git a/dbrepo-data-service/Dockerfile b/dbrepo-data-service/Dockerfile index 4b45e94290..9edf1375fb 100644 --- a/dbrepo-data-service/Dockerfile +++ b/dbrepo-data-service/Dockerfile @@ -8,7 +8,7 @@ LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" COPY ./pom.xml ./ -RUN mvn -fn -B -q dependency:go-offline +RUN mvn -fn dependency:go-offline COPY --from=dependency /root/.m2/repository/at/tuwien /root/.m2/repository/at/tuwien @@ -18,7 +18,7 @@ COPY ./rest-service ./rest-service COPY ./services ./services # Make sure it compiles -RUN mvn -fn -B -q clean package -DskipTests +RUN mvn -fn clean package -DskipTests ###### THIRD STAGE ###### FROM amazoncorretto:17-alpine3.19 AS runtime diff --git a/dbrepo-metadata-service/Dockerfile b/dbrepo-metadata-service/Dockerfile index ddc20cb420..fa92b799ee 100644 --- a/dbrepo-metadata-service/Dockerfile +++ b/dbrepo-metadata-service/Dockerfile @@ -12,7 +12,7 @@ COPY ./rest-service/pom.xml ./rest-service/ COPY ./services/pom.xml ./services/ COPY ./test/pom.xml ./test/ -RUN mvn -fn -B dependency:go-offline +RUN mvn dependency:go-offline COPY ./api ./api COPY ./entities ./entities @@ -24,7 +24,7 @@ COPY ./services ./services COPY ./test ./test # Make sure it compiles -RUN mvn -fn -B clean install -DskipTests +RUN mvn clean install -DskipTests ###### SECOND STAGE ###### FROM amazoncorretto:17-alpine3.19 AS runtime diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java index 97f3502674..f94edc2cf7 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java @@ -50,6 +50,9 @@ public class IdentifierBriefDto { @NotNull private List<IdentifierTitleDto> titles; + @NotNull + private List<IdentifierDescriptionDto> descriptions; + @Schema(example = "10.1038/nphys1170") private String doi; diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java index 1d9bc28c6a..97c0d0b903 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java @@ -393,6 +393,8 @@ public interface MetadataMapper { IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data); + IdentifierStatusType identifierStatusTypeDtoToIdentifierStatusType(IdentifierStatusTypeDto data); + default String identifierToLocationUrl(String baseUrl, Identifier data) { if (data.getType().equals(IdentifierType.SUBSET)) { return baseUrl + "/database/" + data.getDatabase().getId() + "/subset/" + data.getQueryId() + "/info?pid=" + data.getId(); @@ -823,7 +825,8 @@ public interface MetadataMapper { } @Mappings({ - @Mapping(target = "database.views", ignore = true) + @Mapping(target = "database.views", ignore = true), + @Mapping(target = "database.tables", ignore = true) }) ViewDto viewToViewDto(View data); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/config/MvcConfig.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/config/MvcConfig.java new file mode 100644 index 0000000000..6bdb809731 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/config/MvcConfig.java @@ -0,0 +1,16 @@ +package at.tuwien.config; + +import at.tuwien.converters.IdentifierStatusTypeDtoConverter; +import at.tuwien.converters.IdentifierTypeDtoConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class MvcConfig implements WebMvcConfigurer { + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new IdentifierStatusTypeDtoConverter()); + registry.addConverter(new IdentifierTypeDtoConverter()); + } +} \ No newline at end of file diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierStatusTypeDtoConverter.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierStatusTypeDtoConverter.java new file mode 100644 index 0000000000..96e67f63d2 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierStatusTypeDtoConverter.java @@ -0,0 +1,14 @@ +package at.tuwien.converters; + +import at.tuwien.api.identifier.IdentifierStatusTypeDto; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +@Component +public class IdentifierStatusTypeDtoConverter implements Converter<String, IdentifierStatusTypeDto> { + + @Override + public IdentifierStatusTypeDto convert(String source) { + return IdentifierStatusTypeDto.valueOf(source.toUpperCase()); + } +} diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeConverter.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeDtoConverter.java similarity index 79% rename from dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeConverter.java rename to dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeDtoConverter.java index b3f52c4377..61e169604f 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeConverter.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/converters/IdentifierTypeDtoConverter.java @@ -5,7 +5,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; @Component -public class IdentifierTypeConverter implements Converter<String, IdentifierTypeDto> { +public class IdentifierTypeDtoConverter implements Converter<String, IdentifierTypeDto> { @Override public IdentifierTypeDto convert(String source) { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java index b70516fa66..b3d699086e 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java @@ -73,7 +73,7 @@ public class IdentifierEndpoint extends AbstractEndpoint { this.identifierService = identifierService; } - @GetMapping(produces = {MediaType.APPLICATION_JSON_VALUE, "application/ld+json"}) + @GetMapping @Transactional(readOnly = true) @Observed(name = "dbrepo_identifier_list") @Operation(summary = "List identifiers", @@ -87,48 +87,41 @@ public class IdentifierEndpoint extends AbstractEndpoint { @Content(mediaType = "application/ld+json", array = @ArraySchema(schema = @Schema(implementation = LdDatasetDto.class))) }), - @ApiResponse(responseCode = "406", - description = "Identifier could not be exported, the requested style is not known", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<?> findAll(@Valid @RequestParam(value = "dbid", required = false) Long dbid, + public ResponseEntity<?> findAll(@Valid @RequestParam(value = "type", required = false) IdentifierTypeDto type, + @Valid @RequestParam(value = "status", required = false) IdentifierStatusTypeDto status, + @Valid @RequestParam(value = "dbid", required = false) Long dbid, @Valid @RequestParam(value = "qid", required = false) Long qid, @Valid @RequestParam(value = "vid", required = false) Long vid, @Valid @RequestParam(value = "tid", required = false) Long tid, - @RequestHeader(HttpHeaders.ACCEPT) String accept) - throws FormatNotAvailableException { - log.debug("endpoint find identifiers, dbid={}, qid={}, vid={}, tid={}, accept={}", dbid, qid, vid, tid, accept); + @RequestHeader(HttpHeaders.ACCEPT) String accept, + Principal principal) { + log.debug("endpoint find identifiers, type={}, status={}, dbid={}, qid={}, vid={}, tid={}, accept={}", type, + status, dbid, qid, vid, tid, accept); final List<Identifier> identifiers = identifierService.findAll() .stream() + .filter(i -> !Objects.nonNull(type) || metadataMapper.identifierTypeDtoToIdentifierType(type).equals(i.getType())) + .filter(i -> !Objects.nonNull(status) || metadataMapper.identifierStatusTypeDtoToIdentifierStatusType(status).equals(i.getStatus())) .filter(i -> !Objects.nonNull(dbid) || dbid.equals(i.getDatabase().getId())) .filter(i -> !Objects.nonNull(qid) || qid.equals(i.getQueryId())) .filter(i -> !Objects.nonNull(vid) || vid.equals(i.getViewId())) .filter(i -> !Objects.nonNull(tid) || tid.equals(i.getTableId())) + .filter(i -> principal != null && i.getStatus().equals(IdentifierStatusType.DRAFT) ? i.getOwnedBy().equals(getId(principal)) : i.getStatus().equals(IdentifierStatusType.PUBLISHED)) .toList(); if (identifiers.isEmpty()) { return ResponseEntity.ok(List.of()); } log.trace("found persistent identifiers {}", identifiers); - return switch (accept) { - case "application/json" -> { - log.trace("accept header matches json"); - yield ResponseEntity.ok(identifiers.stream() - .map(metadataMapper::identifierToIdentifierBriefDto) - .toList()); - } - case "application/ld+json" -> { - log.trace("accept header matches json-ld"); - yield ResponseEntity.ok(identifiers.stream() - .map(i -> metadataMapper.identifierToLdDatasetDto(i, endpointConfig.getWebsiteUrl())) - .toList()); - } - default -> { - log.error("accept header {} is not supported", accept); - throw new FormatNotAvailableException("Must provide either application/json or application/ld+json headers"); - } - }; + if (accept.equals("application/ld+json")) { + log.trace("accept header matches json-ld"); + return ResponseEntity.ok(identifiers.stream() + .map(i -> metadataMapper.identifierToLdDatasetDto(i, endpointConfig.getWebsiteUrl())) + .toList()); + } + log.trace("default to json"); + return ResponseEntity.ok(identifiers.stream() + .map(metadataMapper::identifierToIdentifierBriefDto) + .toList()); } @GetMapping(value = "/{identifierId}", produces = {MediaType.APPLICATION_JSON_VALUE, "application/ld+json", @@ -156,6 +149,11 @@ public class IdentifierEndpoint extends AbstractEndpoint { content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), + @ApiResponse(responseCode = "403", + description = "Not allowed to view identifier", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), @ApiResponse(responseCode = "404", description = "Identifier could not be found", content = {@Content( @@ -188,14 +186,23 @@ public class IdentifierEndpoint extends AbstractEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<?> find(@Valid @PathVariable("identifierId") Long identifierId, - @RequestHeader(HttpHeaders.ACCEPT) String accept) throws IdentifierNotFoundException, + @RequestHeader(HttpHeaders.ACCEPT) String accept, + Principal principal) throws IdentifierNotFoundException, DataServiceException, DataServiceConnectionException, MalformedException, FormatNotAvailableException, - QueryNotFoundException { + QueryNotFoundException, NotAllowedException { log.debug("endpoint find identifier, identifierId={}, accept={}", identifierId, accept); if (accept == null) { accept = ""; } final Identifier identifier = identifierService.find(identifierId); + if (identifier.getStatus().equals(IdentifierStatusType.DRAFT)) { + if (principal == null) { + throw new NotAllowedException("Draft identifier: authentication required"); + } + if (!identifier.getOwnedBy().equals(getId(principal))) { + throw new NotAllowedException("Draft identifier: not authorized"); + } + } log.info("Found persistent identifier with id: {}", identifier.getId()); switch (accept) { case "application/json": diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/converters/IdentifierTypeConverterUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/converters/IdentifierTypeConverterUnitTest.java index 7215e5db91..b61a39dc0e 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/converters/IdentifierTypeConverterUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/converters/IdentifierTypeConverterUnitTest.java @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.*; public class IdentifierTypeConverterUnitTest extends AbstractUnitTest { @Autowired - private IdentifierTypeConverter identifierTypeConverter; + private IdentifierTypeDtoConverter identifierTypeConverter; @BeforeEach public void beforeEach() { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java index 74a252c5a6..3b06b974d8 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java @@ -122,12 +122,49 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { ); } + public static Stream<Arguments> findAll_anonymousFilterDatabase_parameters() { + return Stream.of( + Arguments.arguments("dbid", DATABASE_1_ID, null, null, null, 1), + Arguments.arguments("qid", DATABASE_1_ID, QUERY_1_ID, null, null, 0), + Arguments.arguments("vid", DATABASE_1_ID, null, VIEW_1_ID, null, 0), + Arguments.arguments("tid", DATABASE_1_ID, null, null, TABLE_1_ID, 0) + ); + } + public static Stream<Arguments> findAll_filterDatabase_parameters() { return Stream.of( - Arguments.arguments("dbid", DATABASE_1_ID, null, null, null, 4), - Arguments.arguments("qid", DATABASE_1_ID, QUERY_1_ID, null, null, 1), - Arguments.arguments("vid", DATABASE_1_ID, null, VIEW_1_ID, null, 1), - Arguments.arguments("tid", DATABASE_1_ID, null, null, TABLE_1_ID, 1) + Arguments.arguments("database_dbid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, null, null, 1, USER_1_PRINCIPAL), + Arguments.arguments("database_qid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, QUERY_1_ID, null, null, 0, USER_1_PRINCIPAL), + Arguments.arguments("database_vid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, VIEW_1_ID, null, 0, USER_1_PRINCIPAL), + Arguments.arguments("database_tid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, null, TABLE_1_ID, 0, USER_1_PRINCIPAL), + Arguments.arguments("subset_dbid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, null, null, null, 1, USER_1_PRINCIPAL), + Arguments.arguments("subset_qid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, QUERY_1_ID, null, null, 1, USER_1_PRINCIPAL), + Arguments.arguments("subset_vid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, null, VIEW_1_ID, null, 0, USER_1_PRINCIPAL), + Arguments.arguments("subset_tid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, null, null, TABLE_1_ID, 0, USER_1_PRINCIPAL), + Arguments.arguments("view_dbid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, null, null, null, 1, USER_1_PRINCIPAL), + Arguments.arguments("view_qid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, QUERY_1_ID, null, null, 0, USER_1_PRINCIPAL), + Arguments.arguments("view_vid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, null, VIEW_1_ID, null, 1, USER_1_PRINCIPAL), + Arguments.arguments("view_tid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, null, null, TABLE_1_ID, 0, USER_1_PRINCIPAL), + Arguments.arguments("table_dbid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, null, null, null, 1, USER_1_PRINCIPAL), + Arguments.arguments("table_qid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, QUERY_1_ID, null, null, 0, USER_1_PRINCIPAL), + Arguments.arguments("table_vid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, null, VIEW_1_ID, null, 0, USER_1_PRINCIPAL), + Arguments.arguments("table_tid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, null, null, TABLE_1_ID, 1, USER_1_PRINCIPAL), + Arguments.arguments("anon_database_dbid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, null, null, 1, null), + Arguments.arguments("anon_database_qid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, QUERY_1_ID, null, null, 0, null), + Arguments.arguments("anon_database_vid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, VIEW_1_ID, null, 0, null), + Arguments.arguments("anon_database_tid", IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, null, TABLE_1_ID, 0, null), + Arguments.arguments("anon_subset_dbid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, null, null, null, 1, null), + Arguments.arguments("anon_subset_qid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, QUERY_1_ID, null, null, 1, null), + Arguments.arguments("anon_subset_vid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, null, VIEW_1_ID, null, 0, null), + Arguments.arguments("anon_subset_tid", IdentifierTypeDto.SUBSET, null, DATABASE_1_ID, null, null, TABLE_1_ID, 0, null), + Arguments.arguments("anon_view_dbid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, null, null, null, 1, null), + Arguments.arguments("anon_view_qid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, QUERY_1_ID, null, null, 0, null), + Arguments.arguments("anon_view_vid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, null, VIEW_1_ID, null, 1, null), + Arguments.arguments("anon_view_tid", IdentifierTypeDto.VIEW, null, DATABASE_1_ID, null, null, TABLE_1_ID, 0, null), + Arguments.arguments("anon_table_dbid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, null, null, null, 1, null), + Arguments.arguments("anon_table_qid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, QUERY_1_ID, null, null, 0, null), + Arguments.arguments("anon_table_vid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, null, VIEW_1_ID, null, 0, null), + Arguments.arguments("anon_table_tid", IdentifierTypeDto.TABLE, null, DATABASE_1_ID, null, null, TABLE_1_ID, 1, null) ); } @@ -146,14 +183,14 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findAll_empty_succeeds() throws FormatNotAvailableException { + public void findAll_empty_succeeds() { /* mock */ when(identifierService.findAll()) .thenReturn(List.of()); /* test */ - final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, "application/json"); + final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, null, null, "application/json", null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody(); @@ -161,12 +198,69 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { assertEquals(0, identifiers.size()); } + @ParameterizedTest + @MethodSource("findAll_anonymousFilterDatabase_parameters") + @WithAnonymousUser + public void findAll_anonymousFilterDatabase_succeeds(String name, Long databaseId, Long queryId, Long viewId, Long tableId, + Integer expectedSize) throws ViewNotFoundException, + TableNotFoundException, DatabaseNotFoundException { + + /* mock */ + when(identifierService.findAll()) + .thenReturn(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4, IDENTIFIER_5, IDENTIFIER_6, IDENTIFIER_7)); + if (viewId != null) { + when(viewService.findById(DATABASE_1, VIEW_1_ID)) + .thenReturn(VIEW_1); + } + if (tableId != null) { + when(tableService.findById(DATABASE_1, TABLE_1_ID)) + .thenReturn(TABLE_1); + } + + /* test */ + final ResponseEntity<?> response = identifierEndpoint.findAll(IdentifierTypeDto.DATABASE, null, databaseId, queryId, viewId, tableId, "application/json", null); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody(); + assertNotNull(identifiers); + assertEquals(expectedSize, identifiers.size()); + } + + @ParameterizedTest + @MethodSource("findAll_anonymousFilterDatabase_parameters") + @WithAnonymousUser + public void findAll_wrongPrincipalFilterDatabase_succeeds(String name, Long databaseId, Long queryId, Long viewId, + Long tableId, Integer expectedSize) + throws ViewNotFoundException, TableNotFoundException, DatabaseNotFoundException { + + /* mock */ + when(identifierService.findAll()) + .thenReturn(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4, IDENTIFIER_5, IDENTIFIER_6, IDENTIFIER_7)); + if (viewId != null) { + when(viewService.findById(DATABASE_1, VIEW_1_ID)) + .thenReturn(VIEW_1); + } + if (tableId != null) { + when(tableService.findById(DATABASE_1, TABLE_1_ID)) + .thenReturn(TABLE_1); + } + + /* test */ + final ResponseEntity<?> response = identifierEndpoint.findAll(IdentifierTypeDto.DATABASE, null, databaseId, queryId, viewId, tableId, "application/json", USER_2_PRINCIPAL); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody(); + assertNotNull(identifiers); + assertEquals(expectedSize, identifiers.size()); + } + @ParameterizedTest @MethodSource("findAll_filterDatabase_parameters") @WithAnonymousUser - public void findAll_filterDatabase_succeeds(String name, Long databaseId, Long queryId, Long viewId, Long tableId, - Integer expectedSize) throws FormatNotAvailableException, - ViewNotFoundException, TableNotFoundException, DatabaseNotFoundException { + public void findAll_filterDatabase_succeeds(String name, IdentifierTypeDto type, IdentifierStatusTypeDto status, + Long databaseId, Long queryId, Long viewId, Long tableId, + Integer expectedSize, Principal principal) throws ViewNotFoundException, + TableNotFoundException, DatabaseNotFoundException { /* mock */ when(identifierService.findAll()) @@ -181,7 +275,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } /* test */ - final ResponseEntity<?> response = identifierEndpoint.findAll(databaseId, queryId, viewId, tableId, "application/json"); + final ResponseEntity<?> response = identifierEndpoint.findAll(type, status, databaseId, queryId, viewId, tableId, "application/json", principal); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody(); @@ -191,14 +285,14 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findAll_json_succeeds() throws FormatNotAvailableException { + public void findAll_json_succeeds() { /* mock */ when(identifierService.findAll()) .thenReturn(List.of(IDENTIFIER_1)); /* test */ - final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, "application/json"); + final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, null, null, "application/json", null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody(); @@ -208,14 +302,14 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findAll_jsonLd_succeeds() throws FormatNotAvailableException { + public void findAll_jsonLd_succeeds() { /* mock */ when(identifierService.findAll()) .thenReturn(List.of(IDENTIFIER_1)); /* test */ - final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, "application/ld+json"); + final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, null, null, "application/ld+json", null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); final List<LdDatasetDto> identifiers = (List<LdDatasetDto>) response.getBody(); @@ -225,23 +319,26 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findAll_format_fails() { + public void findAll_format_succeeds() { /* mock */ when(identifierService.findAll()) .thenReturn(List.of(IDENTIFIER_1)); /* test */ - assertThrows(FormatNotAvailableException.class, () -> { - identifierEndpoint.findAll(null, null, null, null, "text/csv"); - }); + final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, null, null, "text/html", null); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody(); + assertNotNull(identifiers); + assertEquals(1, identifiers.size()); } @Test - @WithAnonymousUser + @WithMockUser(username = USER_4_USERNAME) public void find_json0_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "application/json"; final IdentifierDto compare = objectMapper.readValue(FileUtils.readFileToString(new File("src/test/resources/json/metadata0.json"), StandardCharsets.UTF_8), IdentifierDto.class); @@ -250,7 +347,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_7); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept, USER_4_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final IdentifierDto body = (IdentifierDto) response.getBody(); assertNotNull(body); @@ -271,7 +368,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_json1_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "application/json"; final IdentifierDto compare = objectMapper.readValue(FileUtils.readFileToString(new File("src/test/resources/json/metadata1.json"), StandardCharsets.UTF_8), IdentifierDto.class); @@ -280,7 +377,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final IdentifierDto body = (IdentifierDto) response.getBody(); assertNotNull(body); @@ -321,7 +418,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_csv_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/csv"; final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/csv/keyboard.csv"))); final InputStreamResource mock = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/csv/keyboard.csv"))); @@ -333,7 +430,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(mock); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_2_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_2_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final InputStreamResource body = (InputStreamResource) response.getBody(); assertNotNull(body); @@ -344,7 +441,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliography_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa1.txt"), StandardCharsets.UTF_8); @@ -356,7 +453,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -365,9 +462,29 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser + public void find_anonymousBibliographyApa0_fails() throws IOException, MalformedException, + IdentifierNotFoundException { + final String accept = "text/bibliography; style=apa"; + final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa0.txt"), + StandardCharsets.UTF_8); + + /* mock */ + when(identifierService.exportBibliography(IDENTIFIER_7, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_7_ID)) + .thenReturn(IDENTIFIER_7); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + identifierEndpoint.find(IDENTIFIER_7_ID, accept, null); + }); + } + + @Test + @WithMockUser(username = USER_4_USERNAME) public void find_bibliographyApa0_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=apa"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa0.txt"), StandardCharsets.UTF_8); @@ -379,7 +496,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_7); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept, USER_4_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -390,7 +507,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyApa1_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=apa"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa1.txt"), StandardCharsets.UTF_8); @@ -402,7 +519,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -410,10 +527,10 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } @Test - @WithAnonymousUser - public void find_bibliographyApa2_succeeds() throws IOException, MalformedException, DataServiceException, + @WithMockUser(username = USER_2_USERNAME) + public void find_draftBibliographyApa2_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=apa"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa2.txt"), StandardCharsets.UTF_8); @@ -425,7 +542,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_5); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_5_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_5_ID, accept, USER_2_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -436,7 +553,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyApa3_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=apa"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa3.txt"), StandardCharsets.UTF_8); @@ -448,7 +565,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_6); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_6_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_6_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -459,7 +576,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyApa4_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=apa"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa4.txt"), StandardCharsets.UTF_8); @@ -471,7 +588,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1_WITH_DOI); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -479,10 +596,10 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } @Test - @WithAnonymousUser + @WithMockUser(username = USER_4_USERNAME) public void find_bibliographyIeee0_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=ieee"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee0.txt"), StandardCharsets.UTF_8); @@ -494,7 +611,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_7); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept, USER_4_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -505,7 +622,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyIeee1_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=ieee"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee1.txt"), StandardCharsets.UTF_8); @@ -517,7 +634,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -525,10 +642,10 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } @Test - @WithAnonymousUser + @WithMockUser(username = USER_2_USERNAME) public void find_bibliographyIeee2_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=ieee"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee2.txt"), StandardCharsets.UTF_8); @@ -540,7 +657,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_5); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_5_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_5_ID, accept, USER_2_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -551,7 +668,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyIeee3_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=ieee"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee3.txt"), StandardCharsets.UTF_8); @@ -563,7 +680,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1_WITH_DOI); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -571,10 +688,10 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } @Test - @WithAnonymousUser + @WithMockUser(username = USER_4_USERNAME) public void find_bibliographyBibtex0_succeeds() throws IOException, MalformedException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=bibtex"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex0.txt"), StandardCharsets.UTF_8); @@ -586,7 +703,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_7); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_7_ID, accept, USER_4_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -597,7 +714,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyBibtex1_succeeds() throws MalformedException, IOException, DataServiceException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=bibtex"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex1.txt"), StandardCharsets.UTF_8); @@ -609,7 +726,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -617,10 +734,10 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } @Test - @WithAnonymousUser + @WithMockUser(username = USER_2_USERNAME) public void find_bibliographyBibtex2_succeeds() throws MalformedException, DataServiceException, IOException, DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=bibtex"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex2.txt"), StandardCharsets.UTF_8); @@ -632,7 +749,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_5); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_5_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_5_ID, accept, USER_2_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -643,7 +760,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_bibliographyBibtex3_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, IOException, QueryNotFoundException, IdentifierNotFoundException, - FormatNotAvailableException { + FormatNotAvailableException, NotAllowedException { final String accept = "text/bibliography; style=bibtex"; final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex3.txt"), StandardCharsets.UTF_8); @@ -655,7 +772,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1_WITH_DOI); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final String body = (String) response.getBody(); assertNotNull(body); @@ -665,7 +782,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void find_jsonLd_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, - QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException { + QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, NotAllowedException { final String accept = "application/ld+json"; /* mock */ @@ -673,7 +790,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final LdDatasetDto body = (LdDatasetDto) response.getBody(); assertNotNull(body); @@ -689,22 +806,22 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_7); /* test */ - assertThrows(FormatNotAvailableException.class, () -> { - identifierEndpoint.find(IDENTIFIER_7_ID, accept); + assertThrows(NotAllowedException.class, () -> { + identifierEndpoint.find(IDENTIFIER_7_ID, accept, null); }); } @Test @WithAnonymousUser public void find_move_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, - QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException { + QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, NotAllowedException { /* mock */ when(identifierService.find(IDENTIFIER_1_ID)) .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, null); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, null, null); assertEquals(HttpStatus.MOVED_PERMANENTLY, response.getStatusCode()); } @@ -848,7 +965,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void find_json_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, - FormatNotAvailableException, QueryNotFoundException, IdentifierNotFoundException { + FormatNotAvailableException, QueryNotFoundException, IdentifierNotFoundException, NotAllowedException { final String accept = "application/json"; /* mock */ @@ -856,7 +973,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { .thenReturn(IDENTIFIER_1); /* test */ - final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept); + final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final IdentifierDto body = (IdentifierDto) response.getBody(); assertNotNull(body); @@ -875,7 +992,8 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void find_xml_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, - IOException, QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException { + IOException, QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, + NotAllowedException { final InputStreamResource resource = new InputStreamResource(FileUtils.openInputStream( new File("src/test/resources/xml/datacite-example-dataset-v4.xml"))); @@ -892,7 +1010,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void find_httpRedirect_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, FormatNotAvailableException, QueryNotFoundException, - IdentifierNotFoundException { + IdentifierNotFoundException, NotAllowedException { /* test */ final ResponseEntity<?> response = generic_find(null, null); @@ -1291,7 +1409,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { protected ResponseEntity<?> generic_find(String accept, InputStreamResource resource) throws MalformedException, DataServiceException, DataServiceConnectionException, FormatNotAvailableException, - QueryNotFoundException, IdentifierNotFoundException { + QueryNotFoundException, IdentifierNotFoundException, NotAllowedException { /* mock */ when(identifierService.find(IDENTIFIER_1_ID)) @@ -1304,7 +1422,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest { } /* test */ - return identifierEndpoint.find(IDENTIFIER_1_ID, accept); + return identifierEndpoint.find(IDENTIFIER_1_ID, accept, null); } protected static String inputStreamToString(InputStream inputStream) throws IOException { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index 632affcf91..e22a7a4a05 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -6,6 +6,7 @@ import at.tuwien.api.database.DatabaseModifyImageDto; import at.tuwien.api.database.DatabaseModifyVisibilityDto; import at.tuwien.api.database.DatabaseTransferDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; +import at.tuwien.api.identifier.IdentifierTypeDto; import at.tuwien.config.MetricsConfig; import at.tuwien.endpoints.*; import at.tuwien.test.AbstractUnitTest; @@ -278,7 +279,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - identifierEndpoint.findAll(DATABASE_1_ID, null, null, null, MediaType.APPLICATION_JSON_VALUE); + identifierEndpoint.findAll(IdentifierTypeDto.DATABASE, null, DATABASE_1_ID, null, null, null, MediaType.APPLICATION_JSON_VALUE, null); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-ui/components/ResourceStatus.vue b/dbrepo-ui/components/ResourceStatus.vue index d766f37660..6db6d25385 100644 --- a/dbrepo-ui/components/ResourceStatus.vue +++ b/dbrepo-ui/components/ResourceStatus.vue @@ -5,7 +5,7 @@ v-if="!inline" :size="size" :color="color" - variant="outlined"> + :variant="chipVariant"> {{ status }} </v-chip> <span @@ -39,6 +39,9 @@ export default { if (!this.resource) { return null } + if (this.hasIdentifier) { + return 'pid' + } if (!this.resource.is_public && !this.resource.is_schema_public) { return 'draft' } else if(!this.resource.is_public && this.resource.is_schema_public) { @@ -54,7 +57,19 @@ export default { } return this.$t(`pages.database.status.${this.mode}`) }, + hasIdentifier () { + return this.resource.identifiers?.length > 0 + }, + chipVariant () { + if (this.hasIdentifier) { + return 'tonal' + } + return 'outlined' + }, color () { + if (this.hasIdentifier) { + return 'info' + } switch (this.mode) { case 'schema': case 'data': diff --git a/dbrepo-ui/components/identifier/Banner.vue b/dbrepo-ui/components/identifier/Banner.vue index 7c77a1b28b..63c2a7153a 100644 --- a/dbrepo-ui/components/identifier/Banner.vue +++ b/dbrepo-ui/components/identifier/Banner.vue @@ -24,7 +24,7 @@ export default { return identifierService.identifierToDisplayName(this.identifier) }, href () { - if (!this.identifier || (this.identifier.status && this.identifier.status !== 'published')) { + if (!this.identifier) { return null } const identifierService = useIdentifierService() diff --git a/dbrepo-ui/components/identifier/Persist.vue b/dbrepo-ui/components/identifier/Persist.vue index b02ff09b76..f37c5c6d7d 100644 --- a/dbrepo-ui/components/identifier/Persist.vue +++ b/dbrepo-ui/components/identifier/Persist.vue @@ -12,6 +12,7 @@ <v-spacer /> <v-btn v-if="canSave" + class="mr-2" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null" color="secondary" variant="flat" @@ -22,7 +23,7 @@ @click="createOrSave"/> <v-btn v-if="canRemove" - class="ml-2" + class="mr-2" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null" color="error" variant="flat" @@ -32,7 +33,7 @@ @click="remove" /> <v-btn v-if="canPublish" - class="ml-2" + class="mr-2" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null" color="primary" variant="flat" @@ -138,14 +139,6 @@ :color="canShiftUp(creator, i) ? 'tertiary' : ''" :variant="buttonVariant" @click="shiftDown(i)" /> - <v-btn - v-if="canInsertSelf" - class="mr-2" - size="small" - color="secondary" - variant="flat" - :text="$t('pages.identifier.subpages.create.creators.insert.text')" - @click="insertSelf(creator)" /> <v-btn v-if="i > 0" size="small" @@ -1043,12 +1036,6 @@ export default { } } }, - canInsertSelf () { - if (!this.cacheUser) { - return false - } - return this.cacheUser.given_name || this.cacheUser.family_name || this.cacheUser.attributes.affiliation || this.cacheUser.attributes.orcid - }, isCreator () { if (!this.cacheUser || !this.identifier) { return false diff --git a/dbrepo-ui/components/identifier/Select.vue b/dbrepo-ui/components/identifier/Select.vue index 4ef13b2ca7..e557286614 100644 --- a/dbrepo-ui/components/identifier/Select.vue +++ b/dbrepo-ui/components/identifier/Select.vue @@ -8,7 +8,7 @@ :color="color(identifier)" :variant="listVariant" :href="href(identifier)" - :title="formatTimestampUTCLabel(identifier.created)" + :title="title(identifier)" lines="two"> <v-list-item-subtitle> <Banner @@ -59,7 +59,7 @@ export default { identifier: { type: Object, default () { - return {} + return null } } }, @@ -71,11 +71,14 @@ export default { }, computed: { cacheUser () { - return this.cacheUser.getUser + return this.cacheStore.getUser }, displayIdentifiers () { - if (!this.identifiers) { - return [] + if (!this.identifiers || this.identifiers.length === 0) { + if (!this.identifier) { + return [] + } + return [this.identifier] } if (!this.cacheUser) { return this.identifiers.filter(i => i.status === 'published') @@ -100,6 +103,9 @@ export default { }, methods: { href (identifier) { + if (!identifier) { + return null + } if (identifier.status === 'published') { return `/pid/${identifier.id}` } @@ -114,6 +120,13 @@ export default { return `/database/${identifier.database_id}/view/${identifier.view_id}/persist/${identifier.id}` } }, + title (identifier) { + if (!identifier) { + return null + } + const identifierService = useIdentifierService() + return identifierService.identifierPreferEnglishTitle(identifier) + }, isActive (identifier) { if (!identifier) { return false diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue index 4261b098b6..f19c595aad 100644 --- a/dbrepo-ui/components/subset/Builder.vue +++ b/dbrepo-ui/components/subset/Builder.vue @@ -1,6 +1,5 @@ <template> - <div - v-if="loggedIn"> + <div> <v-toolbar flat> <v-btn size="small" @@ -75,7 +74,7 @@ required clearable :rules="[ - v => !!v || $t('validation.required') + v => v !== null || $t('validation.required') ]" :label="$t('pages.database.resource.data.label')" :hint="$t('pages.database.resource.data.hint')" /> @@ -90,7 +89,7 @@ required clearable :rules="[ - v => !!v || $t('validation.required') + v => v !== null || $t('validation.required') ]" :label="$t('pages.database.resource.schema.label')" :hint="$t('pages.database.resource.schema.hint', { resource: 'subset', schema: 'query' })" /> @@ -445,7 +444,7 @@ export default { if (this.isView) { return this.view.name !== null && this.view.is_public !== null && this.view.query !== null } - return this.sql !== null && !this.sql.includes(';') + return this.sql !== null && this.sql !== '' && !this.sql.includes(';') }, inputVariant () { const runtimeConfig = useRuntimeConfig() diff --git a/dbrepo-ui/components/subset/SubsetList.vue b/dbrepo-ui/components/subset/SubsetList.vue index eb6a27aacd..fa44454af8 100644 --- a/dbrepo-ui/components/subset/SubsetList.vue +++ b/dbrepo-ui/components/subset/SubsetList.vue @@ -6,6 +6,7 @@ rounded="0" :text="$t('pages.database.subpages.subsets.empty')" /> <v-card + v-if="subsets.length > 0" variant="flat" rounded="0"> <v-list-item @@ -14,28 +15,20 @@ <Loading /> </v-list-item> <div - v-for="(item, i) in subsets" + v-for="(subset, i) in subsets" :key="`q-${i}`"> <v-divider v-if="i !== 0" class="mx-4" /> <v-list> <v-list-item lines="two" - :title="title(item)" - :subtitle="subtitle(item)" - :class="clazz(item)" - :to="link(item)" - :href="link(item)"> + :title="title(subset)" + :subtitle="subtitle(subset)" + :class="clazz(subset)" + :to="link(subset)" + :href="link(subset)"> <template v-slot:append> - <v-tooltip - v-if="hasPublishedIdentifier(item)" - :text="$t('pages.identifier.pid.title')" - left> - <template v-slot:activator="{ props }"> - <v-icon - color="primary" - v-bind="props">mdi-identifier</v-icon> - </template> - </v-tooltip> + <ResourceStatus + :resource="subset" /> </template> </v-list-item> </v-list> diff --git a/dbrepo-ui/components/subset/SubsetToolbar.vue b/dbrepo-ui/components/subset/SubsetToolbar.vue index 61e26e9967..e602609097 100644 --- a/dbrepo-ui/components/subset/SubsetToolbar.vue +++ b/dbrepo-ui/components/subset/SubsetToolbar.vue @@ -112,9 +112,7 @@ export default { if (this.pid) { const filter = this.identifiers.filter(i => i.id === Number(this.pid)) if (filter.length > 0) { - const identifier = filter[0] - console.debug('identifier set according to route pid', identifier) - return identifier + return filter[0] } } return this.identifiers[0] diff --git a/dbrepo-ui/components/table/TableList.vue b/dbrepo-ui/components/table/TableList.vue index 71106f7c65..5f87090b85 100644 --- a/dbrepo-ui/components/table/TableList.vue +++ b/dbrepo-ui/components/table/TableList.vue @@ -21,16 +21,6 @@ <template v-slot:append> <ResourceStatus :resource="table" /> - <v-tooltip - v-if="hasPublishedIdentifier(table)" - :text="$t('pages.identifier.pid.title')" - left> - <template v-slot:activator="{ props }"> - <v-icon - color="primary" - v-bind="props">mdi-identifier</v-icon> - </template> - </v-tooltip> </template> </v-list-item> </v-list> diff --git a/dbrepo-ui/components/view/ViewList.vue b/dbrepo-ui/components/view/ViewList.vue index 58038b7a82..afa3067921 100644 --- a/dbrepo-ui/components/view/ViewList.vue +++ b/dbrepo-ui/components/view/ViewList.vue @@ -16,16 +16,6 @@ <template v-slot:append> <ResourceStatus :resource="view" /> - <v-tooltip - v-if="hasPublishedIdentifier(view)" - :text="$t('pages.identifier.pid.title')" - left> - <template v-slot:activator="{ props }"> - <v-icon - color="primary" - v-bind="props">mdi-identifier</v-icon> - </template> - </v-tooltip> </template> </v-list-item> </v-list> diff --git a/dbrepo-ui/composables/identifier-service.ts b/dbrepo-ui/composables/identifier-service.ts index 3ae194ff2f..6875a7cb7b 100644 --- a/dbrepo-ui/composables/identifier-service.ts +++ b/dbrepo-ui/composables/identifier-service.ts @@ -24,7 +24,7 @@ export const useIdentifierService = (): any => { } async function create(data: IdentifierSaveDto): Promise<IdentifierDto> { - const axios= useAxiosInstance() + const axios = useAxiosInstance() console.debug('create identifier') return new Promise<IdentifierDto>((resolve, reject) => { axios.post<IdentifierDto>('/api/identifier', data) @@ -40,7 +40,7 @@ export const useIdentifierService = (): any => { } async function save(data: IdentifierSaveDto): Promise<IdentifierDto> { - const axios= useAxiosInstance() + const axios = useAxiosInstance() console.debug('save identifier', data.id) return new Promise<IdentifierDto>((resolve, reject) => { axios.put<IdentifierDto>(`/api/identifier/${data.id}`, data) @@ -241,13 +241,28 @@ export const useIdentifierService = (): any => { if (!data || !data.titles || data.titles.length === 0) { return null } - const filtered = data.titles.filter(d => d.language && d.language === 'en') + const filtered = data.titles.filter((d) => d.language && d.language === 'en') if (filtered.length === 0) { - return data.titles[0].title + const title = data.titles[0] + return title.title } return filtered[0].title } + function identifierToResourceUrl(identifier: IdentifierDto): string | null { + const config = useRuntimeConfig() + switch (identifier.type) { + case 1: + return `${config.public.api.client}/api/database/${identifier.database_id}/subset/${identifier.subset_id}/data` + case 2: + return `${config.public.api.client}/api/database/${identifier.database_id}/table/${identifier.table_id}/data` + case 3: + return `${config.public.api.client}/api/database/${identifier.database_id}/view/${identifier.view_id}/data` + default: + return null + } + } + function identifierToUrl(data: IdentifierDto): string | null { if (!data) { return null @@ -315,244 +330,48 @@ export const useIdentifierService = (): any => { return jsonLd } - function identifierToHasPartJsonLd(identifier: IdentifierDto) { - return { - '@type': 'Dataset', - name: identifierPreferEnglishTitle(identifier), - description: identifierPreferEnglishDescription(identifier), - identifier: identifierToUrl(identifier), - citation: identifierToUrl(identifier), - temporalCoverage: identifier.publication_year, - version: identifier.created - } - } - - function databaseToServerHead(database: DatabaseDto) { - if (!database) { - return + function identifiersToServerHead(identifiers: IdentifierBriefDto[]): any { + if (!identifiers || !identifiers[0]) { + return null } - const config = useRuntimeConfig() + const identifier = identifiers[0] /* Google Rich Results */ const json: any = { '@context': 'https://schema.org/', '@type': 'Dataset', - url: `${config.public.api.client}/database/${database.id}/info`, - citation: `${config.public.api.client}/database/${database.id}/info`, + url: identifierToUrl(identifier), + citation: identifierToUrl(identifier), hasPart: [], - version: database.created + identifier: identifiers.map(i => identifierToUrl(i)), + creator: identifier.creators.map((c) => creatorToCreatorJsonLd(c)), + temporalCoverage: identifier.publication_year } - /* FAIR Signposting */ - const meta: any [] = [] - if (database.identifiers.length > 0) { - const identifier = database.identifiers[0] - const partIdentifiers: IdentifierDto[] = [] - if (database.subsets.length > 0) { - database.subsets.forEach((s) => { - partIdentifiers.push(s) - }) - } - if (database.tables.length > 0) { - database.tables.forEach((t) => { - if (t.identifiers.length > 0) { - t.identifiers.forEach(i => partIdentifiers.push(i)) - } - }) - } - if (database.views.length > 0) { - database.views.forEach((v) => { - if (v.identifiers.length > 0) { - v.identifiers.forEach(i => partIdentifiers.push(i)) - } - }) - } + if (identifier.titles.length > 0) { json['name'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['identifier'] = database.identifiers.map(i => identifierToUrl(i)) - json['license'] = identifierToPreferFirstLicenseUri(identifier) - json['creator'] = identifier.creators.map(c => creatorToCreatorJsonLd(c)) - json['citation'] = identifierToUrl(identifier) - json['hasPart'] = partIdentifiers.map(i => identifierToHasPartJsonLd(i)) - json['temporalCoverage'] = identifier.publication_year - meta.push({rel: 'cite-as', href: identifierToUrl(identifier)}) - identifier.creators.forEach((c: CreatorDto) => { - if (c.name_identifier) { - meta.push({rel: 'author', href: c.name_identifier}) - } - }) - meta.push({rel: 'describedby', type: 'application/x-bibtex', href: identifierToUrl(identifier)}) - meta.push({rel: 'describedby', type: 'application/vnd.datacite.datacite+json', href: identifierToUrl(identifier)}) - if (identifier.licenses) { - identifier.licenses.forEach((l: LicenseDto) => meta.push({rel: 'license', href: l.uri})) - } - } - return { - script: [ - { - type: 'application/ld+json', - innerHTML: json - } - ], - link: meta } - } - - function subsetToServerHead(subset: QueryDto) { - const config = useRuntimeConfig() - /* Google Rich Results */ - const json: any = { - '@context': 'https://schema.org/', - '@type': 'Dataset', - description: subset.query, - url: `${config.public.api.client}/database/${subset.database_id}/info`, - citation: `${config.public.api.client}/database/${subset.database_id}/info`, - hasPart: [], - version: subset.created - } - /* FAIR Signposting */ - const meta: any[] = [] - if (subset.identifiers.length > 0) { - const identifier = subset.identifiers[0] - json['name'] = identifierPreferEnglishTitle(identifier) + if (identifier.descriptions.length > 0) { json['description'] = identifierPreferEnglishDescription(identifier) - json['identifier'] = subset.identifiers.map(i => identifierToUrl(i)) - json['license'] = identifierToPreferFirstLicenseUri(identifier) - json['creator'] = identifier.creators.map(c => creatorToCreatorJsonLd(c)) - json['citation'] = identifierToUrl(identifier) - json['temporalCoverage'] = identifier.publication_year - meta.push({rel: 'cite-as', href: identifierToUrl(identifier)}) - identifier.creators.forEach((c: CreatorDto) => { - if (c.name_identifier) { - meta.push({rel: 'author', href: c.name_identifier}) - } - }) - meta.push({rel: 'describedby', type: 'application/x-bibtex', href: identifierToUrl(identifier)}) - meta.push({rel: 'describedby', type: 'application/vnd.datacite.datacite+json', href: identifierToUrl(identifier)}) - if (identifier.licenses) { - identifier.licenses.forEach((l: LicenseDto) => meta.push({rel: 'license', href: l.uri})) - } - meta.push({ - rel: 'item', - type: 'application/json', - href: `${config.public.api.client}/api/database/${subset.database_id}/subset/${subset.id}/data` - }) - meta.push({ - rel: 'item', - type: 'text/csv', - href: `${config.public.api.client}/api/database/${subset.database_id}/subset/${subset.id}/data` - }) - } - return { - script: [ - { - type: 'application/ld+json', - innerHTML: json - } - ], - link: meta - } - } - - function tableToServerHead(table: TableDto) { - const config = useRuntimeConfig() - /* Google Rich Results */ - const json: any = { - '@context': 'https://schema.org/', - '@type': 'Dataset', - description: table.description, - url: `${config.public.api.client}/database/${table.database_id}/table/${table.id}/info`, - citation: `${config.public.api.client}/database/${table.database_id}/table/${table.id}/info`, - hasPart: [], - version: table.created } /* FAIR Signposting */ const meta: any[] = [] - if (table.identifiers.length > 0) { - const identifier: IdentifierDto = table.identifiers[0] - json['name'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['identifier'] = table.identifiers.map((i: IdentifierDto) => identifierToUrl(i)) - json['license'] = identifierToPreferFirstLicenseUri(identifier) - json['creator'] = identifier.creators.map((c: CreatorDto) => creatorToCreatorJsonLd(c)) - json['citation'] = identifierToUrl(identifier) - json['temporalCoverage'] = identifier.publication_year - meta.push({rel: 'cite-as', href: identifierToUrl(identifier)}) - identifier.creators.forEach((c: CreatorDto): void => { - if (c.name_identifier) { - meta.push({rel: 'author', href: c.name_identifier}) - } - }) - meta.push({rel: 'describedby', type: 'application/x-bibtex', href: identifierToUrl(identifier)}) - meta.push({rel: 'describedby', type: 'application/vnd.datacite.datacite+json', href: identifierToUrl(identifier)}) - if (identifier.licenses) { - identifier.licenses.forEach((l: LicenseDto) => meta.push({rel: 'license', href: l.uri})) + meta.push({rel: 'cite-as', href: identifierToUrl(identifier)}) + identifier.creators.forEach((c: CreatorDto) => { + if (c.name_identifier) { + meta.push({rel: 'author', href: c.name_identifier}) } - meta.push({ - rel: 'item', - type: 'application/json', - href: `${config.public.api.client}/api/database/${table.database_id}/table/${table.id}/data` - }) - meta.push({ - rel: 'item', - type: 'text/csv', - href: `${config.public.api.client}/api/database/${table.database_id}/table/${table.id}/data` - }) - } - return { - script: [ - { - type: 'application/ld+json', - innerHTML: json - } - ], - link: meta - } - } - - function viewToServerHead(view: ViewDto) { - const config = useRuntimeConfig() - /* Google Rich Results */ - const json: any = { - '@context': 'https://schema.org/', - '@type': 'Dataset', - description: view.query, - url: `${config.public.api.client}/database/${view.database_id}/table/${view.id}/info`, - citation: `${config.public.api.client}/database/${view.database_id}/table/${view.id}/info`, - hasPart: [], - version: view.created - } - /* FAIR Signposting */ - const meta: any[] = [] - if (view.identifiers.length > 0) { - const identifier = view.identifiers[0] - json['name'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['identifier'] = view.identifiers.map(i => identifierToUrl(i)) - json['license'] = identifierToPreferFirstLicenseUri(identifier) - json['creator'] = identifier.creators.map(c => creatorToCreatorJsonLd(c)) - json['citation'] = identifierToUrl(identifier) - json['temporalCoverage'] = identifier.publication_year - meta.push({rel: 'cite-as', href: identifierToUrl(identifier)}) - identifier.creators.forEach((c: CreatorDto) => { - if (c.name_identifier) { - meta.push({rel: 'author', href: c.name_identifier}) - } - }) - meta.push({rel: 'describedby', type: 'application/x-bibtex', href: identifierToUrl(identifier)}) - meta.push({rel: 'describedby', type: 'application/vnd.datacite.datacite+json', href: identifierToUrl(identifier)}) - if (identifier.licenses) { - identifier.licenses.forEach((l: LicenseDto) => meta.push({rel: 'license', href: l.uri})) - } - meta.push({ - rel: 'item', - type: 'application/json', - href: `${config.public.api.client}/api/database/${view.database_id}/view/${view.id}/data` - }) - meta.push({ - rel: 'item', - type: 'text/csv', - href: `${config.public.api.client}/api/database/${view.database_id}/view/${view.id}/data` - }) - } + }) + meta.push({rel: 'describedby', type: 'application/x-bibtex', href: identifierToUrl(identifier)}) + meta.push({rel: 'describedby', type: 'application/vnd.datacite.datacite+json', href: identifierToUrl(identifier)}) + meta.push({ + rel: 'item', + type: 'application/json', + href: identifierToResourceUrl(identifier) + }) + meta.push({ + rel: 'item', + type: 'text/csv', + href: identifierToResourceUrl(identifier) + }) return { script: [ { @@ -564,56 +383,15 @@ export const useIdentifierService = (): any => { } } - function databaseToServerSeoMeta(database: DatabaseDto) { - const json: any = { - ogTitle: database.name - } - if (database.identifiers.length > 0) { - const identifier = database.identifiers[0] - json['ogTitle'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['ogDescription'] = identifierPreferEnglishDescription(identifier) - } - return json - } - - function subsetToServerSeoMeta(subset: QueryDto) { - const json: any = { - description: subset.query - } - if (subset.identifiers.length > 0) { - const identifier = subset.identifiers[0] - json['ogTitle'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['ogDescription'] = identifierPreferEnglishDescription(identifier) - } - return json - } - - function tableToServerSeoMeta(table: TableDto) { - const json: any = { - ogTitle: table.name, - description: table.description - } - if (table.identifiers.length > 0) { - const identifier = table.identifiers[0] - json['ogTitle'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['ogDescription'] = identifierPreferEnglishDescription(identifier) + function identifiersToServerSeoMeta(identifiers: IdentifierBriefDto[]): any | null { + if (!identifiers|| !identifiers[0]) { + return null } - return json - } - - function viewToServerSeoMeta(view: ViewDto) { + const identifier = identifiers[0] const json: any = { - ogTitle: view.name, - description: view.query - } - if (view.identifiers.length > 0) { - const identifier = view.identifiers[0] - json['ogTitle'] = identifierPreferEnglishTitle(identifier) - json['description'] = identifierPreferEnglishDescription(identifier) - json['ogDescription'] = identifierPreferEnglishDescription(identifier) + ogTitle: identifierPreferEnglishTitle(identifier), + ogDescription: identifierPreferEnglishDescription(identifier), + description: identifierPreferEnglishDescription(identifier) } return json } @@ -633,13 +411,7 @@ export const useIdentifierService = (): any => { identifierToUrl, identifierToDisplayName, identifierToDisplayAcronym, - databaseToServerHead, - subsetToServerHead, - tableToServerHead, - viewToServerHead, - databaseToServerSeoMeta, - subsetToServerSeoMeta, - tableToServerSeoMeta, - viewToServerSeoMeta, + identifiersToServerHead, + identifiersToServerSeoMeta } } diff --git a/dbrepo-ui/dto/index.ts b/dbrepo-ui/dto/index.ts index 9171734aa3..605a7c0db9 100644 --- a/dbrepo-ui/dto/index.ts +++ b/dbrepo-ui/dto/index.ts @@ -224,7 +224,11 @@ interface IdentifierFunderSaveDto { interface IdentifierDto { id: number; - type: string; + database_id: number | null; + query_id: number | null; + table_id: number | null; + view_id: number | null; + type: IdentifierTypeDto; titles: IdentifierTitleDto[] | []; descriptions: IdentifierDescriptionDto[] | []; funders: IdentifierFunderDto[] | []; @@ -236,23 +240,43 @@ interface IdentifierDto { licenses: LicenseDto[] | []; creators: CreatorDto[] | []; created: Date; - database_id: number | null; - query_id: number | null; - table_id: number | null; - view_id: number | null; query_normalized: string | null; related_identifiers: RelatedIdentifierDto[] | []; query_hash: string | null; result_hash: string | null; - /** - * @deprecated - */ result_number: number | null; publication_day: number | null; publication_month: number | null; - value: string | null; publication_year: number; - last_modified: Date; +} + +enum IdentifierTypeDto { + database, + subset, + table, + view +} + +enum IdentifierStatusTypeDto { + draft, + published +} + +interface IdentifierBriefDto { + id: number; + database_id: number | null; + query_id: number | null; + table_id: number | null; + view_id: number | null; + type: IdentifierTypeDto; + creators: CreatorBriefDto[] | []; + titles: IdentifierTitleDto[] | []; + description: IdentifierDescriptionDto[] | []; + doi: string | null; + publisher: string; + publication_year: number; + status: IdentifierStatusTypeDto; + owned_by: string; } interface IdentifierTitleDto { @@ -279,19 +303,35 @@ interface IdentifierFunderDto { award_title: string; } +enum NameTypeDto { + Personal, + Organizational +} + interface CreatorDto { id: number; firstname: string; lastname: string; affiliation: string; creator_name: string; - name_type: string; - name_identifier: string; - name_identifier_scheme: string; - name_identifier_scheme_uri: string; - affiliation_identifier: string; - affiliation_identifier_scheme: string; - affiliation_identifier_scheme_uri: string; + name_type: NameTypeDto | null; + name_identifier: string | null; + name_identifier_scheme: string | null; + name_identifier_scheme_uri: string | null; + affiliation_identifier: string | null; + affiliation_identifier_scheme: string | null; + affiliation_identifier_scheme_uri: string | null; +} + +interface CreatorBriefDto { + id: number; + affiliation: string; + creator_name: string; + name_type: NameTypeDto | null; + name_identifier: string | null; + name_identifier_scheme: string | null; + affiliation_identifier: string | null; + affiliation_identifier_scheme: string | null; } interface RelatedIdentifierDto { @@ -342,7 +382,6 @@ interface ColumnDto { database_id: number; table_id: number; internal_name: string; - date_format: ImageDateDto; is_primary_key: boolean; index_length: number; length: number; diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index 9351783574..50aa155d86 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -214,9 +214,15 @@ export default { access () { return this.cacheStore.getAccess }, + roles () { + return this.cacheStore.getRoles + }, cacheUser () { return this.cacheStore.getUser }, + identifier () { + return this.cacheStore.getIdentifier + }, resource () { if (!this.$route.params.database_id) { return null @@ -245,6 +251,9 @@ export default { return this.$config.public.commit.substr(0, 8) }, error () { + if (this.identifier) { + return null + } if (this.databaseError) { return this.databaseError } @@ -254,13 +263,13 @@ export default { if (!this.cacheUser) { return null } - if (this.table && !this.table.is_public && !this.table.is_schema_public && this.table.owner.id !== this.cacheUser.uid) { + if (this.table && !this.table.is_public && !this.table.is_schema_public && !this.access) { return makeError(403, null, null) } - if (this.view && !this.view.is_public && !this.view.is_schema_public && this.view.owner.id !== this.cacheUser.uid) { + if (this.view && !this.view.is_public && !this.view.is_schema_public && !this.access) { return makeError(403, null, null) } - if (this.subset && !this.subset.is_public && !this.subset.is_schema_public && this.subset.owner.id !== this.cacheUser.uid) { + if (this.subset && !this.subset.is_public && !this.subset.is_schema_public && !this.access) { return makeError(403, null, null) } return null @@ -288,6 +297,9 @@ export default { watch: { '$route.params': { handler (newObj, oldObj) { + if (import.meta.server) { + return + } if (!newObj.database_id) { this.databaseError = null this.accessError = null @@ -295,10 +307,20 @@ export default { this.cacheStore.setView(null) this.cacheStore.setSubset(null) this.cacheStore.setAccess(null) + this.cacheStore.setIdentifier(null) return } - if (import.meta.server) { - return + if (this.identifier) { + if (newObj.query_id && this.identifier.query_id !== Number(newObj.query_id)) { + this.cacheStore.setIdentifier(null) + } else if (newObj.table_id && this.identifier.table_id !== Number(newObj.table_id)) { + this.cacheStore.setIdentifier(null) + } else if (newObj.view_id && this.identifier.view_id !== Number(newObj.view_id)) { + this.cacheStore.setIdentifier(null) + } + if (this.$route.query.pid && this.identifier.id !== Number(this.$route.query.pid)) { + this.cacheStore.setIdentifier(null) + } } /* load database and optional access */ this.cacheStore.setRouteAccess(newObj.database_id, this.cacheUser?.uid) diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index 2e94f572ec..07ac0163ef 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -606,10 +606,10 @@ }, "status": { "title": "Status", - "public": "Public", + "public": "Visible", "data": "Data-only", "schema": "Schema-only", - "draft": "Draft" + "draft": "Hidden" }, "resource": { "data": { diff --git a/dbrepo-ui/pages/database/[database_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/info.vue index 8340dfe955..025cc9c4c4 100644 --- a/dbrepo-ui/pages/database/[database_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/info.vue @@ -1,26 +1,27 @@ <template> <div - v-if="canViewSchema"> + v-if="identifier || canViewInfo"> <DatabaseToolbar /> <v-window v-model="tab"> <v-window-item value="1"> <Summary - v-if="hasIdentifier" + v-if="identifier" :identifier="identifier" /> <v-card - v-if="hasIdentifier" + v-if="identifier" variant="flat" rounded="0"> <v-card-text> <Select - :identifiers="filteredIdentifiers" + :identifiers="identifiers" :identifier="identifier" /> </v-card-text> </v-card> <v-divider - v-if="hasIdentifier" /> + v-if="identifier" /> <v-card + v-if="canViewInfo" :title="$t('pages.database.title')" variant="flat" rounded="0"> @@ -94,7 +95,7 @@ <div> <UserBadge :user="database.owner" - :other-user="user" /> + :other-user="cacheUser" /> </div> </v-list-item> <v-list-item @@ -104,7 +105,7 @@ <div> <UserBadge :user="database.contact" - :other-user="user" /> + :other-user="cacheUser" /> </div> </v-list-item> </v-list> @@ -166,7 +167,20 @@ <script setup> import { ref } from 'vue' -const { loggedIn, user, login, logout } = useOidcAuth() +const config = useRuntimeConfig() +const { pid } = useRoute().query +const { database_id } = useRoute().params +const { data } = await useFetch(`${config.public.api.client}/api/identifier?dbid=${database_id}&type=database&status=published`) + +if (data.value && data.value.length > 0) { + const identifierService = useIdentifierService() + useServerHead(identifierService.identifiersToServerHead(data.value)) + useServerSeoMeta(identifierService.identifiersToServerSeoMeta(data.value)) +} +const identifier = ref(data.value && data.value.length > 0 ? (pid && data.value.filter(i => i.id === Number(pid)).length > 0 ? data.value.filter(i => i.id === Number(pid))[0] : data.value[0]) : null) + +const cacheStore = useCacheStore() +cacheStore.setIdentifier(identifier) </script> <script> import DatabaseToolbar from '@/components/database/DatabaseToolbar.vue' @@ -209,7 +223,7 @@ export default { return 0 }, description () { - if (!this.hasIdentifier) { + if (!this.identifier) { return '' } return this.database.identifier.description @@ -221,7 +235,7 @@ export default { return this.$config.public.database.image.height }, publisher () { - if (!this.hasIdentifier) { + if (!this.identifier) { return '' } return this.database.identifier.publisher @@ -232,32 +246,14 @@ export default { cacheUser () { return this.cacheStore.getUser }, - identifiers () { - if (!this.database) { - return [] - } - return this.database.identifiers + access () { + return this.cacheStore.getAccess }, - filteredIdentifiers () { - if (!this.identifiers) { + identifiers () { + if (!this.database || !this.database.identifiers) { return [] } - if (!this.cacheUser) { - return this.identifiers.filter(i => i.status === 'published') - } - return this.identifiers.filter(i => i.status === 'published' || i.owner.id === this.cacheUser.uid) - }, - identifier () { - if (this.pid) { - const filter = this.filteredIdentifiers.filter(i => i.id === Number(this.pid)) - if (filter.length > 0) { - return filter[0] - } - } - return this.filteredIdentifiers[0] - }, - access () { - return this.cacheStore.getAccess + return this.database.identifiers.filter(i => i.query_id === Number(this.$route.params.subset_id)) }, pid () { return this.$route.query.pid @@ -300,9 +296,6 @@ export default { const databaseService = useDatabaseService() return databaseService.databaseToOwner(this.database) }, - hasIdentifier () { - return this.identifier - }, accessDescription () { if (!this.access) { return @@ -335,19 +328,19 @@ export default { } return this.database.preview_image }, - canViewSchema () { - if (this.error) { - return false - } + canViewInfo () { if (!this.database) { return false } - if (this.database.is_schema_public) { + if (this.database.is_public || this.database.is_schema_public) { return true } + if (!this.access) { + return false + } const userService = useUserService() return userService.hasReadAccess(this.access) - } + }, } } </script> diff --git a/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue index 61d34fb5c2..505a765123 100644 --- a/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canCreateIdentifier || canUpdateIdentifier"> + v-if="canPersistIdentifier || canUpdateIdentifier"> <Persist type="database" :database="database" /> @@ -51,6 +51,9 @@ export default { cacheUser () { return this.cacheStore.getUser }, + access () { + return this.cacheStore.getAccess + }, identifier () { if (!this.database) { return false @@ -58,26 +61,30 @@ export default { const filter = this.database.identifiers.filter(i => i.id === Number(this.$route.params.identifier_id)) return filter.length === 1 ? filter[0] : null }, - canCreateIdentifier () { - if (!this.roles) { + canPersistIdentifier () { + if (!this.database || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.database) { + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('create-identifier') && this.database.owner.id === this.cacheUser.uid + const userService = useUserService() + return userService.hasReadAccess(this.access) && this.database.owner.id === this.cacheUser.uid }, canUpdateIdentifier () { - if (!this.roles) { + if (!this.identifier || !this.roles) { return false } - if (!this.identifier) { + if (this.roles.includes('modify-identifier-metadata')) { + return true + } + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('modify-identifier-metadata') && this.identifier.owner.id === this.cacheUser.uid + return this.identifier.owner.id === this.cacheUser.uid } } } diff --git a/dbrepo-ui/pages/database/[database_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/persist/index.vue index 52e36cd0d5..4bbf0e8d2a 100644 --- a/dbrepo-ui/pages/database/[database_id]/persist/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/persist/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canPersistDatabase"> + v-if="canPersistIdentifier"> <Persist type="database" :database="database" /> @@ -50,14 +50,14 @@ export default { } return this.database.owner.id === this.cacheUser.uid }, - canPersistDatabase () { - if (!this.database || !this.roles) { + canPersistIdentifier () { + if (!this.database || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.roles.includes('create-identifier') || !this.cacheUser || !this.access) { + if (!this.roles.includes('create-identifier')) { return false } const userService = useUserService() diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue index 26ddf9478c..1556dcc387 100644 --- a/dbrepo-ui/pages/database/[database_id]/settings.vue +++ b/dbrepo-ui/pages/database/[database_id]/settings.vue @@ -462,7 +462,7 @@ export default { .then((database) => { const toast = useToastInstance() toast.success(this.$t('success.database.visibility')) - this.cacheStore.setDatabase(database) + this.cacheStore.reloadDatabase() }) .catch(() => { this.loading = false diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue index 30bebeb2b8..682fc59b98 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue @@ -18,7 +18,7 @@ </v-toolbar-title> <v-spacer /> <v-btn - v-if="canDownload" + v-if="canViewSubsetData" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-download' : null" variant="flat" :loading="downloadLoading" @@ -97,34 +97,22 @@ export default { subset () { return this.cacheStore.getSubset }, + access () { + return this.cacheStore.getAccess + }, executionUTC () { if (!this.subset) { return null } return formatTimestampUTCLabel(this.subset.created) }, - canDownload () { - if (!this.result_visibility || !this.subset.id) { - return false - } - return this.subset.id - }, - result_visibility () { + canViewSubsetData () { if (!this.database || !this.subset) { return false } if (this.database.is_public) { return true } - return this.subset.owner.username === this.username - }, - canViewSubsetData () { - if (this.error || !this.subset) { - return false - } - if (this.subset.is_public) { - return true - } if (!this.access) { return false } diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue index eeb0a70c3e..db5d45b461 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue @@ -1,36 +1,29 @@ <template> - <div> + <div + v-if="identifier || canViewInfo"> <SubsetToolbar /> <v-card variant="flat" rounded="0"> <Summary - v-if="hasIdentifier" + v-if="identifier" :identifier="identifier" /> <v-card-text - v-if="hasIdentifier"> + v-if="identifier"> <Select :identifiers="identifiers" :identifier="identifier" /> </v-card-text> </v-card> <v-divider - v-if="subset && identifier" /> + v-if="canViewInfo && identifier" /> <v-card + v-if="canViewInfo" variant="flat" rounded="0" :title="$t('pages.subset.title')"> <v-card-text> <v-list - v-if="!subset" - lines="two" - dense> - <v-skeleton-loader - type="list-item-three-line" - width="50%" /> - </v-list> - <v-list - v-else-if="subset" lines="two" dense> <v-list-item @@ -88,6 +81,24 @@ </div> </template> +<script setup> +import { ref } from 'vue' + +const config = useRuntimeConfig() +const { pid } = useRoute().query +const { database_id, subset_id } = useRoute().params +const { data } = await useFetch(`${config.public.api.client}/api/identifier?dbid=${database_id}&qid=${subset_id}&type=subset&status=published`) + +if (data.value && data.value.length > 0) { + const identifierService = useIdentifierService() + useServerHead(identifierService.identifiersToServerHead(data.value)) + useServerSeoMeta(identifierService.identifiersToServerSeoMeta(data.value)) +} +const identifier = ref(data.value && data.value.length > 0 ? (pid && data.value.filter(i => i.id === Number(pid)).length > 0 ? data.value.filter(i => i.id === Number(pid))[0] : data.value[0]) : null) + +const cacheStore = useCacheStore() +cacheStore.setIdentifier(identifier) +</script> <script> import Summary from '@/components/identifier/Summary.vue' import SubsetToolbar from '@/components/subset/SubsetToolbar.vue' @@ -152,25 +163,26 @@ export default { return this.cacheStore.getSubset }, identifiers () { - if (!this.database || !this.database.subsets || this.database.subsets.length === 0) { + if (!this.database || !this.database.subsets) { return [] } - return this.database.subsets.filter(s => s.query_id === Number(this.$route.params.subset_id)) - }, - hasIdentifier () { - return this.identifiers.length > 0 + return this.database.subsets.filter(i => i.query_id === Number(this.$route.params.subset_id)) }, - identifier () { - if (this.pid) { - const filter = this.identifiers.filter(i => i.id === Number(this.pid)) - if (filter.length > 0) { - return filter[0] - } + canViewInfo () { + if (!this.database) { + return false + } + if (this.database.is_public || this.database.is_schema_public) { + return true + } + if (!this.access) { + return false } - return this.identifiers[0] + const userService = useUserService() + return userService.hasReadAccess(this.access) }, title () { - if (!this.hasIdentifier) { + if (!this.identifier) { return null } const enTitle = this.identifier.titles.filter(t => t.language).filter(t => t.language === 'en') diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue index f50b9788c7..78878a0015 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canCreateIdentifier || canUpdateIdentifier"> + v-if="canPersistIdentifier || canUpdateIdentifier"> <Persist type="subset" :database="database" /> @@ -59,6 +59,12 @@ export default { subset () { return this.cacheStore.getSubset }, + access () { + return this.cacheStore.getAccess + }, + cacheUser () { + return this.cacheStore.getUser + }, identifier () { if (!this.subset) { return false @@ -66,26 +72,30 @@ export default { const filter = this.subset.identifiers.filter(i => i.id === Number(this.$route.params.identifier_id)) return filter.length === 1 ? filter[0] : null }, - canCreateIdentifier () { - if (!this.roles) { + canPersistIdentifier () { + if (!this.subset || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.subset) { + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('create-identifier') && this.subset.owner.id === this.cacheUser.uid + const userService = useUserService() + return userService.hasReadAccess(this.access) && this.subset.owner.id === this.cacheUser.uid }, canUpdateIdentifier () { - if (!this.roles) { + if (!this.identifier || !this.roles) { return false } - if (!this.identifier) { + if (this.roles.includes('modify-identifier-metadata')) { + return true + } + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('modify-identifier-metadata') && this.identifier.owner.id === this.cacheUser.uid + return this.identifier.owner.id === this.cacheUser.uid } } } diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue index d8e7510787..88209f5018 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue @@ -1,10 +1,10 @@ <template> <div - v-if="canPersistSubset"> + v-if="canPersistIdentifier"> <Persist type="subset" :database="database" - :query="query" /> + :query="subset" /> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> </template> @@ -20,8 +20,6 @@ export default { data () { return { loading: false, - loadingQuery: false, - query: null, isAuthorizationError: false, items: [ { @@ -59,44 +57,25 @@ export default { subset () { return this.cacheStore.getSubset }, - canPersistSubset () { - if (!this.subset || !this.roles) { + roles () { + return this.cacheStore.getRoles + }, + cacheUser () { + return this.cacheStore.getUser + }, + canPersistIdentifier () { + if (!this.subset || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.roles.includes('create-identifier') || !this.cacheUser || !this.access) { + if (!this.roles.includes('create-identifier')) { return false } const userService = useUserService() return userService.hasReadAccess(this.access) && this.subset.owner.id === this.cacheUser.uid } - }, - mounted () { - this.loadQuery() - }, - methods: { - loadQuery () { - this.loadingQuery = true - return new Promise((resolve, reject) => { - const queryService = useQueryService() - queryService.findOne(this.$route.params.database_id, this.$route.params.subset_id) - .then((query) => { - this.query = query - resolve(query) - }) - .catch((error) => { - if (error.response.status === 405) { - this.isAuthorizationError = true - } - reject(error) - }) - .finally(() => { - this.loadingQuery = false - }) - }) - } } } </script> diff --git a/dbrepo-ui/pages/database/[database_id]/subset/create.vue b/dbrepo-ui/pages/database/[database_id]/subset/create.vue index 94fd8e8ec9..c3d07bba15 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/create.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/create.vue @@ -52,6 +52,9 @@ export default { if (this.database.is_public) { return true } + if (!this.access) { + return false + } const userService = useUserService() return userService.hasReadAccess(this.access) } diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue index 4811e5c47c..efbcd6accf 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue @@ -81,6 +81,9 @@ export default { cacheUser () { return this.cacheStore.getUser }, + access () { + return this.cacheStore.getAccess + }, title () { if (!this.table) { return this.$t('pages.table.import.title') diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue index dad79a6fa8..89e2714115 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue @@ -1,23 +1,23 @@ <template> <div - v-if="canViewInfo"> + v-if="identifier || canViewInfo"> <TableToolbar :selection="selection" /> <v-card + v-if="identifier" variant="flat"> <Summary - v-if="hasIdentifier" :identifier="identifier" /> - <v-card-text - v-if="hasIdentifier"> + <v-card-text> <Select :identifiers="identifiers" :identifier="identifier" /> </v-card-text> </v-card> <v-divider - v-if="identifier" /> + v-if="canViewInfo" /> <v-card + v-if="canViewInfo" variant="flat" rounded="0" :title="$t('pages.table.title')"> @@ -118,6 +118,24 @@ </div> </template> +<script setup> +import { ref } from 'vue' + +const config = useRuntimeConfig() +const { pid } = useRoute().query +const { database_id, table_id } = useRoute().params +const { data } = await useFetch(`${config.public.api.client}/api/identifier?dbid=${database_id}&tid=${table_id}&type=table&status=published`) + +if (data.value && data.value.length > 0) { + const identifierService = useIdentifierService() + useServerHead(identifierService.identifiersToServerHead(data.value)) + useServerSeoMeta(identifierService.identifiersToServerSeoMeta(data.value)) +} +const identifier = ref(data.value && data.value.length > 0 ? (pid && data.value.filter(i => i.id === Number(pid)).length > 0 ? data.value.filter(i => i.id === Number(pid))[0] : data.value[0]) : null) + +const cacheStore = useCacheStore() +cacheStore.setIdentifier(identifier) +</script> <script> import TableToolbar from '@/components/table/TableToolbar.vue' import Select from '@/components/identifier/Select.vue' @@ -194,7 +212,7 @@ export default { return userService.hasReadAccess(this.access) }, canViewInfo () { - if (this.error || !this.table) { + if (!this.table) { return false } if (this.table.is_public || this.table.is_schema_public) { @@ -223,31 +241,10 @@ export default { return this.roles.includes('insert-table-data') }, identifiers () { - if (!this.table || !this.table.identifiers || this.table.identifiers.length === 0) { - return [] - } - return this.table.identifiers - }, - filteredIdentifiers () { - if (!this.identifiers) { + if (!this.table || !this.table.identifiers) { return [] } - if (!this.cacheUser) { - return this.identifiers.filter(i => i.status === 'published') - } - return this.identifiers.filter(i => i.status === 'published' || i.owned_by === this.cacheUser.uid) - }, - identifier () { - if (this.pid) { - const filter = this.filteredIdentifiers.filter(i => i.id === Number(this.pid)) - if (filter.length > 0) { - return filter[0] - } - } - return this.filteredIdentifiers[0] - }, - hasIdentifier () { - return this.identifier + return this.table.identifiers.filter(i => i.query_id === Number(this.$route.params.subset_id)) }, brokerExtraInfo () { return this.$config.public.broker.extra diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue index 39c0ea1aad..e2d16e8db4 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canCreateIdentifier || canUpdateIdentifier"> + v-if="canPersistIdentifier || canUpdateIdentifier"> <Persist type="table" :database="database" /> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> @@ -57,6 +57,12 @@ export default { table () { return this.cacheStore.getTable }, + cacheUser () { + return this.cacheStore.getUser + }, + access () { + return this.cacheStore.getAccess + }, identifier () { if (!this.table) { return false @@ -64,26 +70,30 @@ export default { const filter = this.table.identifiers.filter(i => i.id === Number(this.$route.params.identifier_id)) return filter.length === 1 ? filter[0] : null }, - canCreateIdentifier () { - if (!this.roles) { + canPersistIdentifier () { + if (!this.table || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.table) { + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('create-identifier') && this.table.owner.id === this.cacheUser.uid + const userService = useUserService() + return userService.hasReadAccess(this.access) && this.table.owner.id === this.cacheUser.uid }, canUpdateIdentifier () { - if (!this.roles) { + if (!this.identifier || !this.roles) { return false } - if (!this.identifier) { + if (this.roles.includes('modify-identifier-metadata')) { + return true + } + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('modify-identifier-metadata') && this.identifier.owner.id === this.cacheUser.uid + return this.identifier.owner.id === this.cacheUser.uid } } } diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue index 9d75edae1b..6c26187fa2 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canPersistTable"> + v-if="canPersistIdentifier"> <Persist type="table" :database="database" @@ -60,14 +60,20 @@ export default { table () { return this.cacheStore.getTable }, - canPersistTable () { - if (!this.table || !this.roles) { + roles () { + return this.cacheStore.getRoles + }, + cacheUser () { + return this.cacheStore.getUser + }, + canPersistIdentifier () { + if (!this.table || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.roles.includes('create-identifier') || !this.cacheUser || !this.access) { + if (!this.roles.includes('create-identifier')) { return false } const userService = useUserService() diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue index 860d4819bb..4c5b046742 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue @@ -121,8 +121,6 @@ </template> <script setup> -import { ref } from 'vue' - const { loggedIn } = useOidcAuth() </script> <script> diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue index d9c98f6111..0f0a8feab6 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue @@ -22,9 +22,6 @@ <v-textarea v-model="modify.description" rows="2" - :rules="[ - v => max(v, 180) || ($t('validation.max-length') + 180), - ]" clearable counter="180" persistent-counter @@ -125,7 +122,7 @@ export default { data () { return { tab: 0, - valid: false, + valid: true, loading: false, modify: { description: null, @@ -196,10 +193,10 @@ export default { if (!this.table) { return false } - if (this.table.is_public !== this.modify.is_public) { + if (this.table.is_public !== this.modify.is_public || this.table.is_schema_public !== this.modify.is_schema_public) { return true } - return this.table.is_schema_public !== this.modify.is_schema_public + return this.table.description !== this.modify.description }, canUpdateTable () { if (!this.cacheUser || !this.table || !this.access || !this.roles || !this.roles.includes('update-table')) { diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue index a9ddd46929..24aed7f2ff 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue @@ -92,9 +92,6 @@ <v-textarea v-model="tableCreate.description" rows="2" - :rules="[ - v => (!!v || v.length <= 180) || ($t('validation.max-length') + 180), - ]" clearable counter="180" persistent-counter diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue index d6bd4414df..804ae03c15 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue @@ -70,9 +70,6 @@ <v-textarea v-model="tableCreate.description" rows="2" - :rules="[ - v => (!v || v.length <= 180) || $t('validation.max-length') + 180 - ]" clearable counter="180" persistent-counter diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue index b492476b97..2b0936cba5 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue @@ -34,12 +34,6 @@ </div> </template> -<script setup> -import { ref } from 'vue' - -const { loggedIn, user, login, logout } = useOidcAuth() -const cacheUser = ref(loggedIn ? user.value?.cacheUser : null) -</script> <script> import TimeDrift from '@/components/TimeDrift.vue' import QueryResults from '@/components/subset/Results.vue' diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue index 75eca0100f..3c0c40e33c 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue @@ -1,25 +1,25 @@ <template> <div - v-if="canViewView"> + v-if="identifier || canViewInfo"> <ViewToolbar /> <v-window v-model="tab"> - <v-window-item - v-if="view"> + <v-window-item> <v-card variant="flat"> <Summary - v-if="hasIdentifier" + v-if="identifier" :identifier="identifier" /> <v-card-text - v-if="hasIdentifier"> + v-if="identifier"> <Select :identifiers="identifiers" :identifier="identifier" /> </v-card-text> </v-card> <v-divider - v-if="hasIdentifier" /> + v-if="identifier" /> <v-card + v-if="canViewInfo" :title="$t('pages.view.title')" variant="flat"> <v-card-text> @@ -39,7 +39,7 @@ <UserBadge v-if="view" :user="view.owner" - :other-user="user" /> + :other-user="cacheUser" /> <v-skeleton-loader v-else type="subtitle" @@ -59,6 +59,24 @@ </div> </template> +<script setup> +import { ref } from 'vue' + +const config = useRuntimeConfig() +const { pid } = useRoute().query +const { database_id, view_id } = useRoute().params +const { data } = await useFetch(`${config.public.api.client}/api/identifier?dbid=${database_id}&vid=${view_id}&type=view&status=published`) + +if (data.value && data.value.length > 0) { + const identifierService = useIdentifierService() + useServerHead(identifierService.identifiersToServerHead(data.value)) + useServerSeoMeta(identifierService.identifiersToServerSeoMeta(data.value)) +} +const identifier = ref(data.value && data.value.length > 0 ? (pid && data.value.filter(i => i.id === Number(pid)).length > 0 ? data.value.filter(i => i.id === Number(pid))[0] : data.value[0]) : null) + +const cacheStore = useCacheStore() +cacheStore.setIdentifier(identifier) +</script> <script> import ViewToolbar from '@/components/view/ViewToolbar.vue' import Summary from '@/components/identifier/Summary.vue' @@ -122,28 +140,10 @@ export default { return this.cacheStore.getUser }, identifiers () { - if (!this.view) { - return [] - } - return this.view.identifiers - }, - filteredIdentifiers () { - if (!this.identifiers) { + if (!this.view || !this.view.identifiers) { return [] } - if (!this.cacheUser) { - return this.identifiers.filter(i => i.status === 'published') - } - return this.identifiers.filter(i => i.status === 'published' || i.owner.id === this.cacheUser.uid) - }, - identifier () { - if (this.pid) { - const filter = this.filteredIdentifiers.filter(i => i.id === Number(this.pid)) - if (filter.length > 0) { - return filter[0] - } - } - return this.filteredIdentifiers[0] + return this.view.identifiers.filter(i => i.query_id === Number(this.$route.params.subset_id)) }, views () { if (!this.database) { @@ -154,9 +154,6 @@ export default { pid () { return this.$route.query.pid }, - hasIdentifier () { - return this.identifier - }, creator () { if (!this.view) { return null @@ -164,7 +161,7 @@ export default { const userService = useUserService() return userService.userToFullName(this.view.creator) }, - canViewView () { + canViewInfo () { if (!this.view) { return false } diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue index c220a8aa9b..540bbbdb5e 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canCreateIdentifier || canUpdateIdentifier"> + v-if="canPersistIdentifier || canUpdateIdentifier"> <Persist type="view" :database="database" /> @@ -59,6 +59,12 @@ export default { view () { return this.cacheStore.getView }, + access () { + return this.cacheStore.getAccess + }, + cacheUser () { + return this.cacheStore.getUser + }, identifier () { if (!this.view) { return false @@ -66,26 +72,30 @@ export default { const filter = this.view.identifiers.filter(i => i.id === Number(this.$route.params.identifier_id)) return filter.length === 1 ? filter[0] : null }, - canCreateIdentifier () { - if (!this.roles) { + canPersistIdentifier () { + if (!this.view || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.view) { + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('create-identifier') && this.view.owner.id === this.cacheUser.uid + const userService = useUserService() + return userService.hasReadAccess(this.access) && this.view.owner.id === this.cacheUser.uid }, canUpdateIdentifier () { - if (!this.roles) { + if (!this.identifier || !this.roles) { return false } - if (!this.identifier) { + if (this.roles.includes('modify-identifier-metadata')) { + return true + } + if (!this.roles.includes('create-identifier')) { return false } - return this.roles.includes('modify-identifier-metadata') && this.identifier.owner.id === this.cacheUser.uid + return this.identifier.owner.id === this.cacheUser.uid } } } diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue index 2a8d010db0..ed8067d213 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue @@ -1,6 +1,6 @@ <template> <div - v-if="canPersistView"> + v-if="canPersistIdentifier"> <Persist type="view" :database="database" @@ -60,14 +60,17 @@ export default { view () { return this.cacheStore.getView }, - canPersistView () { - if (!this.view || !this.roles) { + roles () { + return this.cacheStore.getRoles + }, + canPersistIdentifier () { + if (!this.view || !this.roles || !this.cacheUser || !this.access) { return false } if (this.roles.includes('create-foreign-identifier')) { return true } - if (!this.roles.includes('create-identifier') || !this.cacheUser || !this.access) { + if (!this.roles.includes('create-identifier')) { return false } const userService = useUserService() diff --git a/dbrepo-ui/pages/database/[database_id]/view/create.vue b/dbrepo-ui/pages/database/[database_id]/view/create.vue index c3a0e73f49..a834bdb5c9 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/create.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/create.vue @@ -8,6 +8,7 @@ <script> import Builder from '@/components/subset/Builder.vue' +import { useCacheStore } from '@/stores/cache.js' export default { components: { @@ -33,7 +34,8 @@ export default { to: `/database/${this.$route.params.database_id}/view/create`, disabled: true } - ] + ], + cacheStore: useCacheStore() } }, computed: { @@ -44,11 +46,11 @@ export default { return this.cacheStore.getRoles }, canCreateView () { - if (!this.roles) { + if (!this.roles || !this.roles.includes('create-database-view')) { return false } const userService = useUserService() - return userService.hasReadAccess(this.access) && this.roles.includes('create-database-view') + return userService.hasReadAccess(this.access) } } } diff --git a/dbrepo-ui/stores/cache.js b/dbrepo-ui/stores/cache.js index 8ba0d2702f..c2e34f48bb 100644 --- a/dbrepo-ui/stores/cache.js +++ b/dbrepo-ui/stores/cache.js @@ -10,6 +10,7 @@ export const useCacheStore = defineStore('cache', { access: null, subset: null, locale: null, + identifier: null, ontologies: [], messages: [], user: null, @@ -24,6 +25,7 @@ export const useCacheStore = defineStore('cache', { getAccess: (state) => state.access, getSubset: (state) => state.subset, getLocale: (state) => state.locale, + getIdentifier: (state) => state.identifier, getOntologies: (state) => state.ontologies, getMessages: (state) => state.messages, getUser: (state) => state.user, @@ -49,6 +51,9 @@ export const useCacheStore = defineStore('cache', { setLocale(locale) { this.locale = locale }, + setIdentifier(identifier) { + this.identifier = identifier + }, setOntologies(ontologies) { this.ontologies = ontologies }, diff --git a/docker-compose.yml b/docker-compose.yml index ef7e6a8a32..ed0f7e26c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -486,7 +486,7 @@ services: LDAP_ADMIN_PASSWORD: "${IDENTITY_SERVICE_ADMIN_PASSWORD:-admin}" LDAP_ROOT: "${IDENTITY_SERVICE_ROOT:-dc=dbrepo,dc=at}" healthcheck: - test: test -f /opt/bitnami/grafana/tmp/grafana.pid + test: curl -fsSL --head http://127.0.0.1:3000 interval: 10s timeout: 5s retries: 12 diff --git a/make/build.mk b/make/build.mk index bc6dfc56a7..800c879c97 100644 --- a/make/build.mk +++ b/make/build.mk @@ -14,6 +14,10 @@ build-data-service: ## Build the Data Service. build-metadata-service: ## Build the Metadata Service. mvn -f ./dbrepo-metadata-service/pom.xml clean package -DskipTests +.PHONY: build-auth-event-listener +build-auth-event-listener: ## Build the Auth Service Event Listener. + mvn -f ./dbrepo-auth-service/listeners/pom.xml clean package -DskipTests + .PHONY: build-ui build-ui: ## Build the UI. bun --cwd ./dbrepo-ui build diff --git a/make/dev.mk b/make/dev.mk index 0282dbbce2..d8da31086b 100644 --- a/make/dev.mk +++ b/make/dev.mk @@ -1,7 +1,7 @@ ##@ Development .PHONY: start-dev -start-dev: build-images ## Start the development deployment. +start-dev: build-images build-auth-event-listener ## Start the development deployment. docker container stop dbrepo-gateway-service || true docker container rm dbrepo-gateway-service || true docker compose up -d -- GitLab