From 9f27930e95401764c76c3b766f2ac3627e95106b Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 19 Feb 2025 12:01:50 +0100 Subject: [PATCH] WIP Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .gitlab-ci.yml | 2 +- .../at/tuwien/endpoints/SubsetEndpoint.java | 3 +- .../database/table/HistoryEventTypeDto.java | 11 +- .../api/database/table/TableHistoryDto.java | 3 +- .../at/tuwien/endpoints/DatabaseEndpoint.java | 39 +- .../at/tuwien/endpoints/TableEndpoint.java | 17 +- .../at/tuwien/endpoints/UserEndpoint.java | 23 +- lib/python/dbrepo/RestClient.py | 119 ++-- lib/python/dbrepo/api/dto.py | 40 +- lib/python/tests/test_unit_concept.py | 35 ++ lib/python/tests/test_unit_database.py | 115 ++++ lib/python/tests/test_unit_identifier.py | 555 +++++++++++++++++- lib/python/tests/test_unit_jwt.py | 65 -- lib/python/tests/test_unit_license.py | 12 +- lib/python/tests/test_unit_query.py | 291 ++++++++- lib/python/tests/test_unit_rest_client.py | 11 + lib/python/tests/test_unit_table.py | 65 +- lib/python/tests/test_unit_view.py | 40 ++ 18 files changed, 1185 insertions(+), 261 deletions(-) create mode 100644 lib/python/tests/test_unit_concept.py delete mode 100644 lib/python/tests/test_unit_jwt.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 65f52bea38..2d26738384 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -403,7 +403,7 @@ test-lib: script: - "pip install pipenv" - "pipenv install gunicorn && pipenv install --dev --system --deploy" - - cd ./lib/python/ && coverage run -m pytest tests/test_unit_analyse.py tests/test_unit_container.py tests/test_unit_database.py tests/test_unit_identifier.py tests/test_unit_license.py tests/test_unit_query.py tests/test_unit_rest_client.py tests/test_unit_table.py tests/test_unit_user.py tests/test_unit_view.py tests/test_unit_rest_client.py --junitxml=report.xml && coverage html --omit="tests/*" && coverage report --omit="tests/*" > ./coverage.txt + - cd ./lib/python/ && coverage run -m pytest tests/test_unit_analyse.py tests/test_unit_container.py tests/test_unit_concept.py tests/test_unit_database.py tests/test_unit_identifier.py tests/test_unit_license.py tests/test_unit_query.py tests/test_unit_rest_client.py tests/test_unit_table.py tests/test_unit_user.py tests/test_unit_view.py tests/test_unit_unit.py tests/test_integration_table.py --junitxml=report.xml && coverage html --omit="tests/*" && coverage report --omit="tests/*" > ./coverage.txt - "cat ./coverage.txt | grep -o 'TOTAL[^%]*%'" artifacts: when: always diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index 90cb9846b2..7f1f516495 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -341,7 +341,8 @@ public class SubsetEndpoint extends RestEndpoint { @RequestParam(required = false) Long size) throws PaginationException, DatabaseNotFoundException, RemoteUnavailableException, NotAllowedException, QueryNotFoundException, DatabaseUnavailableException, TableMalformedException, QueryMalformedException, - UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException { + UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewNotFoundException, + ViewMalformedException { log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}", databaseId, subsetId, principal != null ? principal.getName() : null, page, size); endpointValidator.validateDataParams(page, size); diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java index 8912295add..83d8441752 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java @@ -8,14 +8,11 @@ import lombok.Getter; @Schema public enum HistoryEventTypeDto { - @JsonProperty("read") - READ("read"), + @JsonProperty("insert") + INSERT("insert"), - @JsonProperty("write_own") - WRITE_OWN("write_own"), - - @JsonProperty("write_all") - WRITE_ALL("write_all"); + @JsonProperty("delete") + DELETE("delete"); private String name; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java index 35df29430f..e6ed667235 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java @@ -24,7 +24,8 @@ public class TableHistoryDto { private Instant timestamp; @NotNull - private String event; + @Schema(example = "INSERT") + private HistoryEventTypeDto event; @NotNull @Schema(example = "1") diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 41cc59ca6a..ccfd23e1ce 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -44,16 +44,16 @@ import java.util.Optional; public class DatabaseEndpoint extends AbstractEndpoint { private final UserService userService; - private final MetadataMapper databaseMapper; + private final MetadataMapper metadataMapper; private final StorageService storageService; private final DatabaseService databaseService; private final ContainerService containerService; @Autowired - public DatabaseEndpoint(UserService userService, MetadataMapper databaseMapper, StorageService storageService, + public DatabaseEndpoint(UserService userService, MetadataMapper metadataMapper, StorageService storageService, DatabaseService databaseService, ContainerService containerService) { this.userService = userService; - this.databaseMapper = databaseMapper; + this.metadataMapper = metadataMapper; this.storageService = storageService; this.databaseService = databaseService; this.containerService = containerService; @@ -100,7 +100,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { return ResponseEntity.status(HttpStatus.OK) .headers(headers) .body(databases.stream() - .map(databaseMapper::databaseToDatabaseBriefDto) + .map(metadataMapper::databaseToDatabaseBriefDto) .toList()); } @@ -166,7 +166,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { } final User caller = userService.findById(getId(principal)); return ResponseEntity.status(HttpStatus.CREATED) - .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + .body(metadataMapper.databaseDtoToDatabaseBriefDto(metadataMapper.databaseToDatabaseDto( databaseService.create(container, data, caller, userService.findAllInternalUsers())))); } @@ -220,7 +220,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { log.error("Failed to refresh database tables metadata: not owner"); throw new NotAllowedException("Failed to refresh tables metadata: not owner"); } - return ResponseEntity.ok(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + return ResponseEntity.ok(metadataMapper.databaseDtoToDatabaseBriefDto(metadataMapper.databaseToDatabaseDto( databaseService.updateTableMetadata(database)))); } @@ -268,7 +268,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { log.error("Failed to refresh database views metadata: not owner"); throw new NotAllowedException("Failed to refresh database views metadata: not owner"); } - return ResponseEntity.ok(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + return ResponseEntity.ok(metadataMapper.databaseDtoToDatabaseBriefDto(metadataMapper.databaseToDatabaseDto( databaseService.updateViewMetadata(database)))); } @@ -322,7 +322,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to modify database visibility: not owner"); } return ResponseEntity.accepted() - .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + .body(metadataMapper.databaseDtoToDatabaseBriefDto(metadataMapper.databaseToDatabaseDto( databaseService.modifyVisibility(database, data)))); } @@ -378,7 +378,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to transfer database: not owner"); } return ResponseEntity.accepted() - .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + .body(metadataMapper.databaseDtoToDatabaseBriefDto(metadataMapper.databaseToDatabaseDto( databaseService.modifyOwner(database, newOwner)))); } @@ -437,7 +437,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { image = storageService.getBytes(data.getKey()); } return ResponseEntity.accepted() - .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + .body(metadataMapper.databaseDtoToDatabaseBriefDto(metadataMapper.databaseToDatabaseDto( databaseService.modifyImage(database, image)))); } @@ -485,24 +485,13 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @ApiResponse(responseCode = "404", - description = "Database, user or exchange could not be found", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), - @ApiResponse(responseCode = "502", - description = "Connection to the broker service could not be established", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), - @ApiResponse(responseCode = "503", - description = "Failed to find queue information in broker service", + description = "Database could not be found", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), + schema = @Schema(implementation = ApiErrorDto.class))}) }) public ResponseEntity<DatabaseDto> findById(@NotNull @PathVariable("databaseId") Long databaseId, - Principal principal) throws DataServiceException, - DataServiceConnectionException, DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException, + Principal principal) throws DatabaseNotFoundException, NotAllowedException { log.debug("endpoint find database, databaseId={}", databaseId); final Database database = databaseService.findById(databaseId); @@ -553,7 +542,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { .toList()); database.setAccesses(List.of()); } - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(database); + final DatabaseDto dto = metadataMapper.databaseToDatabaseDto(database); final HttpHeaders headers = new HttpHeaders(); if (isSystem(principal)) { headers.set("X-Username", database.getContainer().getPrivilegedUsername()); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 08535fde69..32075fb5d8 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; -import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.CreateTableDto; +import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableUpdateDto; import at.tuwien.api.database.table.columns.ColumnDto; @@ -457,22 +457,11 @@ public class TableEndpoint extends AbstractEndpoint { content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), - @ApiResponse(responseCode = "502", - description = "Failed to establish connection with broker service", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), - @ApiResponse(responseCode = "503", - description = "Failed to obtain queue information from broker service", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<TableDto> findById(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, - Principal principal) throws DataServiceException, - DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException, - UserNotFoundException, NotAllowedException, AccessNotFoundException { + Principal principal) throws TableNotFoundException, + DatabaseNotFoundException, UserNotFoundException, NotAllowedException { log.debug("endpoint find table, databaseId={}, tableId={}", databaseId, tableId); final Database database = databaseService.findById(databaseId); final Table table = tableService.findById(database, tableId); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java index 3636aa63a3..8f3e15529e 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java @@ -10,8 +10,6 @@ import at.tuwien.exception.AuthServiceException; import at.tuwien.exception.NotAllowedException; import at.tuwien.exception.UserNotFoundException; import at.tuwien.mapper.MetadataMapper; -import at.tuwien.service.AuthenticationService; -import at.tuwien.service.DatabaseService; import at.tuwien.service.UserService; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -43,17 +41,12 @@ import java.util.UUID; public class UserEndpoint extends AbstractEndpoint { private final UserService userService; - private final MetadataMapper userMapper; - private final DatabaseService databaseService; - private final AuthenticationService authenticationService; + private final MetadataMapper metadataMapper; @Autowired - public UserEndpoint(UserService userService, MetadataMapper userMapper, DatabaseService databaseService, - AuthenticationService authenticationService) { + public UserEndpoint(UserService userService, MetadataMapper metadataMapper) { this.userService = userService; - this.userMapper = userMapper; - this.databaseService = databaseService; - this.authenticationService = authenticationService; + this.metadataMapper = metadataMapper; } @GetMapping @@ -74,7 +67,7 @@ public class UserEndpoint extends AbstractEndpoint { return ResponseEntity.ok(userService.findAll() .stream() .filter(user -> !user.getIsInternal()) - .map(userMapper::userToUserBriefDto) + .map(metadataMapper::userToUserBriefDto) .toList()); } log.trace("filter by username: {}", username); @@ -83,7 +76,7 @@ public class UserEndpoint extends AbstractEndpoint { if (user.getIsInternal()) { return ResponseEntity.ok(List.of()); } - return ResponseEntity.ok(List.of(userMapper.userToUserBriefDto(user))); + return ResponseEntity.ok(List.of(metadataMapper.userToUserBriefDto(user))); } catch (UserNotFoundException e) { log.trace("filter by username {} failed: return empty list", username); return ResponseEntity.ok(List.of()); @@ -110,7 +103,7 @@ public class UserEndpoint extends AbstractEndpoint { public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody CreateUserDto data) { log.debug("endpoint create user, data.id={}, data.username={}", data.getId(), data.getUsername()); return ResponseEntity.status(HttpStatus.CREATED) - .body(userMapper.userToUserBriefDto( + .body(metadataMapper.userToUserBriefDto( userService.create(data))); } @@ -160,7 +153,7 @@ public class UserEndpoint extends AbstractEndpoint { } return ResponseEntity.status(HttpStatus.OK) .headers(headers) - .body(userMapper.userToUserDto(user)); + .body(metadataMapper.userToUserDto(user)); } @PutMapping("/{userId}") @@ -208,7 +201,7 @@ public class UserEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to modify user: not current user " + user.getId()); } return ResponseEntity.accepted() - .body(userMapper.userToUserBriefDto( + .body(metadataMapper.userToUserBriefDto( userService.modify(user, data))); } diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index 368e3886be..11e171b056 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -861,7 +861,7 @@ class RestClient: params.append(('page', page)) params.append(('size', size)) if timestamp is not None: - params.append(('timestamp', timestamp)) + params.append(('timestamp', timestamp.strftime("%Y-%m-%dT%H:%M:%SZ"))) response = self._wrapper(method="get", url=url, params=params) if response.status_code == 200: return DataFrame.from_records(response.json()) @@ -1100,15 +1100,12 @@ class RestClient: raise ResponseCodeError(f'Failed to delete table data: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def get_table_data_count(self, database_id: int, table_id: int, page: int = 0, size: int = 10, - timestamp: datetime.datetime = None) -> int: + def get_table_data_count(self, database_id: int, table_id: int, timestamp: datetime.datetime = None) -> int: """ Get data count of a table in a database with given database id and table id. :param database_id: The database id. :param table_id: The table id. - :param page: The result pagination number. Optional. Default: `0`. - :param size: The result pagination size. Optional. Default: `10`. :param timestamp: The query execution time. Optional. :returns: The result of the view query, if successful. @@ -1121,15 +1118,10 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}/table/{table_id}/data' - if page is not None and size is not None: - url += f'?page={page}&size={size}' + params = [] if timestamp is not None: - if page is not None and size is not None: - url += '&' - else: - url += '?' - url += f'timestamp={timestamp}' - response = self._wrapper(method="head", url=url) + params.append(('timestamp', timestamp.strftime("%Y-%m-%dT%H:%M:%SZ"))) + response = self._wrapper(method="head", url=url, params=params) if response.status_code == 200: return int(response.headers.get('X-Count')) if response.status_code == 400: @@ -1204,29 +1196,6 @@ class RestClient: raise ResponseCodeError(f'Failed to get database access: response code: {response.status_code} is not ' f'200 (OK): {response.text}') - def check_database_access(self, database_id: int) -> bool: - """ - Checks access of a view in a database with given database id and view id. - - :param database_id: The database id. - - :returns: The access type, if successful. - - :raises ForbiddenError: If something went wrong with the authorization. - :raises NotExistsError: If the container does not exist. - :raises ResponseCodeError: If something went wrong with the retrieval. - """ - url = f'/api/database/{database_id}/access' - response = self._wrapper(method="get", url=url) - if response.status_code == 200: - return True - if response.status_code == 403: - return False - if response.status_code == 404: - raise NotExistsError(f'Failed to check database access: not found') - raise ResponseCodeError(f'Failed to check database access: response code: {response.status_code} is not ' - f'200 (OK): {response.text}') - def create_database_access(self, database_id: int, user_id: str, type: AccessType) -> AccessType: """ Create access to a database with given database id and user id. @@ -1355,14 +1324,13 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}/subset' + params = [] if page is not None and size is not None: - url += f'?page={page}&size={size}' - if timestamp is not None: - url += f'×tamp={timestamp.strftime("%Y-%m-%dT%H:%M:%SZ")}' - else: - if timestamp is not None: - url += f'?timestamp={timestamp.strftime("%Y-%m-%dT%H:%M:%SZ")}' - response = self._wrapper(method="post", url=url, headers={"Accept": "application/json"}, + params.append(('page', page)) + params.append(('size', size)) + if timestamp is not None: + params.append(('timestamp', timestamp.strftime("%Y-%m-%dT%H:%M:%SZ"))) + response = self._wrapper(method="post", url=url, headers={"Accept": "application/json"}, params=params, payload=ExecuteQuery(statement=query)) if response.status_code == 201: logging.info(f'Created subset with id: {response.headers["X-Id"]}') @@ -1418,14 +1386,12 @@ class RestClient: raise ResponseCodeError(f'Failed to get query data: response code: {response.status_code} is not ' f'200 (OK): {response.text}') - def get_subset_data_count(self, database_id: int, subset_id: int, page: int = 0, size: int = 10) -> int: + def get_subset_data_count(self, database_id: int, subset_id: int) -> int: """ Re-executes a query in a database with given database id and query id and only counts the results. :param database_id: The database id. :param subset_id: The subset id. - :param page: The result pagination number. Optional. Default: `0`. - :param size: The result pagination size. Optional. Default: `10`. :returns: The result set, if successful. @@ -1436,8 +1402,6 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}/subset/{subset_id}/data' - if page is not None and size is not None: - url += f'?page={page}&size={size}' response = self._wrapper(method="head", url=url) if response.status_code == 200: return int(response.headers.get('X-Count')) @@ -1546,13 +1510,13 @@ class RestClient: raise ResponseCodeError(f'Failed to update query: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def create_identifier(self, database_id: int, type: IdentifierType, titles: List[SaveIdentifierTitle], + def create_identifier(self, database_id: int, type: IdentifierType, titles: List[CreateIdentifierTitle], publisher: str, creators: List[CreateIdentifierCreator], publication_year: int, - descriptions: List[SaveIdentifierDescription] = None, - funders: List[SaveIdentifierFunder] = None, licenses: List[License] = None, + descriptions: List[CreateIdentifierDescription] = None, + funders: List[CreateIdentifierFunder] = None, licenses: List[License] = None, language: Language = None, subset_id: int = None, view_id: int = None, table_id: int = None, publication_day: int = None, publication_month: int = None, - related_identifiers: List[SaveRelatedIdentifier] = None) -> Identifier: + related_identifiers: List[CreateRelatedIdentifier] = None) -> Identifier: """ Create an identifier draft. @@ -1585,7 +1549,7 @@ class RestClient: url = f'/api/identifier' payload = CreateIdentifier(database_id=database_id, type=type, titles=titles, publisher=publisher, creators=creators, publication_year=publication_year, descriptions=descriptions, - funders=funders, licenses=licenses, language=language, subset_id=subset_id, + funders=funders, licenses=licenses, language=language, query_id=subset_id, view_id=view_id, table_id=table_id, publication_day=publication_day, publication_month=publication_month, related_identifiers=related_identifiers) response = self._wrapper(method="post", url=url, force_auth=True, payload=payload) @@ -1606,15 +1570,15 @@ class RestClient: raise ResponseCodeError(f'Failed to create identifier: response code: {response.status_code} is not ' f'201 (CREATED): {response.text}') - def save_identifier(self, identifier_id: int, database_id: int, type: IdentifierType, - titles: List[SaveIdentifierTitle], publisher: str, creators: List[CreateIdentifierCreator], - publication_year: int, descriptions: List[SaveIdentifierDescription] = None, - funders: List[SaveIdentifierFunder] = None, licenses: List[License] = None, - language: Language = None, subset_id: int = None, view_id: int = None, table_id: int = None, - publication_day: int = None, publication_month: int = None, - related_identifiers: List[SaveRelatedIdentifier] = None) -> Identifier: + def update_identifier(self, identifier_id: int, database_id: int, type: IdentifierType, + titles: List[SaveIdentifierTitle], publisher: str, creators: List[SaveIdentifierCreator], + publication_year: int, descriptions: List[SaveIdentifierDescription] = None, + funders: List[SaveIdentifierFunder] = None, licenses: List[License] = None, + language: Language = None, subset_id: int = None, view_id: int = None, table_id: int = None, + publication_day: int = None, publication_month: int = None, + related_identifiers: List[SaveRelatedIdentifier] = None) -> Identifier: """ - Save an existing identifier and update the metadata attached to it. + Update an existing identifier and update the metadata attached to it. :param identifier_id: The identifier id. :param database_id: The database id of the created identifier. @@ -1644,11 +1608,12 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the creation of the identifier. """ url = f'/api/identifier/{identifier_id}' - payload = CreateIdentifier(database_id=database_id, type=type, titles=titles, publisher=publisher, - creators=creators, publication_year=publication_year, descriptions=descriptions, - funders=funders, licenses=licenses, language=language, subset_id=subset_id, - view_id=view_id, table_id=table_id, publication_day=publication_day, - publication_month=publication_month, related_identifiers=related_identifiers) + payload = IdentifierSave(id=identifier_id, database_id=database_id, type=type, titles=titles, + publisher=publisher, creators=creators, publication_year=publication_year, + descriptions=descriptions, funders=funders, licenses=licenses, language=language, + query_id=subset_id, view_id=view_id, table_id=table_id, + publication_day=publication_day, publication_month=publication_month, + related_identifiers=related_identifiers) response = self._wrapper(method="put", url=url, force_auth=True, payload=payload) if response.status_code == 202: body = response.json() @@ -1730,7 +1695,8 @@ class RestClient: f'200 (OK): {response.text}') def get_identifiers(self, database_id: int = None, subset_id: int = None, view_id: int = None, - table_id: int = None) -> List[Identifier] | str: + table_id: int = None, type: IdentifierType = None, status: IdentifierStatusType = None) -> List[ + Identifier] | str: """ Get list of identifiers, filter by the remaining optional arguments. @@ -1738,6 +1704,8 @@ class RestClient: :param subset_id: The subset id. Optional. Requires `database_id` to be set. :param view_id: The view id. Optional. Requires `database_id` to be set. :param table_id: The table id. Optional. Requires `database_id` to be set. + :param type: The identifier type. Optional. + :param status: The identifier status. Optional. :returns: List of identifiers, if successful. @@ -1746,28 +1714,33 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the retrieval of the identifiers. """ url = f'/api/identifiers' + params = [] if database_id is not None: - url += f'?dbid={database_id}' + params.append(('dbid', database_id)) if subset_id is not None: if database_id is None: raise RequestError(f'Filtering by subset_id requires database_id to be set') - url += f'&qid={subset_id}' + params.append(('qid', subset_id)) if view_id is not None: if database_id is None: raise RequestError(f'Filtering by view_id requires database_id to be set') - url += f'&vid={view_id}' + params.append(('vid', view_id)) if table_id is not None: if database_id is None: raise RequestError(f'Filtering by table_id requires database_id to be set') - url += f'&tid={table_id}' - response = self._wrapper(method="get", url=url, headers={'Accept': 'application/json'}) + params.append(('tid', table_id)) + if type is not None: + params.append(('type', type)) + if status is not None: + params.append(('status', status)) + response = self._wrapper(method="get", url=url, params=params, headers={'Accept': 'application/json'}) if response.status_code == 200: body = response.json() return TypeAdapter(List[Identifier]).validate_python(body) if response.status_code == 404: raise NotExistsError(f'Failed to get identifiers: requested style is not known') if response.status_code == 406: - raise MalformedError( + raise FormatNotAvailable( f'Failed to get identifiers: accept header must be application/json or application/ld+json') raise ResponseCodeError(f'Failed to get identifiers: response code: {response.status_code} is not ' f'200 (OK): {response.text}') diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index 27801a9482..9e042b966d 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -409,12 +409,16 @@ class IdentifierTitle(BaseModel): type: Optional[TitleType] = None -class SaveIdentifierTitle(BaseModel): +class CreateIdentifierTitle(BaseModel): title: str language: Optional[Language] = None type: Optional[TitleType] = None +class SaveIdentifierTitle(CreateIdentifierTitle): + id: int + + class IdentifierDescription(BaseModel): id: int description: str @@ -422,12 +426,16 @@ class IdentifierDescription(BaseModel): type: Optional[DescriptionType] = None -class SaveIdentifierDescription(BaseModel): +class CreateIdentifierDescription(BaseModel): description: str language: Optional[Language] = None type: Optional[DescriptionType] = None +class SaveIdentifierDescription(CreateIdentifierDescription): + id: int + + class IdentifierFunder(BaseModel): id: int funder_name: str @@ -438,7 +446,7 @@ class IdentifierFunder(BaseModel): award_title: Optional[str] = None -class SaveIdentifierFunder(BaseModel): +class CreateIdentifierFunder(BaseModel): funder_name: str funder_identifier: Optional[str] = None funder_identifier_type: Optional[str] = None @@ -447,6 +455,10 @@ class SaveIdentifierFunder(BaseModel): award_title: Optional[str] = None +class SaveIdentifierFunder(CreateIdentifierFunder): + id: int + + class License(BaseModel): identifier: str uri: str @@ -577,6 +589,10 @@ class CreateIdentifierCreator(BaseModel): affiliation_identifier_scheme_uri: Optional[str] = None +class SaveIdentifierCreator(CreateIdentifierCreator): + id: int + + class RelatedIdentifier(BaseModel): id: int value: str @@ -584,21 +600,25 @@ class RelatedIdentifier(BaseModel): relation: RelatedIdentifierRelation -class SaveRelatedIdentifier(BaseModel): +class CreateRelatedIdentifier(BaseModel): value: str type: RelatedIdentifierType relation: RelatedIdentifierRelation +class SaveRelatedIdentifier(CreateRelatedIdentifier): + id: int + + class CreateIdentifier(BaseModel): database_id: int type: IdentifierType creators: List[CreateIdentifierCreator] publication_year: int publisher: str - titles: List[SaveIdentifierTitle] - descriptions: List[SaveIdentifierDescription] - funders: Optional[List[SaveIdentifierFunder]] = field(default_factory=list) + titles: List[CreateIdentifierTitle] + descriptions: List[CreateIdentifierDescription] + funders: Optional[List[CreateIdentifierFunder]] = field(default_factory=list) doi: Optional[str] = None language: Optional[str] = None licenses: Optional[List[License]] = field(default_factory=list) @@ -608,13 +628,17 @@ class CreateIdentifier(BaseModel): query: Optional[str] = None query_normalized: Optional[str] = None execution: Optional[str] = None - related_identifiers: Optional[List[SaveRelatedIdentifier]] = field(default_factory=list) + related_identifiers: Optional[List[CreateRelatedIdentifier]] = field(default_factory=list) result_hash: Optional[str] = None result_number: Optional[int] = None publication_day: Optional[int] = None publication_month: Optional[int] = None +class IdentifierSave(CreateIdentifier): + id: int + + class Identifier(BaseModel): id: int database_id: int diff --git a/lib/python/tests/test_unit_concept.py b/lib/python/tests/test_unit_concept.py new file mode 100644 index 0000000000..54104dada8 --- /dev/null +++ b/lib/python/tests/test_unit_concept.py @@ -0,0 +1,35 @@ +import unittest + +import requests_mock + +from dbrepo.RestClient import RestClient +from dbrepo.api.dto import ConceptBrief +from dbrepo.api.exceptions import ResponseCodeError + + +class ContainerUnitTest(unittest.TestCase): + + def test_get_concepts_succeeds(self): + with requests_mock.Mocker() as mock: + exp = [ConceptBrief(id=1, + uri="http://dbpedia.org/page/Category:Precipitation", + name="Precipitation")] + # mock + mock.get('/api/concept', json=[exp[0].model_dump()]) + # test + response = RestClient().get_concepts() + self.assertEqual(exp, response) + + def test_get_concepts_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/concept', status_code=202) + # test + try: + RestClient().get_concepts() + except ResponseCodeError: + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py index 0eb4675433..952b27c61f 100644 --- a/lib/python/tests/test_unit_database.py +++ b/lib/python/tests/test_unit_database.py @@ -642,6 +642,16 @@ class DatabaseUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_database_access_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/access', status_code=202) + # test + try: + response = RestClient().get_database_access(database_id=1) + except ResponseCodeError: + pass + def test_create_database_access_succeeds(self): exp = DatabaseAccess(type=AccessType.READ, user=UserBrief(id='abdbf897-e599-4e5a-a3f0-7529884ea011', username='other')) @@ -702,6 +712,42 @@ class DatabaseUnitTest(unittest.TestCase): except NotExistsError: pass + def test_create_database_access_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=502) + # test + try: + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceConnectionError: + pass + + def test_create_database_access_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=503) + # test + try: + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceError: + pass + + def test_create_database_access_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=200) + # test + try: + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ResponseCodeError: + pass + def test_update_database_access_succeeds(self): exp = DatabaseAccess(type=AccessType.READ, user=UserBrief(id='abdbf897-e599-4e5a-a3f0-7529884ea011', username='other')) @@ -751,6 +797,42 @@ class DatabaseUnitTest(unittest.TestCase): except NotExistsError: pass + def test_update_database_access_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=502) + # test + try: + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceConnectionError: + pass + + def test_update_database_access_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=503) + # test + try: + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceError: + pass + + def test_update_database_access_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=200) + # test + try: + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ResponseCodeError: + pass + def test_update_database_access_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock @@ -804,6 +886,39 @@ class DatabaseUnitTest(unittest.TestCase): except NotExistsError: pass + def test_delete_database_access_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=502) + # test + try: + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceConnectionError: + pass + + def test_delete_database_access_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=503) + # test + try: + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceError: + pass + + def test_delete_database_access_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=200) + # test + try: + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ResponseCodeError: + pass + def test_delete_database_access_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock diff --git a/lib/python/tests/test_unit_identifier.py b/lib/python/tests/test_unit_identifier.py index 137f26aab2..36630e3df0 100644 --- a/lib/python/tests/test_unit_identifier.py +++ b/lib/python/tests/test_unit_identifier.py @@ -6,8 +6,10 @@ from dbrepo.RestClient import RestClient from dbrepo.api.dto import Identifier, IdentifierType, SaveIdentifierTitle, Creator, IdentifierTitle, \ IdentifierDescription, SaveIdentifierDescription, Language, SaveIdentifierFunder, SaveRelatedIdentifier, \ RelatedIdentifierRelation, RelatedIdentifierType, IdentifierFunder, RelatedIdentifier, UserBrief, \ - IdentifierStatusType, CreateIdentifierCreator -from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, AuthenticationError + IdentifierStatusType, CreateIdentifierCreator, CreateIdentifierTitle, CreateIdentifierFunder, \ + CreateRelatedIdentifier, CreateIdentifierDescription, SaveIdentifierCreator +from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, AuthenticationError, \ + ServiceConnectionError, ServiceError, ResponseCodeError, FormatNotAvailable, RequestError class IdentifierUnitTest(unittest.TestCase): @@ -36,14 +38,14 @@ class IdentifierUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") response = client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[SaveIdentifierTitle(title='Test Title')], + titles=[CreateIdentifierTitle(title='Test Title')], publisher='TU Wien', publication_year=2024, language=Language.EN, - funders=[SaveIdentifierFunder(funder_name='FWF')], - related_identifiers=[SaveRelatedIdentifier(value='10.12345/abc', - relation=RelatedIdentifierRelation.CITES, - type=RelatedIdentifierType.DOI)], - descriptions=[SaveIdentifierDescription(description='Test Description')], + funders=[CreateIdentifierFunder(funder_name='FWF')], + related_identifiers=[CreateRelatedIdentifier(value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[CreateIdentifierDescription(description='Test Description')], creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) self.assertEqual(exp, response) @@ -54,10 +56,10 @@ class IdentifierUnitTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.create_identifier( + client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[SaveIdentifierTitle(title='Test Title')], - descriptions=[SaveIdentifierDescription(description='Test')], + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except MalformedError: @@ -70,10 +72,10 @@ class IdentifierUnitTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.create_identifier( + client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[SaveIdentifierTitle(title='Test Title')], - descriptions=[SaveIdentifierDescription(description='Test')], + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except ForbiddenError: @@ -86,31 +88,76 @@ class IdentifierUnitTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.create_identifier( + client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[SaveIdentifierTitle(title='Test Title')], - descriptions=[SaveIdentifierDescription(description='Test')], + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except NotExistsError: pass - def test_create_identifier_anonymous_fails(self): + def test_create_identifier_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/identifier', status_code=502) + # test + try: + client = RestClient(username="a", password="b") + client.create_identifier( + database_id=1, type=IdentifierType.VIEW, + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], + publisher='TU Wien', publication_year=2024, + creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) + except ServiceConnectionError: + pass + + def test_create_identifier_503_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/identifier', status_code=503) # test try: - response = RestClient().create_identifier( + client = RestClient(username="a", password="b") + client.create_identifier( + database_id=1, type=IdentifierType.VIEW, + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], + publisher='TU Wien', publication_year=2024, + creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) + except ServiceError: + pass + + def test_create_identifier_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/identifier', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[SaveIdentifierTitle(title='Test Title')], - descriptions=[SaveIdentifierDescription(description='Test')], + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) - except AuthenticationError: + except ResponseCodeError: pass - def test_get_identifiers_succeeds(self): + def test_create_identifier_anonymous_fails(self): + # test + try: + RestClient().create_identifier( + database_id=1, type=IdentifierType.VIEW, + titles=[CreateIdentifierTitle(title='Test Title')], + descriptions=[CreateIdentifierDescription(description='Test')], + publisher='TU Wien', publication_year=2024, + creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) + except AuthenticationError: + pass + + def test_get_identifiers_view_succeeds(self): with requests_mock.Mocker() as mock: exp = [Identifier(id=10, database_id=1, @@ -131,9 +178,469 @@ class IdentifierUnitTest(unittest.TestCase): # mock mock.get('/api/identifiers', json=[exp[0].model_dump()], headers={"Accept": "application/json"}) # test - response = RestClient().get_identifiers() + response = RestClient().get_identifiers(database_id=1, view_id=32, type=IdentifierType.VIEW, + status=IdentifierStatusType.PUBLISHED) + self.assertEqual(exp, response) + + def test_get_identifiers_subset_succeeds(self): + with requests_mock.Mocker() as mock: + exp = [] + # mock + mock.get('/api/identifiers', json=[], headers={"Accept": "application/json"}) + # test + response = RestClient().get_identifiers(database_id=1, subset_id=2) + self.assertEqual(exp, response) + + def test_get_identifiers_table_succeeds(self): + with requests_mock.Mocker() as mock: + exp = [] + # mock + mock.get('/api/identifiers', json=[], headers={"Accept": "application/json"}) + # test + response = RestClient().get_identifiers(database_id=1, table_id=3) self.assertEqual(exp, response) + def test_get_identifiers_view_param_database_fails(self): + # test + try: + RestClient().get_identifiers(view_id=1) + except RequestError: + pass + + def test_get_identifiers_subset_param_database_fails(self): + # test + try: + RestClient().get_identifiers(subset_id=1) + except RequestError: + pass + + def test_get_identifiers_table_param_database_fails(self): + # test + try: + RestClient().get_identifiers(table_id=1) + except RequestError: + pass + + def test_get_identifiers_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/identifiers', status_code=404) + # test + try: + RestClient().get_identifiers() + except NotExistsError: + pass + + def test_get_identifiers_406_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/identifiers', status_code=406) + # test + try: + RestClient().get_identifiers() + except FormatNotAvailable: + pass + + def test_get_identifiers_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/identifiers', status_code=202) + # test + try: + RestClient().get_identifiers() + except ResponseCodeError: + pass + + def test_update_identifier_succeeds(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10', json=exp.model_dump(), status_code=202) + # test + client = RestClient(username="a", password="b") + response = client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + self.assertEqual(exp, response) + + def test_update_identifier_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/identifier/10', status_code=400) + # test + client = RestClient(username="a", password="b") + try: + client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except MalformedError: + pass + + def test_update_identifier_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/identifier/10', status_code=403) + # test + client = RestClient(username="a", password="b") + try: + client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except ForbiddenError: + pass + + def test_update_identifier_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/identifier/10', status_code=404) + # test + client = RestClient(username="a", password="b") + try: + client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except NotExistsError: + pass + + def test_update_identifier_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/identifier/10', status_code=502) + # test + client = RestClient(username="a", password="b") + try: + client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except ServiceConnectionError: + pass + + def test_update_identifier_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/identifier/10', status_code=503) + # test + client = RestClient(username="a", password="b") + try: + client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except ServiceError: + pass + + def test_update_identifier_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/identifier/10', status_code=200) + # test + client = RestClient(username="a", password="b") + try: + client.update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except ResponseCodeError: + pass + + def test_update_identifier_anonymous_fails(self): + # test + try: + RestClient().update_identifier(identifier_id=10, + database_id=1, type=IdentifierType.VIEW, + titles=[SaveIdentifierTitle(id=10, title='Test Title')], + publisher='TU Wien', publication_year=2024, + language=Language.EN, + funders=[SaveIdentifierFunder(id=2, funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(id=2, + value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(id=2, + description='Test Description')], + creators=[SaveIdentifierCreator(id=30, + creator_name='Carberry, Josiah')]) + except AuthenticationError: + pass + + def test_publish_identifier_succeeds(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=202) + # test + client = RestClient(username="a", password="b") + response = client.publish_identifier(identifier_id=10) + self.assertEqual(exp, response) + + def test_publish_identifier_400_fails(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=400) + # test + try: + RestClient(username="a", password="b").publish_identifier(identifier_id=10) + except MalformedError: + pass + + def test_publish_identifier_403_fails(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=403) + # test + try: + RestClient(username="a", password="b").publish_identifier(identifier_id=10) + except ForbiddenError: + pass + + def test_publish_identifier_404_fails(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=404) + # test + try: + RestClient(username="a", password="b").publish_identifier(identifier_id=10) + except NotExistsError: + pass + + def test_publish_identifier_502_fails(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=502) + # test + try: + RestClient(username="a", password="b").publish_identifier(identifier_id=10) + except ServiceConnectionError: + pass + + def test_publish_identifier_503_fails(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=503) + # test + try: + RestClient(username="a", password="b").publish_identifier(identifier_id=10) + except ServiceError: + pass + + def test_publish_identifier_unknown_fails(self): + with requests_mock.Mocker() as mock: + exp = Identifier(id=10, + database_id=1, + view_id=32, + publication_year=2024, + publisher='TU Wien', + type=IdentifierType.VIEW, + language=Language.EN, + descriptions=[IdentifierDescription(id=2, description='Test Description')], + titles=[IdentifierTitle(id=3, title='Test Title')], + funders=[IdentifierFunder(id=4, funder_name='FWF')], + related_identifiers=[ + RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], + status=IdentifierStatusType.PUBLISHED, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) + # mock + mock.put('/api/identifier/10/publish', json=exp.model_dump(), status_code=200) + # test + try: + RestClient(username="a", password="b").publish_identifier(identifier_id=10) + except ResponseCodeError: + pass + + def test_publish_identifier_anonymous_fails(self): + # test + try: + RestClient().publish_identifier(identifier_id=10) + except AuthenticationError: + pass + if __name__ == "__main__": unittest.main() diff --git a/lib/python/tests/test_unit_jwt.py b/lib/python/tests/test_unit_jwt.py deleted file mode 100644 index 4a0748fe38..0000000000 --- a/lib/python/tests/test_unit_jwt.py +++ /dev/null @@ -1,65 +0,0 @@ -import json -from unittest import TestCase, main - -import requests_mock - -from dbrepo.RestClient import RestClient -from dbrepo.api.dto import JwtAuth - - -class DatabaseUnitTest(TestCase): - - def test_get_jwt_auth_succeeds(self): - exp = JwtAuth(access_token='eyABC', - refresh_token='ey123', - id_token='eyXYZ', - expires_in=3600, - refresh_expires_in=36000, - not_before_policy=0, - scope='openid', - session_state='4604e4b1-2163-42c3-806d-3be2e426c3a5', - token_type='Bearer') - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', json=exp.model_dump(), status_code=202) - # test - response = RestClient().get_jwt_auth(username='foo', password='bar') - self.assertEqual(exp, response) - - def test_get_jwt_auth_empty_succeeds(self): - exp = JwtAuth(access_token='eyABC', - refresh_token='ey123', - id_token='eyXYZ', - expires_in=3600, - refresh_expires_in=36000, - not_before_policy=0, - scope='openid', - session_state='4604e4b1-2163-42c3-806d-3be2e426c3a5', - token_type='Bearer') - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', json=exp.model_dump(), status_code=202) - # test - response = RestClient().get_jwt_auth() - self.assertEqual(exp, response) - - def test_refresh_jwt_auth_succeeds(self): - exp = JwtAuth(access_token='eyABC', - refresh_token='ey123', - id_token='eyXYZ', - expires_in=3600, - refresh_expires_in=36000, - not_before_policy=0, - scope='openid', - session_state='4604e4b1-2163-42c3-806d-3be2e426c3a5', - token_type='Bearer') - with requests_mock.Mocker() as mock: - # mock - mock.put('/api/user/token', json=exp.model_dump(), status_code=202) - # test - response = RestClient().refresh_jwt_auth(refresh_token='ey123') - self.assertEqual(exp, response) - - -if __name__ == "__main__": - main() diff --git a/lib/python/tests/test_unit_license.py b/lib/python/tests/test_unit_license.py index f64ed76d31..b2bb3a79f3 100644 --- a/lib/python/tests/test_unit_license.py +++ b/lib/python/tests/test_unit_license.py @@ -3,8 +3,8 @@ import unittest import requests_mock from dbrepo.RestClient import RestClient - from dbrepo.api.dto import License +from dbrepo.api.exceptions import ResponseCodeError class LicenseUnitTest(unittest.TestCase): @@ -27,6 +27,16 @@ class LicenseUnitTest(unittest.TestCase): response = RestClient().get_licenses() self.assertEqual(exp, response) + def test_get_licenses_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/license', status_code=202) + # test + try: + RestClient().get_licenses() + except ResponseCodeError: + pass + if __name__ == "__main__": unittest.main() diff --git a/lib/python/tests/test_unit_query.py b/lib/python/tests/test_unit_query.py index 31b4182a4b..415a35b86c 100644 --- a/lib/python/tests/test_unit_query.py +++ b/lib/python/tests/test_unit_query.py @@ -1,14 +1,14 @@ +import datetime import json import unittest import requests_mock -import datetime - -from dbrepo.RestClient import RestClient from pandas import DataFrame -from dbrepo.api.dto import Query, User, UserAttributes, QueryType, UserBrief -from dbrepo.api.exceptions import MalformedError, NotExistsError, ForbiddenError +from dbrepo.RestClient import RestClient +from dbrepo.api.dto import Query, QueryType, UserBrief +from dbrepo.api.exceptions import MalformedError, NotExistsError, ForbiddenError, QueryStoreError, FormatNotAvailable, \ + ServiceError, ResponseCodeError, AuthenticationError class QueryUnitTest(unittest.TestCase): @@ -22,6 +22,7 @@ class QueryUnitTest(unittest.TestCase): # test client = RestClient(username="a", password="b") response = client.create_subset(database_id=1, page=0, size=10, + timestamp=datetime.datetime(2024, 1, 1, 0, 0, 0, 0, datetime.timezone.utc), query="SELECT id, username FROM some_table WHERE id IN (1,2)") self.assertTrue(DataFrame.equals(df, response)) @@ -32,8 +33,8 @@ class QueryUnitTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.create_subset(database_id=1, - query="SELECT id, username FROM some_table WHERE id IN (1,2)") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") except MalformedError: pass @@ -44,8 +45,8 @@ class QueryUnitTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.create_subset(database_id=1, - query="SELECT id, username FROM some_table WHERE id IN (1,2)") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") except ForbiddenError: pass @@ -56,11 +57,59 @@ class QueryUnitTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.create_subset(database_id=1, - query="SELECT id, username FROM some_table WHERE id IN (1,2)") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") except NotExistsError: pass + def test_create_subset_417_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/subset', status_code=417) + # test + try: + client = RestClient(username="a", password="b") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") + except QueryStoreError: + pass + + def test_create_subset_501_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/subset', status_code=501) + # test + try: + client = RestClient(username="a", password="b") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") + except FormatNotAvailable: + pass + + def test_create_subset_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/subset', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") + except ServiceError: + pass + + def test_create_subset_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/subset', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.create_subset(database_id=1, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") + except ResponseCodeError: + pass + def test_create_subset_anonymous_succeeds(self): with requests_mock.Mocker() as mock: exp = [{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}] @@ -74,7 +123,7 @@ class QueryUnitTest(unittest.TestCase): query="SELECT id, username FROM some_table WHERE id IN (1,2)") self.assertTrue(DataFrame.equals(df, response)) - def test_find_query_succeeds(self): + def test_get_subset_succeeds(self): with requests_mock.Mocker() as mock: exp = Query(id=6, owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), @@ -94,26 +143,56 @@ class QueryUnitTest(unittest.TestCase): response = RestClient().get_subset(database_id=1, subset_id=6) self.assertEqual(exp, response) - def test_find_query_403_fails(self): + def test_get_subset_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6', status_code=403) # test try: - response = RestClient().get_subset(database_id=1, subset_id=6) + RestClient().get_subset(database_id=1, subset_id=6) except ForbiddenError: pass - def test_find_query_404_fails(self): + def test_get_subset_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6', status_code=404) # test try: - response = RestClient().get_subset(database_id=1, subset_id=6) + RestClient().get_subset(database_id=1, subset_id=6) except NotExistsError: pass + def test_get_subset_406_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset/6', status_code=406) + # test + try: + RestClient().get_subset(database_id=1, subset_id=6) + except FormatNotAvailable: + pass + + def test_get_subset_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset/6', status_code=503) + # test + try: + RestClient().get_subset(database_id=1, subset_id=6) + except ServiceError: + pass + + def test_get_subset_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset/6', status_code=202) + # test + try: + RestClient().get_subset(database_id=1, subset_id=6) + except ResponseCodeError: + pass + def test_get_queries_empty_succeeds(self): with requests_mock.Mocker() as mock: exp = [] @@ -123,6 +202,94 @@ class QueryUnitTest(unittest.TestCase): response = RestClient().get_queries(database_id=1) self.assertEqual(exp, response) + def test_update_subset_succeeds(self): + with requests_mock.Mocker() as mock: + exp = Query(id=6, + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), + execution=datetime.datetime(2024, 1, 1, 0, 0, 0, 0, datetime.timezone.utc), + query='SELECT id, username FROM some_table WHERE id IN (1,2)', + query_normalized='SELECT id, username FROM some_table WHERE id IN (1,2)', + type=QueryType.QUERY, + database_id=1, + query_hash='da5ff66c4a57683171e2ffcec25298ee684680d1e03633cd286f9067d6924ad8', + result_hash='464740ba612225913bb15b26f13377707949b55e65288e89c3f8b4c6469aecb4', + is_persisted=True, + result_number=None, + identifiers=[]) + # mock + mock.put('/api/database/1/subset/6', json=exp.model_dump(), status_code=202) + # test + response = RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, + persist=True) + self.assertEqual(exp, response) + + def test_update_subset_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/subset/6', status_code=400) + # test + try: + RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, persist=True) + except MalformedError: + pass + + def test_update_subset_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/subset/6', status_code=403) + # test + try: + RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, persist=True) + except ForbiddenError: + pass + + def test_update_subset_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/subset/6', status_code=404) + # test + try: + RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, persist=True) + except NotExistsError: + pass + + def test_update_subset_417_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/subset/6', status_code=417) + # test + try: + RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, persist=True) + except QueryStoreError: + pass + + def test_update_subset_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/subset/6', status_code=503) + # test + try: + RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, persist=True) + except ServiceError: + pass + + def test_update_subset_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/subset/6', status_code=200) + # test + try: + RestClient(username='foo', password='bar').update_subset(database_id=1, subset_id=6, persist=True) + except ResponseCodeError: + pass + + def test_update_subset_anonymous_fails(self): + # test + try: + RestClient().update_subset(database_id=1, subset_id=6, persist=True) + except AuthenticationError: + pass + def test_get_queries_succeeds(self): with requests_mock.Mocker() as mock: exp = [Query(id=6, @@ -149,7 +316,7 @@ class QueryUnitTest(unittest.TestCase): mock.get('/api/database/1/subset', status_code=403) # test try: - response = RestClient().get_queries(database_id=1) + RestClient().get_queries(database_id=1) except ForbiddenError: pass @@ -159,10 +326,30 @@ class QueryUnitTest(unittest.TestCase): mock.get('/api/database/1/subset', status_code=404) # test try: - response = RestClient().get_queries(database_id=1) + RestClient().get_queries(database_id=1) except NotExistsError: pass + def test_get_queries_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset', status_code=503) + # test + try: + RestClient().get_queries(database_id=1) + except ServiceError: + pass + + def test_get_queries_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset', status_code=202) + # test + try: + RestClient().get_queries(database_id=1) + except ResponseCodeError: + pass + def test_get_subset_data_succeeds(self): with requests_mock.Mocker() as mock: exp = [{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}] @@ -184,13 +371,23 @@ class QueryUnitTest(unittest.TestCase): self.assertEqual(df.shape, response.shape) self.assertTrue(DataFrame.equals(df, response)) + def test_get_subset_data_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset/6/data', status_code=400) + # test + try: + RestClient().get_subset_data(database_id=1, subset_id=6) + except MalformedError: + pass + def test_get_subset_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6/data', status_code=403) # test try: - response = RestClient().get_subset_data(database_id=1, subset_id=6) + RestClient().get_subset_data(database_id=1, subset_id=6) except ForbiddenError: pass @@ -200,10 +397,30 @@ class QueryUnitTest(unittest.TestCase): mock.get('/api/database/1/subset/6/data', status_code=404) # test try: - response = RestClient().get_subset_data(database_id=1, subset_id=6) + RestClient().get_subset_data(database_id=1, subset_id=6) except NotExistsError: pass + def test_get_subset_data_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset/6/data', status_code=503) + # test + try: + RestClient().get_subset_data(database_id=1, subset_id=6) + except ServiceError: + pass + + def test_get_subset_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/subset/6/data', status_code=202) + # test + try: + RestClient().get_subset_data(database_id=1, subset_id=6) + except ResponseCodeError: + pass + def test_get_subset_data_count_succeeds(self): with requests_mock.Mocker() as mock: exp = 2 @@ -213,13 +430,23 @@ class QueryUnitTest(unittest.TestCase): response = RestClient().get_subset_data_count(database_id=1, subset_id=6) self.assertEqual(exp, response) + def test_get_subset_data_count_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/subset/6/data', status_code=400) + # test + try: + RestClient().get_subset_data_count(database_id=1, subset_id=6) + except MalformedError: + pass + def test_get_subset_data_count_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/subset/6/data', status_code=403) # test try: - response = RestClient().get_subset_data_count(database_id=1, subset_id=6) + RestClient().get_subset_data_count(database_id=1, subset_id=6) except ForbiddenError: pass @@ -229,10 +456,30 @@ class QueryUnitTest(unittest.TestCase): mock.head('/api/database/1/subset/6/data', status_code=404) # test try: - response = RestClient().get_subset_data_count(database_id=1, subset_id=6) + RestClient().get_subset_data_count(database_id=1, subset_id=6) except NotExistsError: pass + def test_get_subset_data_count_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/subset/6/data', status_code=503) + # test + try: + RestClient().get_subset_data_count(database_id=1, subset_id=6) + except ServiceError: + pass + + def test_get_subset_data_count_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/subset/6/data', status_code=202) + # test + try: + RestClient().get_subset_data_count(database_id=1, subset_id=6) + except ResponseCodeError: + pass + if __name__ == "__main__": unittest.main() diff --git a/lib/python/tests/test_unit_rest_client.py b/lib/python/tests/test_unit_rest_client.py index 0c52cfd33e..e4eef89066 100644 --- a/lib/python/tests/test_unit_rest_client.py +++ b/lib/python/tests/test_unit_rest_client.py @@ -1,6 +1,8 @@ import os import unittest +import requests_mock + from dbrepo.RestClient import RestClient @@ -12,6 +14,15 @@ class RestClientUnitTest(unittest.TestCase): response = RestClient() self.assertTrue(response.secure) + def test_constructor_token_succeeds(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/user', json=[]) + # test + client = RestClient(password='bar') + client.get_users() + self.assertEqual('bar', client.password) + def test_whoami_anonymous_succeeds(self): # test response = RestClient().whoami() diff --git a/lib/python/tests/test_unit_table.py b/lib/python/tests/test_unit_table.py index 998a1bc4f0..3518439b8c 100644 --- a/lib/python/tests/test_unit_table.py +++ b/lib/python/tests/test_unit_table.py @@ -491,7 +491,8 @@ class TableUnitTest(unittest.TestCase): # mock mock.head('/api/database/1/table/9/data', headers={'X-Count': str(exp)}) # test - response = RestClient().get_table_data_count(database_id=1, table_id=9) + response = RestClient().get_table_data_count(database_id=1, table_id=9, + timestamp=datetime.datetime(2024, 1, 1, 0, 0, 0, 0)) self.assertEqual(exp, response) def test_get_table_data_count_400_fails(self): @@ -524,6 +525,26 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_table_data_count_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/table/9/data', status_code=503) + # test + try: + response = RestClient().get_table_data_count(database_id=1, table_id=9) + except ServiceError: + pass + + def test_get_table_data_count_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/table/9/data', status_code=202) + # test + try: + response = RestClient().get_table_data_count(database_id=1, table_id=9) + except ResponseCodeError: + pass + def test_get_table_data_count_not_countable_fails(self): with requests_mock.Mocker() as mock: # mock @@ -770,10 +791,7 @@ class TableUnitTest(unittest.TestCase): database_id=1, table_id=2, internal_name="id", - auto_generated=True, - is_primary_key=True, type=ColumnType.BIGINT, - is_public=True, concept=ConceptBrief(id=2, uri="http://dbpedia.org/page/Category:Precipitation", name="Precipitation"), @@ -829,6 +847,45 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_update_table_column_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/table/2/column/1', status_code=502) + # test + try: + client = RestClient(username="a", password="b") + client.update_table_column(database_id=1, table_id=2, column_id=1, + unit_uri="http://www.wikidata.org/entity/Q119856947", + concept_uri="http://dbpedia.org/page/Category:Precipitation") + except ServiceConnectionError: + pass + + def test_update_table_column_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/table/2/column/1', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.update_table_column(database_id=1, table_id=2, column_id=1, + unit_uri="http://www.wikidata.org/entity/Q119856947", + concept_uri="http://dbpedia.org/page/Category:Precipitation") + except ServiceError: + pass + + def test_update_table_column_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/table/2/column/1', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.update_table_column(database_id=1, table_id=2, column_id=1, + unit_uri="http://www.wikidata.org/entity/Q119856947", + concept_uri="http://dbpedia.org/page/Category:Precipitation") + except ResponseCodeError: + pass + def test_update_table_column_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock diff --git a/lib/python/tests/test_unit_view.py b/lib/python/tests/test_unit_view.py index 842c2d7388..7a8bebe4e0 100644 --- a/lib/python/tests/test_unit_view.py +++ b/lib/python/tests/test_unit_view.py @@ -504,6 +504,46 @@ class ViewUnitTest(unittest.TestCase): except ForbiddenError: pass + def test_get_view_data_count_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/view/3/data', status_code=404) + # test + try: + response = RestClient().get_view_data_count(database_id=1, view_id=3) + except NotExistsError: + pass + + def test_get_view_data_count_409_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/view/3/data', status_code=409) + # test + try: + response = RestClient().get_view_data_count(database_id=1, view_id=3) + except ExternalSystemError: + pass + + def test_get_view_data_count_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/view/3/data', status_code=503) + # test + try: + response = RestClient().get_view_data_count(database_id=1, view_id=3) + except ServiceError: + pass + + def test_get_view_data_count_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database/1/view/3/data', status_code=202) + # test + try: + response = RestClient().get_view_data_count(database_id=1, view_id=3) + except ResponseCodeError: + pass + if __name__ == "__main__": unittest.main() -- GitLab