Skip to content

Commit f87917e

Browse files
authored
fix: improve errors on backend errors (#103)
1 parent 422d504 commit f87917e

File tree

8 files changed

+62
-4
lines changed

8 files changed

+62
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
44

5-
## Unreleased
5+
## Version [v0.1.15] - 2025-08-29
66

77
### Fixed
88

99
* Top-level `.juliabundleignore` files are now correctly handled when using `JuliaHub.appbundle`. ([#99], [#100])
10+
* The `JuliaHub.upload_dataset` now correctly throws a `JuliaHubError` on certain backend errors. ([#103])
1011

1112
## Version [v0.1.14] - 2025-06-11
1213

@@ -159,6 +160,7 @@ Initial package release.
159160
[v0.1.12]: https://github.com/JuliaComputing/JuliaHub.jl/releases/tag/v0.1.12
160161
[v0.1.13]: https://github.com/JuliaComputing/JuliaHub.jl/releases/tag/v0.1.13
161162
[v0.1.14]: https://github.com/JuliaComputing/JuliaHub.jl/releases/tag/v0.1.14
163+
[v0.1.15]: https://github.com/JuliaComputing/JuliaHub.jl/releases/tag/v0.1.15
162164
[#1]: https://github.com/JuliaComputing/JuliaHub.jl/issues/1
163165
[#2]: https://github.com/JuliaComputing/JuliaHub.jl/issues/2
164166
[#3]: https://github.com/JuliaComputing/JuliaHub.jl/issues/3
@@ -194,3 +196,4 @@ Initial package release.
194196
[#96]: https://github.com/JuliaComputing/JuliaHub.jl/issues/96
195197
[#99]: https://github.com/JuliaComputing/JuliaHub.jl/issues/99
196198
[#100]: https://github.com/JuliaComputing/JuliaHub.jl/issues/100
199+
[#103]: https://github.com/JuliaComputing/JuliaHub.jl/issues/103

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "JuliaHub"
22
uuid = "bc7fa6ce-b75e-4d60-89ad-56c957190b6e"
33
authors = ["JuliaHub Inc."]
4-
version = "0.1.14"
4+
version = "0.1.15"
55

66
[deps]
77
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

src/datasets.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,9 @@ function _new_dataset(
793793
end
794794

795795
function _open_dataset_version(auth::Authentication, name::AbstractString)::_RESTResponse
796-
_restcall(auth, :POST, "user", "datasets", name, "versions")
796+
r = _restcall(auth, :POST, "user", "datasets", name, "versions")
797+
_check_internal_error(r; var="POST /user/datasets/{name}/versions")
798+
return r
797799
end
798800

799801
function _upload_dataset(upload_config, local_path; progress::Bool)

src/projects.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,14 @@ function _open_dataset_version(
282282
auth::Authentication, dataset_uuid::UUID, project_uuid::UUID
283283
)::_RESTResponse
284284
body = Dict("project" => string(project_uuid))
285-
return JuliaHub._restcall(
285+
r = JuliaHub._restcall(
286286
auth,
287287
:POST,
288288
("datasets", string(dataset_uuid), "versions"),
289289
JSON.json(body),
290290
)
291+
_check_internal_error(r; var="POST /user/datasets/{name}/versions")
292+
return r
291293
end
292294

293295
function _close_dataset_version(

src/restapi.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ function _parse_response_json(r::_RESTResponse, ::Type{T})::Tuple{T, String} whe
7979
return _parse_response_json(r.body, T)
8080
end
8181

82+
# Check that the API response is not a legacy 200 internal error, where
83+
# we return
84+
#
85+
# {"success": false, "interal_error": true, "message": "..."}
86+
#
87+
# on internal errors. If it detects that this is an internal error, it throws
88+
# a JuliaHubError. Returns `nothing` otherwise.
89+
function _check_internal_error(r::_RESTResponse; var::AbstractString)
90+
if !(r.status == 200)
91+
return nothing
92+
end
93+
success = _get_json_or(r.json, "success", Any, nothing)
94+
internal_error = _get_json_or(r.json, "internal_error", Any, nothing)
95+
if (success === false) && (internal_error === true)
96+
e = JuliaHubError(
97+
"""
98+
Internal Server Error 200 response from JuliaHub ($var):
99+
JSON: $(sprint(show, MIME("text/plain"), r.json))
100+
""",
101+
)
102+
throw(e)
103+
end
104+
return nothing
105+
end
106+
82107
# _restcall calls _rest_request_mockable which calls _rest_request_http. The reason for this
83108
# indirection is that the signature of _rest_request_mockable is extremely simple and therefore
84109
# each to hook into with Mockable.

test/datasets.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,15 @@ end
435435
@test_throws TypeError JuliaHub.upload_dataset(
436436
"example-dataset-license", @__FILE__; replace=true, license=(:fulltext, 1234)
437437
)
438+
439+
# Make sure we throw a JuliaHubError when we encounter an internal backend error
440+
# that gets reported over a 200.
441+
@test JuliaHub.upload_dataset("example-dataset-200-error-1", @__FILE__) isa JuliaHub.Dataset
442+
MOCK_JULIAHUB_STATE[:internal_error_200] = true
443+
@test_throws JuliaHub.JuliaHubError JuliaHub.upload_dataset(
444+
"example-dataset-200-error", @__FILE__
445+
)
446+
MOCK_JULIAHUB_STATE[:internal_error_200] = false
438447
end
439448
empty!(MOCK_JULIAHUB_STATE)
440449
end

test/mocking.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ JuliaHub.__AUTH__[] = DEFAULT_GLOBAL_MOCK_AUTH
4646
Mocking.activate()
4747
const MOCK_JULIAHUB_STATE = Dict{Symbol, Any}()
4848
jsonresponse(status) = d -> JuliaHub._RESTResponse(status, JSON.json(d))
49+
function internal_error_200_response()
50+
d = Dict(
51+
"success" => false,
52+
"internal_error" => true,
53+
"message" => "Internal Server Error",
54+
)
55+
return JuliaHub._RESTResponse(200, JSON.json(d))
56+
end
4957
mocking_patch = [
5058
Mocking.@patch(
5159
JuliaHub._rest_request_mockable(args...; kwargs...) = _restcall_mocked(args...; kwargs...)
@@ -412,6 +420,9 @@ function _restcall_mocked(method, url, headers, payload; query)
412420
Dict("repo_id" => string(UUIDs.uuid4())) |> jsonresponse(200)
413421
end
414422
elseif (method == :POST) && endswith(url, DATASET_VERSIONS_REGEX)
423+
if get(MOCK_JULIAHUB_STATE, :internal_error_200, false)
424+
return internal_error_200_response()
425+
end
415426
dataset, is_user = let m = match(DATASET_VERSIONS_REGEX, url)
416427
URIs.unescapeuri(m[2]), m[1] == "user/"
417428
end

test/projects.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ end
243243
@test dataset.project.uuid === project_auth_2.project_id
244244
@test dataset.project.is_writable === false
245245
@test JuliaHub.upload_project_dataset(dataset_noproject, @__FILE__) isa JuliaHub.Dataset
246+
247+
MOCK_JULIAHUB_STATE[:internal_error_200] = true
248+
@test_throws JuliaHub.JuliaHubError JuliaHub.upload_project_dataset(
249+
dataset_noproject, @__FILE__
250+
)
251+
MOCK_JULIAHUB_STATE[:internal_error_200] = false
246252
end
247253
end
248254

0 commit comments

Comments
 (0)