11const _DOCS_nondynamic_datasets_object_warning = """
22!!! warning "Non-dynamic dataset objects"
33
4- [`Dataset`](@ref) and [`ProjectDataset`](@ref) objects represents the dataset metadata when the
5- Julia object was created (e.g. with [`dataset`](@ref)), and are not automatically kept up to date.
6- To refresh the dataset metadata, you can pass the existing [`Dataset`](@ref) to [`JuliaHub.dataset`](@ref),
7- or [`ProjectDataset `](@ref) to [`project_dataset`](@ref).
4+ [`Dataset`](@ref) objects represents the dataset metadata when the Julia object was created
5+ (e.g. with [`dataset`](@ref)), and are not automatically kept up to date.
6+ To refresh the dataset metadata, you can pass an existing [`Dataset`](@ref) object
7+ to [`JuliaHub.dataset `](@ref) or [`project_dataset`](@ref).
88"""
99
1010Base. @kwdef struct _DatasetStorage
@@ -71,6 +71,25 @@ function Base.show(io::IO, ::MIME"text/plain", dsv::DatasetVersion)
7171 print (io, " \n size: " , dsv. size, " bytes" )
7272end
7373
74+ """
75+ struct DatasetProjectLink
76+
77+ Holds the project-dataset link metadata for datasets that were accessed via a project
78+ (e.g. when using [`project_datasets`](@ref)).
79+
80+ - `.uuid :: UUID`: the UUID of the project
81+ - `.is_writable :: Bool`: whether the user has write access to the dataset via the
82+ this project
83+
84+ See also: [`project_dataset`](@ref), [`project_datasets`](@ref), [`upload_project_dataset`](@ref).
85+
86+ $(_DOCS_no_constructors_admonition)
87+ """
88+ struct DatasetProjectLink
89+ uuid:: UUIDs.UUID
90+ is_writable:: Bool
91+ end
92+
7493"""
7594 struct Dataset
7695
@@ -87,6 +106,13 @@ public API:
87106- Fields to access user-provided dataset metadata:
88107 - `description :: String`: dataset description
89108 - `tags :: Vector{String}`: a list of tags
109+ - If the dataset was accessed via a project (e.g. via [`project_datasets`](@ref)), `.project` will
110+ contain project metadata (see also: [`DatasetProjectLink`](@ref)). Otherwise this field is `nothing`.
111+ - `project.uuid`: the UUID of the project
112+ - `project.is_writable`: whether the user has write access to the dataset via the
113+ this project
114+ Note that two `Dataset` objects are considered to be equal (i.e. `==`) regardless of the `.project`
115+ value -- it references the same dataset regardless of the project it was accessed in.
90116
91117!!! note "Canonical fully qualified dataset name"
92118
@@ -108,6 +134,7 @@ Base.@kwdef struct Dataset
108134 # User-set metadata
109135 description:: String
110136 tags:: Vector{String}
137+ project:: Union{DatasetProjectLink, Nothing}
111138 # Additional metadata, but not part of public API
112139 _last_modified:: Union{Nothing, TimeZones.ZonedDateTime}
113140 _downloadURL:: String
@@ -117,18 +144,38 @@ Base.@kwdef struct Dataset
117144 _json:: Dict
118145end
119146
120- function Dataset (d:: Dict )
147+ function Dataset (d:: Dict ; expected_project :: Union{UUIDs.UUID, Nothing} = nothing )
121148 owner = d[" owner" ][" username" ]
122149 name = d[" name" ]
123150 versions_json = _get_json_or (d, " versions" , Vector, [])
124151 versions = sort ([DatasetVersion (json; owner, name) for json in versions_json]; by= dsv -> dsv. id)
152+ project = if ! isnothing (expected_project)
153+ project_json = _get_json (d, " project" , Dict)
154+ project_json_uuid = UUIDs. UUID (
155+ _get_json (project_json, " project_id" , String; msg= " .project" )
156+ )
157+ if project_json_uuid != expected_project
158+ msg = " Project UUID mismatch in dataset response: $(project_json_uuid) , requested $(project) "
159+ throw (JuliaHubError (msg))
160+ end
161+ is_writable = _get_json (
162+ project_json,
163+ " is_writable" ,
164+ Bool;
165+ msg= " Unable to parse .project in /datasets?project response" ,
166+ )
167+ DatasetProjectLink (project_json_uuid, is_writable)
168+ else
169+ nothing
170+ end
125171 Dataset (;
126172 uuid= UUIDs. UUID (d[" id" ]),
127173 name, owner, versions,
128174 dtype= d[" type" ],
129175 description= d[" description" ],
130176 size= d[" size" ],
131177 tags= d[" tags" ],
178+ project= project,
132179 _downloadURL= d[" downloadURL" ],
133180 _last_modified= _nothing_or (d[" lastModified" ]) do last_modified
134181 datetime_utc = Dates. DateTime (
@@ -151,7 +198,12 @@ function Base.propertynames(::Dataset)
151198end
152199
153200function Base. show (io:: IO , d:: Dataset )
154- print (io, " JuliaHub.dataset((\" " , d. owner, " \" , \" " , d. name, " \" ))" )
201+ dsref = string (" (\" " , d. owner, " \" , \" " , d. name, " \" )" )
202+ if isnothing (d. project)
203+ print (io, " JuliaHub.dataset(" , dsref, " )" )
204+ else
205+ print (io, " JuliaHub.project_dataset(" , dsref, " ; project=" , d. project. uuid, " )" )
206+ end
155207end
156208
157209function Base. show (io:: IO , :: MIME"text/plain" , d:: Dataset )
@@ -162,6 +214,13 @@ function Base.show(io::IO, ::MIME"text/plain", d::Dataset)
162214 print (io, " \n versions: " , length (d. versions))
163215 print (io, " \n size: " , d. size, " bytes" )
164216 isempty (d. tags) || print (io, " \n tags: " , join (d. tags, " , " ))
217+ if ! isnothing (d. project)
218+ print (
219+ io,
220+ " \n project: " , d. project. uuid, " " ,
221+ d. project. is_writable ? " (writable)" : " (not writable)" ,
222+ )
223+ end
165224end
166225
167226function Base.:(== )(d1:: Dataset , d2:: Dataset )
@@ -331,7 +390,9 @@ function datasets(
331390end
332391
333392function _parse_dataset_list (
334- datasets:: Vector ; username:: Union{AbstractString, Nothing} = nothing
393+ datasets:: Vector ;
394+ username:: Union{AbstractString, Nothing} = nothing ,
395+ expected_project:: Union{UUIDs.UUID, Nothing} = nothing ,
335396):: Vector{Dataset}
336397 # It might happen that some of the elements of the `datasets` array can not be parsed for some reason,
337398 # and the Dataset() constructor will throw. Rather than having `datasets` throw an error (as we would
@@ -348,8 +409,14 @@ function _parse_dataset_list(
348409 if ! isnothing (username) && (dataset[" owner" ][" username" ] != username)
349410 return nothing
350411 end
351- return Dataset (dataset)
412+ return Dataset (dataset; expected_project )
352413 catch e
414+ # If we fail to parse the server response for a dataset, we should always get a JuliaHubError.
415+ # Other errors types might indicate e.g. code errors, so we don't want to swallow those
416+ # here, and instead throw immediately.
417+ if ! isa (e, JuliaHubError)
418+ rethrow ()
419+ end
353420 @debug " Invalid dataset in GET /datasets response" dataset exception = (
354421 e, catch_backtrace ()
355422 )
0 commit comments