Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ concurrency:

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ github.event_name }}
name: Julia ${{ matrix.version }} - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand Down Expand Up @@ -68,6 +68,32 @@ jobs:
with:
token: ${{ secrets.CODECOV_TOKEN }}

test-json-021:
name: Julia 1 - ubuntu-latest - JSON 0.21
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v2
with:
version: "1"
- uses: actions/cache@v4
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- name: Install dependencies with JSON 0.21
shell: julia --color=yes --project {0}
run: |
import Pkg
Pkg.add(name="JSON", version="0.21")
Pkg.instantiate()
- uses: julia-actions/julia-runtest@v1

aqua:
name: Aqua
runs-on: ubuntu-latest
Expand Down
12 changes: 6 additions & 6 deletions src/applications.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct _RegistryInfo
name::String
uuid::UUIDs.UUID

function _RegistryInfo(json::Dict; var="_RegistryInfo")
function _RegistryInfo(json::AbstractDict; var="_RegistryInfo")
id = _json_get(json, "id", Integer; var)
name = _json_get(json, "name", AbstractString; var)
uuid = _json_get(json, "uuid", UUIDs.UUID; var, parse=true)
Expand All @@ -23,7 +23,7 @@ end

function _api_registries(auth::Authentication)::Vector{_RegistryInfo}
r = _restcall(auth, :GET, "app", "packages", "registries")
json, _ = _parse_response_json(r, Dict)
json, _ = _parse_response_json(r, AbstractDict)
_json_check_success(json; var="app/packages/registries")
registries = _json_get(json, "registries", Vector; var="app/packages/registries")
# Note: this broadcast can return Any[] if `registries` is empty, hence
Expand Down Expand Up @@ -78,7 +78,7 @@ struct DefaultApp <: AbstractJuliaHubApp
_apptype::String
_json::Dict{String, Any}

function DefaultApp(json::Dict, appargs::AbstractVector)
function DefaultApp(json::AbstractDict, appargs::AbstractVector)
apptype = _json_get(json, "appType", AbstractString; var="default app")
name = _json_get(json, "name", AbstractString; var="default app")
new(name, appargs, apptype, json)
Expand Down Expand Up @@ -124,7 +124,7 @@ struct PackageApp <: AbstractJuliaHubApp
_registry::_RegistryInfo
_json::Dict{String, Any}

function PackageApp(json::Dict, registries::Vector{_RegistryInfo})
function PackageApp(json::AbstractDict, registries::Vector{_RegistryInfo})
name = _json_get(json, "name", AbstractString; var="registered app")
uuid = _json_get(json, "uuid", UUIDs.UUID; var="registered app", parse=true)
registrymap = _json_get(json, "registrymap", Vector; var="registered app")
Expand Down Expand Up @@ -180,7 +180,7 @@ struct UserApp <: AbstractJuliaHubApp
_repository::String
_json::Dict{String, Any}

function UserApp(json::Dict)
function UserApp(json::AbstractDict)
name = _json_get(json, "name", AbstractString; var="user app")
repository_url = _json_get(json, "repourl", AbstractString; var="user app")
new(name, repository_url, json)
Expand Down Expand Up @@ -258,7 +258,7 @@ end
function _api_apps_default(auth::Authentication)
r = _restcall(auth, :GET, "app", "applications", "default")
r.status == 200 || _throw_invalidresponse(r; msg="Unable to list default applications.")
return _parse_response_json(r, Dict)
return _parse_response_json(r, AbstractDict)
end

function _apps_default(auth::Authentication)
Expand Down
8 changes: 4 additions & 4 deletions src/batchimages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ end
function _api_product_image_groups(auth::Authentication)
r = _restcall(auth, :GET, "juliaruncloud", "product_image_groups"; query=[("extended", "true")])
r.status == 200 || _throw_invalidresponse(r)
return _parse_response_json(r, Dict)
return _parse_response_json(r, AbstractDict)
end

function _product_image_groups(auth::Authentication)
r_json, r_json_str = _api_product_image_groups(auth)
image_groups = _get_json(r_json, "image_groups", Dict)
image_groups = _get_json(r_json, "image_groups", AbstractDict)
# Double check that the returned JSON is correct
image_groups = map(collect(pairs(image_groups))) do (image_group, images)
if !isa(images, Vector)
Expand Down Expand Up @@ -227,7 +227,7 @@ function _group_images(images; image_group::AbstractString)
# which should be unique.
grouped_images = Dict{String, _ImageKeys}()
for image in images
if !isa(image, Dict)
if !isa(image, AbstractDict)
msg = """
Invalid JSON returned by the server: image value is not an object
image_group = $(image_group)
Expand Down Expand Up @@ -279,7 +279,7 @@ function _group_images(images; image_group::AbstractString)
sort(grouped_images; by=((display_name, keys)::Pair) -> (!keys.isdefault, display_name))
end

function _parse_image_group_entry_type(image::Dict)
function _parse_image_group_entry_type(image::AbstractDict)
image_type = _get_json(image, "type", String)
m = match(r"(base|option)-(cpu|gpu)", image_type)
if isnothing(m)
Expand Down
18 changes: 9 additions & 9 deletions src/datasets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct DatasetVersion
timestamp::TimeZones.ZonedDateTime
_blobstore_path::String

function DatasetVersion(json::Dict; owner::AbstractString, name::AbstractString)
function DatasetVersion(json::AbstractDict; owner::AbstractString, name::AbstractString)
msg = "Unable to parse dataset version info for ($owner, $name)"
version = _get_json(json, "version", Int; msg)
size = _get_json(json, "size", Int; msg)
Expand Down Expand Up @@ -158,9 +158,9 @@ Base.@kwdef struct Dataset
_json::Dict
end

function Dataset(d::Dict; expected_project::Union{UUID, Nothing}=nothing)
function Dataset(d::AbstractDict; expected_project::Union{UUID, Nothing}=nothing)
owner = _get_json(
_get_json(d, "owner", Dict),
_get_json(d, "owner", AbstractDict),
"username", String,
)
name = _get_json(d, "name", AbstractString)
Expand All @@ -169,7 +169,7 @@ function Dataset(d::Dict; expected_project::Union{UUID, Nothing}=nothing)
[DatasetVersion(json; owner, name) for json in versions_json];
by=dsv -> dsv.id,
)
_storage = let storage_json = _get_json(d, "storage", Dict)
_storage = let storage_json = _get_json(d, "storage", AbstractDict)
_DatasetStorage(;
credentials_url=_get_json(d, "credentials_url", AbstractString),
region=_get_json(storage_json, "bucket_region", AbstractString),
Expand All @@ -178,7 +178,7 @@ function Dataset(d::Dict; expected_project::Union{UUID, Nothing}=nothing)
)
end
project = if !isnothing(expected_project)
project_json = _get_json(d, "project", Dict)
project_json = _get_json(d, "project", AbstractDict)
project_json_uuid = UUIDs.UUID(
_get_json(project_json, "project_id", String)
)
Expand Down Expand Up @@ -723,7 +723,7 @@ end
function _check_dataset_upload_config(
r::_RESTResponse, expected_dtype::AbstractString; newly_created_dataset::Bool
)
upload_config, _ = _parse_response_json(r, Dict)
upload_config, _ = _parse_response_json(r, AbstractDict)
# Verify that the dtype of the remote dataset is what we expect it to be.
if upload_config["dataset_type"] != expected_dtype
if newly_created_dataset
Expand Down Expand Up @@ -899,7 +899,7 @@ storage_class =
)
end

function _write_rclone_config(io::IO, upload_config::Dict)
function _write_rclone_config(io::IO, upload_config::AbstractDict)
region = upload_config["location"]["region"]
access_key_id = upload_config["credentials"]["access_key_id"]
secret_access_key = upload_config["credentials"]["secret_access_key"]
Expand All @@ -914,7 +914,7 @@ end
function _get_dataset_credentials(auth::Authentication, dataset::Dataset)
r = @_httpcatch HTTP.get(dataset._storage.credentials_url, _authheaders(auth))
r.status == 200 || _throw_invalidresponse(r; msg="Unable get credentials for $(dataset)")
credentials, _ = _parse_response_json(r, Dict)
credentials, _ = _parse_response_json(r, AbstractDict)
return credentials
end

Expand Down Expand Up @@ -1143,7 +1143,7 @@ end

# Low-level internal function that just takes a dict of params, without caring
# if they are valid or not, and returns the raw HTTP response.
function _update_dataset(auth::Authentication, dataset_name::AbstractString, params::Dict)
function _update_dataset(auth::Authentication, dataset_name::AbstractString, params::AbstractDict)
_restcall(auth, :PATCH, ("user", "datasets", dataset_name), JSON.json(params))
end

Expand Down
8 changes: 4 additions & 4 deletions src/jobs/jobs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct JobFile
_upload_timestamp::String

function JobFile(jobname::AbstractString, jf::AbstractDict; var)
filehash = let hash = _json_get(jf, "hash", Dict; var)
filehash = let hash = _json_get(jf, "hash", AbstractDict; var)
hash_algorithm = _json_get(hash, "algorithm", Union{String, Nothing}; var)
hash_value = _json_get(hash, "value", Union{String, Nothing}; var)
if isnothing(hash_algorithm) || isnothing(hash_value)
Expand Down Expand Up @@ -374,7 +374,7 @@ function job end
function job(id::AbstractString; throw::Bool=true, auth::Authentication=__auth__())
r = _restcall(auth, :GET, "api", "rest", "jobs", id; hasura=true)
r.status == 200 || _throw_invalidresponse(r)
job, json = _parse_response_json(r, Dict)
job, json = _parse_response_json(r, AbstractDict)
details = get(job, "details") do
Base.throw(JuliaHubError("Invalid JSON returned by the server:\n$(json)"))
end
Expand Down Expand Up @@ -559,7 +559,7 @@ kill_job(job::Job; auth::Authentication=__auth__()) = kill_job(job.id; auth)
function kill_job(jobname::AbstractString; auth::Authentication=__auth__())
r = _restcall(auth, :GET, "juliaruncloud", "kill_job"; query=(; jobname=string(jobname)))
if r.status == 200
response, json = _parse_response_json(r, Dict)
response, json = _parse_response_json(r, AbstractDict)
# response_json["status"] might not be a Bool
if get(response, "status", false) != true
throw(JuliaHubError("Unexpected JSON returned by the server\n$(json)"))
Expand Down Expand Up @@ -601,7 +601,7 @@ function extend_job(jobname::AbstractString, extension::Limit; auth::Authenticat
)
r = _restcall(auth, :POST, ("juliaruncloud", "extend_job_time_limit"), payload)
if r.status == 200
response, json = _parse_response_json(r, Dict)
response, json = _parse_response_json(r, AbstractDict)
success = get(response, "success", nothing)
message = get(response, "message", "")
if success === true
Expand Down
22 changes: 11 additions & 11 deletions src/jobs/logging-kafka.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
struct _KafkaLogging <: _JobLoggingAPIVersion end

function JobLogMessage(::_KafkaLogging, json::Dict)
function JobLogMessage(::_KafkaLogging, json::AbstractDict)
offset = _get_json(json, "offset", Int)
# The timestamps in Kafka logs are in milliseconds
value = _get_json(json, "value", Dict)
value = _get_json(json, "value", AbstractDict)
timestamp = _ms_utc2localtz(_get_json(value, "timestamp", Int))
log = _get_json(value, "log", Dict)
log = _get_json(value, "log", AbstractDict)
message = _get_json(log, "message", String)
metadata = _get_json_or(log, "metadata", Dict, Dict{String, Any}())
keywords = _get_json_or(log, "keywords", Dict, Dict{String, Any}())
metadata::Dict = _get_json_or(log, "metadata", AbstractDict, Dict{String, Any}())
keywords::Dict = _get_json_or(log, "keywords", AbstractDict, Dict{String, Any}())
stream = _get_json_or(log, "stream", String, nothing)
JobLogMessage(;
_offset=offset, timestamp, message, _metadata=metadata, _keywords=keywords,
Expand Down Expand Up @@ -412,12 +412,12 @@ function _get_logs_kafka_parsed(
@debug "_get_logs_kafka_parsed($jobname): start REST call" _taskstamp() offset consumer_id timeout
r = _get_job_logs_kafka_restcall(auth, jobname; offset, consumer_id, timeout)
r.status == 200 || _throw_invalidresponse(r)
json, json_str = _parse_response_json(r, Dict)
json, json_str = _parse_response_json(r, AbstractDict)
consumer_id = _get_json(json, "consumer_id", Int)
# If the Kafka endpoints want to return an empty list of log messages, it returns it
# as an empty object (i.e. "logs": {}), rather than an empty array.
logs = _get_json(json, "logs", Union{Dict, Vector})
logmessages, jobdone = if isa(logs, Dict)
logs = _get_json(json, "logs", Union{AbstractDict, Vector})
logmessages, jobdone = if isa(logs, AbstractDict)
if !isempty(logs)
throw(JuliaHubError("Non-empty dictionary for logs\n$(json_str)"))
end
Expand All @@ -434,7 +434,7 @@ function _get_logs_kafka_parsed(
# also occur in a random place.. or there may be multiple meta messages.
bottom_message = false
for (i, log) in enumerate(logs)
if !isa(log, Dict)
if !isa(log, AbstractDict)
@error "Invalid log message type $(typeof(log)) (at $i / $(length(logs)); omitting)" i log
continue
end
Expand Down Expand Up @@ -477,8 +477,8 @@ function _get_logs_kafka_parsed(
return (; consumer_id, logs=logmessages, isdone=jobdone)
end

function _kafka_is_last_message(json::Dict)
value = _get_json_or(json, "value", Dict, Dict())
function _kafka_is_last_message(json::AbstractDict)
value::Dict = _get_json_or(json, "value", AbstractDict, Dict())
return get(value, "meta", nothing) == "bottom"
end

Expand Down
10 changes: 5 additions & 5 deletions src/jobs/logging-legacy.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
struct _LegacyLogging <: _JobLoggingAPIVersion end

function JobLogMessage(::_LegacyLogging, json::Dict, offset::Integer)
function JobLogMessage(::_LegacyLogging, json::AbstractDict, offset::Integer)
# The .message property _should_ always be present in the log messages,
# but there are a few versions out there where it's sometimes omitted due
# to a backend bug. So we default to an empty string in those cases.
message = _get_json_or(json, "message", String, "")
keywords = _get_json_or(json, "keywords", Dict, Dict{String, Any}())
metadata = _get_json_or(json, "metadata", Dict, Dict{String, Any}())
keywords::Dict = _get_json_or(json, "keywords", AbstractDict, Dict{String, Any}())
metadata::Dict = _get_json_or(json, "metadata", AbstractDict, Dict{String, Any}())
timestamp = if haskey(json, "timestamp")
# Apparently timestamps are sometimes strings, sometimes integers..
timestamp = _get_json(json, "timestamp", Union{String, Integer})
Expand Down Expand Up @@ -273,7 +273,7 @@ end
# The log messages (may) have special _meta messages at the start and at the end.
# These have a `"_meta": true` field, and should have either `"end": "top"` (if first)
# or `"end": "bottom"` (if last message).
function _log_legacy_is_meta(log::Dict, s::AbstractString)
function _log_legacy_is_meta(log::AbstractDict, s::AbstractString)
haskey(log, "_meta") || return false
if log["_meta"] !== true
throw(JuliaHubError("""
Expand Down Expand Up @@ -529,7 +529,7 @@ function _job_logs_legacy_start_streaming!(auth::Authentication, buffer::_Legacy
end
# If the message wasn't empty, we assume that it is a valid JSON blob containing
# a log message.
msg, _ = _parse_response_json(msg, Dict)
msg, _ = _parse_response_json(msg, AbstractDict)
if _log_legacy_is_meta(msg, "top")
@error "Unexpected `top` meta message streamed" msg
return nothing
Expand Down
18 changes: 10 additions & 8 deletions src/jobsubmission.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct _JobSubmission1
# User code arguments
appType::Union{AbstractString, Nothing}=nothing,
appArgs::Union{Dict, Nothing}=nothing,
args::Dict,
args::AbstractDict,
projectid::Union{AbstractString, Nothing},
customcode::Bool, usercode::Union{AbstractString, Nothing}=nothing,
projecttoml::Union{AbstractString, Nothing}=nothing,
Expand Down Expand Up @@ -158,7 +158,7 @@ end

_string_or_nothing(x) = isnothing(x) ? nothing : string(x)

function _check_key(d::Dict, key, ::Type{T}; varname) where {T}
function _check_key(d::AbstractDict, key, ::Type{T}; varname) where {T}
haskey(d, key) || throw(ArgumentError("Dictionary `$varname` is missing key `$key`."))
isa(d[key], T) ||
throw(ArgumentError("`$varname[\"$key\"]` is not `<: $T` (got `$(typeof(d[key]))`)."))
Expand All @@ -185,7 +185,7 @@ function _submit_job(auth::Authentication, j::_JobSubmission1)
"""
r = _restcall(auth, :POST, ("juliaruncloud", "submit_job"), HTTP.Form(params))
if r.status == 200
r_json, _ = _parse_response_json(r, Dict)
r_json, _ = _parse_response_json(r, AbstractDict)
haskey(r_json, "success") && r_json["success"] || throw(JuliaHubError(
"""
Invalid response JSON from JuliaHub:
Expand Down Expand Up @@ -868,10 +868,10 @@ function _get_appbundle_upload_url(auth::Authentication, appbundle_tar_path::Abs
)
r = _restcall(auth, :GET, "jobs", "appbundle_upload_url"; query=appbundle_params)
r.status == 200 || _throw_invalidresponse(r; msg="Unable to upload appbundle to JuliaHub.")
r_json, _ = _parse_response_json(r, Dict)
r_json, _ = _parse_response_json(r, AbstractDict)
_get_json(r_json, "success", Bool) ||
_throw_invalidresponse(r; msg="Unable to upload appbundle to JuliaHub.")
message = _get_json(r_json, "message", Dict)
message = _get_json(r_json, "message", AbstractDict)
upload_url = _get_json(message, "upload_url", AbstractString)
return upload_url, appbundle_params
end
Expand Down Expand Up @@ -914,9 +914,11 @@ struct PackageJob <: AbstractJobConfig
args::Dict
sysimage::Bool

PackageJob(app::PackageApp; args::Dict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
PackageJob(
app::PackageApp; args::AbstractDict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage
) =
new(app, app.name, app._registry.name, string(app._uuid), args, sysimage)
PackageJob(app::UserApp; args::Dict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
PackageJob(app::UserApp; args::AbstractDict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
new(app, app.name, nothing, app._repository, args, sysimage)
end

Expand All @@ -926,7 +928,7 @@ function _check_packagebundler_dir(bundlepath::AbstractString)
return nothing
end

function _check_job_args(args::Dict)
function _check_job_args(args::AbstractDict)
for k in keys(args)
isa(k, AbstractString) ||
throw(
Expand Down
Loading