Skip to content

Commit af5fed3

Browse files
authored
fix: handle backend responses more gracefully in datasets requests (#46)
1 parent 7394f23 commit af5fed3

File tree

6 files changed

+78
-12
lines changed

6 files changed

+78
-12
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
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+
## Version v0.1.7 - 2024-01-18
6+
7+
### Fixed
8+
9+
* `JuliaHub.datasets` and `JuliaHub.dataset` now handle problematic backend responses more gracefully. (#46)
10+
511
## Version v0.1.6 - 2023-11-27
612

713
### Fixed
814

9-
* `JuliaHub.appbundle`, when it has to generate a `Projec.toml` file, now correctly includes it in the appbundle tarball. (#44)
15+
* `JuliaHub.appbundle`, when it has to generate a `Project.toml` file, now correctly includes it in the appbundle tarball. (#44)
1016
* `JuliaHub.appbundle` now works with relative paths such as `"."`. (#44)
1117

1218
## Version v0.1.5 - 2023-09-27

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.6"
4+
version = "0.1.7"
55

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

src/datasets.jl

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -312,29 +312,50 @@ function datasets(
312312
auth::Authentication=__auth__(),
313313
)
314314
datasets, _ = try
315-
_get_datasets(; auth)
315+
_get_datasets(auth)
316316
catch e
317317
e isa JuliaHubException && rethrow(e)
318318
e isa JuliaHubError && rethrow(e)
319319
throw(
320320
JuliaHubError("Error while retrieving datasets from the server", e, catch_backtrace()),
321321
)
322322
end
323-
shared || filter!(datasets) do dataset
324-
dataset["owner"]["username"] == username
323+
# It might happen that some of the elements of the `datasets` array can not be parsed for some reason,
324+
# and the Dataset() constructor will throw. Rather than having `datasets` throw an error (as we would
325+
# normally do for invalid backend responses), in this case we handle the situation more gracefully,
326+
# and just filter out the invalid elements (and print a warning to the user -- it's still an unusual
327+
# situation and should be reported). This is because we also re-use `JuliaHub.datasets` in
328+
# `JuliaHub.dataset` when fetching the information for just one dataset, and unconditionally throwing
329+
# would mean that `JuliaHub.dataset` can break due an issue with an unrelated dataset.
330+
n_erroneous_datasets = 0
331+
datasets = map(datasets) do dataset
332+
try
333+
# We also use the `nothing` method for filtering out datasets that are not owned by the
334+
# current `username` if `shared = false`.
335+
if !shared && (dataset["owner"]["username"] != username)
336+
return nothing
337+
end
338+
return Dataset(dataset)
339+
catch e
340+
@debug "Invalid dataset in GET /datasets response" dataset exception = (
341+
e, catch_backtrace()
342+
)
343+
n_erroneous_datasets += 1
344+
return nothing
345+
end
325346
end
326-
return try
327-
Dataset.(datasets)
328-
catch e
329-
throw(JuliaHubError("Unable to parse the response from the server", e, catch_backtrace()))
347+
if n_erroneous_datasets > 0
348+
@warn "The JuliaHub GET /datasets response contains erroneous datasets. Omitting $(n_erroneous_datasets) entries."
330349
end
350+
# We'll filter down to just Dataset objects, and enforce type-stability on the array type here.
351+
return Dataset[ds for ds in datasets if isa(ds, Dataset)]
331352
end
332353

333354
function datasets(; auth::Authentication=__auth__(), kwargs...)
334355
datasets(auth.username; auth, kwargs...)
335356
end
336357

337-
function _get_datasets(; writable=false, auth)
358+
function _get_datasets(auth::Authentication; writable=false)
338359
url_path = writable ? ("user", "datasets") : ("datasets",)
339360
r = _restcall(auth, :GET, url_path, nothing)
340361
r.status == 200 || _throw_invalidresponse(r; msg="Unable to fetch datasets.")

test/datasets-live.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ try
8383
JuliaHub.upload_dataset(
8484
(auth.username, blobname), joinpath(TESTDATA, "hi.txt"); create=false, update=true
8585
)
86-
datasets, _ = JuliaHub._get_datasets(; auth)
86+
datasets, _ = JuliaHub._get_datasets(auth)
8787
blob_dataset_json = only(filter(d -> d["name"] == blobname, datasets))
8888
@test length(blob_dataset_json["versions"]) == 2
8989

@@ -100,7 +100,7 @@ try
100100
@test tree_dataset.dtype == "BlobTree"
101101

102102
JuliaHub.upload_dataset(treename, TESTDATA; auth, create=false, update=true)
103-
datasets, _ = JuliaHub._get_datasets(; auth)
103+
datasets, _ = JuliaHub._get_datasets(auth)
104104
tree_dataset_json = only(filter(d -> d["name"] == treename, datasets))
105105
@test length(tree_dataset_json["versions"]) == 2
106106

test/datasets.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,29 @@ end
104104
@test ds.description == "An example dataset"
105105
@test isempty(ds.versions)
106106
end
107+
108+
MOCK_JULIAHUB_STATE[:datasets_erroneous] = ["erroneous_dataset"]
109+
err_ds_warn = (
110+
:warn,
111+
"The JuliaHub GET /datasets response contains erroneous datasets. Omitting 1 entries.",
112+
)
113+
let datasets = @test_nowarn JuliaHub.datasets()
114+
@test length(datasets) == 2
115+
end
116+
let datasets = @test_logs err_ds_warn JuliaHub.datasets(; shared=true)
117+
@test length(datasets) == 3
118+
end
119+
let ds = @test_logs err_ds_warn JuliaHub.dataset("example-dataset")
120+
@test ds isa JuliaHub.Dataset
121+
end
122+
let ds = @test_logs err_ds_warn JuliaHub.dataset("example-dataset")
123+
@test ds isa JuliaHub.Dataset
124+
@test ds.owner == "username"
125+
@test ds.name == "example-dataset"
126+
end
127+
@test_logs err_ds_warn begin
128+
@test_throws JuliaHub.InvalidRequestError JuliaHub.dataset("erroneous_dataset")
129+
end
107130
end
108131
end
109132

test/mocking.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,22 @@ function _restcall_mocked(method, url, headers, payload; query)
384384
),
385385
)
386386
end
387+
for dataset in get(MOCK_JULIAHUB_STATE, :datasets_erroneous, String[])
388+
push!(datasets,
389+
Dict(
390+
"id" => string(uuidhash(dataset)),
391+
"name" => dataset,
392+
"owner" => Dict(
393+
"username" => nothing,
394+
"type" => "User",
395+
),
396+
"type" => occursin("blobtree", dataset) ? "BlobTree" : "Blob",
397+
"visibility" => occursin("public", dataset) ? "public" : "private",
398+
versions_json(dataset)...,
399+
shared...,
400+
),
401+
)
402+
end
387403
datasets |> jsonresponse(200)
388404
elseif (method == :DELETE) && endswith(url, DATASET_REGEX)
389405
dataset = URIs.unescapeuri(match(DATASET_REGEX, url)[1])

0 commit comments

Comments
 (0)