Upload and download failures were printed and then forgotten: a batch that lost files still exited 0, and a file the client could not write was listed as if it had arrived. Nothing downstream could tell. - Return a verdict from every transfer worker and raise once at the end, so a run that lost a file exits 2. - Raise the error when a downloaded file cannot be written locally, instead of printing it and reporting the path as a success. - Write a download beside its destination and move it into place once complete, and refuse a destination that cannot be written, so a failed transfer no longer leaves a truncated file behind. - Carry on through the remaining sub-folders when a recursive download loses a file or cannot create a folder locally. - Name the file and give the reason in every failure message. - Count a failure the API layer did not raise, such as a file that disappeared between the directory walk and its turn to be sent. - Extract the directory walk from `Uploader.upload` to keep it within the complexity limit. - Cover all of the above in `tests/test_transfer.py`.
163 lines
6.2 KiB
Markdown
163 lines
6.2 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to this project will be documented in this file.
|
|
|
|
## [Unreleased]
|
|
|
|
### Fixed
|
|
- Reported a failed upload to the caller. A file the server refused was printed and then forgotten, so a batch that lost files still ended in success and scripts could not tell.
|
|
- Reported a failed single-file download to the caller, which was counted internally but never raised, so only recursive downloads ever ended in failure.
|
|
- Raised the error when a downloaded file could not be written locally. A permission error was printed and the path was then listed as if the file had arrived.
|
|
- Wrote a download to a temporary file beside its destination and moved it into place once the whole body had arrived. A transfer that failed part way used to leave a truncated file under the real name, and a destination that could not be opened was then deleted even though nothing had been written to it. A destination the client may not write is now refused before anything is fetched.
|
|
- Named the file in the message when an upload failed, and counted a failure the API layer did not raise, such as a file that disappeared between the directory walk and its turn to be sent.
|
|
- Carried on through the remaining sub-folders when a recursive download lost a file or could not be created locally, instead of abandoning the rest of the tree at the first failure. Every failure is now printed against its own file and reported once at the end.
|
|
- Said why a download failed, rather than printing the path alone.
|
|
- Serialised the token refresh across processes. Concurrent `mdrs` invocations shared one refresh token and each sent it, so a provider that rotates refresh tokens accepted the first and refused the rest.
|
|
- Wrote the login cache through a temporary file so a reader can no longer catch it mid-truncation and discard the session.
|
|
- Joined the base URL and the API's relative path correctly in `ls --json` output, which produced a doubled separator when the configured URL ended with one.
|
|
|
|
### Changed
|
|
- Accepted bare hostnames such as `localhost` in `config create`/`config update`, and stored the URL without its trailing slash, matching the Rust client so both can share `config.ini`.
|
|
- Bounded the token refresh request with its own timeout, so a provider that stops answering cannot hold the cross-process lock indefinitely.
|
|
- **Breaking for embedders:** `CacheInterface` now requires `lock()` and `reload()`. A cache passed to `MdrsClient.from_remote(..., cache=...)` must provide both; `InMemoryCache` implements them as no-ops.
|
|
|
|
## [1.3.18] - 2026-07-02
|
|
|
|
### Added
|
|
- Added a comprehensive unit test suite in `tests/test_commands.py` checking registration, parsing, and execution flow of all 16 commands.
|
|
|
|
### Refactored
|
|
- Abstracted configuration storage (introducing `ConfigInterface`, `InMemoryConfig`, and updating `ConfigFile`) to enable dependency injection.
|
|
- Modularized transfer operations (upload and download) to decouple them from the service layer.
|
|
- Decoupled commands from direct file system configurations and migrated all subcommands to use abstract config classes.
|
|
|
|
### Changed
|
|
- Upgraded dependencies including `pydantic-settings` to `2.14.2` and `pyright` to `1.1.411`.
|
|
|
|
### Fixed
|
|
- Fixed duplicate `__all__` definitions in package initialization file `mdrsclient/__init__.py` that caused `__version__` export to be overwritten.
|
|
|
|
## [1.3.17] - 2026-07-02
|
|
|
|
### Refactored
|
|
- Decoupled core logic from CLI interface and introduced `MdrsClient` service layer to improve library portability.
|
|
- Migrated all CLI commands to utilize `MdrsClient` for execution.
|
|
|
|
### Added
|
|
- Abstract authentication state using `CacheInterface` and `InMemoryCache`.
|
|
|
|
## [1.3.16] - 2026-06-12
|
|
|
|
### Fixed
|
|
- Retrieve the full `Folder` object from `FoldersApi` instead of using the `FolderSimple` returned by `find_sub_folder` when resolving DOI subfolders. This fixes a type checker error under the upgraded pyright and avoids a potential AttributeError at runtime due to `FolderSimple` lacking the `path` attribute.
|
|
|
|
### Changed
|
|
- Upgraded dependencies and bumped version to 1.3.16 in pyproject.toml.
|
|
|
|
## [1.3.15] - 2026-05-01
|
|
|
|
### Fixed
|
|
- Apply NFC normalization to filenames and folder names sent to the server.
|
|
|
|
## [1.3.14] - 2026-04-17
|
|
|
|
### Changed
|
|
- Simplified `config list` command (removed `-l`/`--long` option, always display URL).
|
|
- Renamed `--quick` option to `--quiet` for `ls` subcommand.
|
|
|
|
### Added
|
|
- Added subcommand aliases for config commands (e.g. `ls` alias for list, `rm` alias for delete).
|
|
- Added `version` command.
|
|
|
|
## [1.3.13] - 2025-07-02
|
|
|
|
### Fixed
|
|
- Fixed pagination logic for the `file.list` API.
|
|
|
|
## [1.3.12] - 2025-05-20
|
|
|
|
### Fixed
|
|
- Fixed bug where file downloading was skipped incorrectly when `-s`/`--skip-if-file-exists` option was present.
|
|
|
|
## [1.3.11] - 2025-01-21
|
|
|
|
### Fixed
|
|
- Follow-up fixes for User API specification changes.
|
|
|
|
## [1.3.10] - 2024-12-23
|
|
|
|
### Added
|
|
- Delete broken files and show a summary when a file download fails.
|
|
|
|
### Changed
|
|
- Updated dependency libraries.
|
|
|
|
## [1.3.9] - 2024-10-23
|
|
|
|
### Fixed
|
|
- Fixed compatibility with Python 3.10.
|
|
|
|
## [1.3.8] - 2024-09-18
|
|
|
|
### Added
|
|
- Implemented `-s`/`--skip-if-file-exists` option for `download` command.
|
|
|
|
### Fixed
|
|
- Added exception handling for unexpected responses from the server.
|
|
|
|
## [1.3.7] - 2024-07-22
|
|
|
|
### Added
|
|
- Implemented `--exclude` argument for download subcommand.
|
|
|
|
## [1.3.6] - 2024-07-08
|
|
|
|
### Added
|
|
- Support cancelling recursive downloads if downloading some files fails.
|
|
|
|
## [1.3.5] - 2024-07-08
|
|
|
|
### Added
|
|
- Added authorization token validation checks for file download operations.
|
|
|
|
### Removed
|
|
- Removed unnecessary debug code.
|
|
|
|
## [1.3.4] - 2024-07-04
|
|
|
|
### Added
|
|
- Added some aliases for config sub command.
|
|
|
|
### Fixed
|
|
- Fixed bug when uploading large files.
|
|
|
|
## [1.3.3] - 2024-02-13
|
|
|
|
### Added
|
|
- Implemented `-s`/`--skip-if-file-exists` option for `upload` command.
|
|
|
|
## [1.3.2] - 2024-02-09
|
|
|
|
### Added
|
|
- Added `-u` and `-p` options to login command.
|
|
|
|
## [1.3.1] - 2023-12-20
|
|
|
|
### Fixed
|
|
- Fixed bug to resolve local files for recursive file upload.
|
|
|
|
## [1.3.0] - 2023-12-18
|
|
|
|
### Changed
|
|
- Removed debug comments.
|
|
|
|
## [1.2.0] - 2023-10-04
|
|
|
|
### Changed
|
|
- Follow-up recent specification changes about folder access level.
|
|
|
|
## [1.1.1] - 2023-07-26
|
|
|
|
### Changed
|
|
- Set destination folder name using name attribute of folder copy API.
|