Skip to content
Draft
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
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ keywords = ["Swagger", "OpenAPI", "REST"]
license = "MIT"
desc = "OpenAPI server and client helper for Julia"
authors = ["JuliaHub Inc."]
version = "0.2.1"
version = "0.2.2"

[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Expand All @@ -22,8 +22,8 @@ p7zip_jll = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
[compat]
Downloads = "1"
HTTP = "1"
JSON = "0.20, 0.21"
LibCURL = "0.6"
JSON = "0.20, 0.21, 1"
LibCURL = "0.6, 1"
MIMEs = "0.1, 1"
MbedTLS = "0.6.8, 0.7, 1"
TimeZones = "1"
Expand Down
9 changes: 9 additions & 0 deletions src/OpenAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ using p7zip_jll
import Base: getindex, keys, length, iterate, hasproperty
import JSON: lower


const _JSON_PARSE_ISROOT_SUPPORTED = try; JSON.parse("1 "; isroot=false); true; catch; false; end

if _JSON_PARSE_ISROOT_SUPPORTED
_json_parse(io_or_str) = JSON.parse(io_or_str; isroot=false)
else
_json_parse(io_or_str) = JSON.parse(io_or_str)
end

include("commontypes.jl")
include("datetime.jl")
include("val.jl")
Expand Down
12 changes: 6 additions & 6 deletions src/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ using MIMEs

import Base: convert, show, summary, getproperty, setproperty!, iterate
import ..OpenAPI: APIModel, UnionAPIModel, OneOfAPIModel, AnyOfAPIModel, APIClientImpl, OpenAPIException, InvocationException, to_json, from_json, validate_property, property_type
import ..OpenAPI: str2zoneddatetime, str2datetime, str2date
import ..OpenAPI: str2zoneddatetime, str2datetime, str2date, _json_parse

include("client/clienttypes.jl")
include("client/chunk_readers.jl")
Expand Down Expand Up @@ -159,8 +159,8 @@ response(::Type{DateTime}, data) = str2datetime(data)
response(::Type{Date}, data) = str2date(data)

response(::Type{T}, data) where {T} = convert(T, data)
response(::Type{T}, data::Dict{String,Any}) where {T} = from_json(T, data)::T
response(::Type{T}, data::Dict{String,Any}) where {T<:Dict} = convert(T, data)
response(::Type{T}, data::AbstractDict{String,Any}) where {T} = from_json(T, data)::T
response(::Type{T}, data::AbstractDict{String,Any}) where {T<:Dict} = convert(T, Dict{String,Any}(data))
response(::Type{Vector{T}}, data::Vector{V}) where {T,V} = T[response(T, v) for v in data]

noop_pre_request_hook(ctx::Ctx) = ctx
Expand Down Expand Up @@ -299,14 +299,14 @@ end

Base.hasproperty(o::T, name::Symbol) where {T<:APIModel} = ((name in propertynames(o)) && (getproperty(o, name) !== nothing))

convert(::Type{T}, json::Dict{String,Any}) where {T<:APIModel} = from_json(T, json)
convert(::Type{T}, json::AbstractDict{String,Any}) where {T<:APIModel} = from_json(T, json)
convert(::Type{T}, v::Nothing) where {T<:APIModel} = T()
convert(::Type{T}, v::T) where {T<:OneOfAPIModel} = v
convert(::Type{T}, json::Dict{String,Any}) where {T<:OneOfAPIModel} = from_json(T, json)
convert(::Type{T}, json::AbstractDict{String,Any}) where {T<:OneOfAPIModel} = from_json(T, json)
convert(::Type{T}, v) where {T<:OneOfAPIModel} = T(v)
convert(::Type{T}, v::String) where {T<:OneOfAPIModel} = T(v)
convert(::Type{T}, v::T) where {T<:AnyOfAPIModel} = v
convert(::Type{T}, json::Dict{String,Any}) where {T<:AnyOfAPIModel} = from_json(T, json)
convert(::Type{T}, json::AbstractDict{String,Any}) where {T<:AnyOfAPIModel} = from_json(T, json)
convert(::Type{T}, v) where {T<:AnyOfAPIModel} = T(v)
convert(::Type{T}, v::String) where {T<:AnyOfAPIModel} = T(v)

Expand Down
69 changes: 68 additions & 1 deletion src/client/chunk_readers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,71 @@ struct JSONChunkReader <: AbstractChunkReader
buffered_input::Base.BufferStream
end

function _read_json_chunk(io::IO)
out = IOBuffer()
first_byte = peek(io, UInt8)

if first_byte == UInt8('{') || first_byte == UInt8('[')
close_byte = first_byte == UInt8('{') ? UInt8('}') : UInt8(']')
depth = 0
in_string = false
escaped = false

while !eof(io)
byte = read(io, UInt8)
write(out, byte)

if escaped
escaped = false
continue
end

if in_string
if byte == UInt8('\\')
escaped = true
elseif byte == UInt8('"')
in_string = false
end
else
if byte == UInt8('"')
in_string = true
elseif byte == first_byte
depth += 1
elseif byte == close_byte
depth -= 1
depth == 0 && break
end
end
end
elseif first_byte == UInt8('"')
escaped = false
read(io, UInt8) # consume opening quote
write(out, UInt8('"'))
while !eof(io)
byte = read(io, UInt8)
write(out, byte)
if escaped
escaped = false
elseif byte == UInt8('\\')
escaped = true
elseif byte == UInt8('"')
break
end
end
else
# number / true / false / null: read until delimiter
while !eof(io)
byte = peek(io, UInt8)
if isspace(Char(byte)) || byte == UInt8(',') || byte == UInt8(']') || byte == UInt8('}')
break
end
write(out, read(io, UInt8))
end
end

take!(out)
end

function Base.iterate(iter::JSONChunkReader, _state=nothing)
if eof(iter.buffered_input)
return nothing
Expand All @@ -34,7 +99,9 @@ function Base.iterate(iter::JSONChunkReader, _state=nothing)
end
end
eof(iter.buffered_input) && return nothing
valid_json = JSON.parse(iter.buffered_input)
chunk_bytes = _read_json_chunk(iter.buffered_input)
isempty(chunk_bytes) && return nothing
valid_json = _json_parse(String(chunk_bytes))
bytes = convert(Vector{UInt8}, codeunits(JSON.json(valid_json)))
return (bytes, iter)
end
Expand Down
22 changes: 11 additions & 11 deletions src/json.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ end

is_deep_explode(sctx::StyleCtx) = sctx.name == "deepObject" && sctx.is_explode

function deep_object_to_array(src::Dict)
function deep_object_to_array(src::AbstractDict)
keys_are_int = all(key -> occursin(r"^\d+$", key), keys(src))
if keys_are_int
sorted_keys = sort(collect(keys(src)), by=x->parse(Int, x))
Expand All @@ -58,14 +58,14 @@ end

to_json(o) = JSON.json(o)

from_json(::Type{Union{Nothing,T}}, json::Dict{String,Any}; stylectx=nothing) where {T} = from_json(T, json; stylectx)
from_json(::Type{T}, json::Dict{String,Any}; stylectx=nothing) where {T} = from_json(T(), json; stylectx)
from_json(::Type{T}, json::Dict{String,Any}; stylectx=nothing) where {T <: Dict} = convert(T, json)
from_json(::Type{T}, j::Dict{String,Any}; stylectx=nothing) where {T <: String} = to_json(j)
from_json(::Type{Any}, j::Dict{String,Any}; stylectx=nothing) = j
from_json(::Type{Union{Nothing,T}}, json::AbstractDict{String,Any}; stylectx=nothing) where {T} = from_json(T, json; stylectx)
from_json(::Type{T}, json::AbstractDict{String,Any}; stylectx=nothing) where {T} = from_json(T(), json; stylectx)
from_json(::Type{T}, json::AbstractDict{String,Any}; stylectx=nothing) where {T <: Dict} = convert(T, Dict{String,Any}(json))
from_json(::Type{T}, j::AbstractDict{String,Any}; stylectx=nothing) where {T <: String} = to_json(j)
from_json(::Type{Any}, j::AbstractDict{String,Any}; stylectx=nothing) = j
from_json(::Type{Vector{T}}, j::Vector{Any}; stylectx=nothing) where {T} = j

function from_json(::Type{Vector{T}}, json::Dict{String, Any}; stylectx=nothing) where {T}
function from_json(::Type{Vector{T}}, json::AbstractDict{String, Any}; stylectx=nothing) where {T}
if !isnothing(stylectx) && is_deep_explode(stylectx)
cvt = deep_object_to_array(json)
if isa(cvt, Vector)
Expand All @@ -78,7 +78,7 @@ function from_json(::Type{Vector{T}}, json::Dict{String, Any}; stylectx=nothing)
end
end

function from_json(o::T, json::Dict{String,Any};stylectx=nothing) where {T <: UnionAPIModel}
function from_json(o::T, json::AbstractDict{String,Any};stylectx=nothing) where {T <: UnionAPIModel}
return from_json(o, :value, json;stylectx)
end

Expand All @@ -88,16 +88,16 @@ function from_json(o::T, val::Union{String,Real};stylectx=nothing) where {T <: U
return o
end

function from_json(o::T, json::Dict{String,Any};stylectx=nothing) where {T <: APIModel}
function from_json(o::T, json::AbstractDict{String,Any};stylectx=nothing) where {T <: APIModel}
jsonkeys = [Symbol(k) for k in keys(json)]
for name in intersect(propertynames(o), jsonkeys)
from_json(o, name, json[String(name)];stylectx)
end
return o
end

function from_json(o::T, name::Symbol, json::Dict{String,Any};stylectx=nothing) where {T <: APIModel}
ftype = (T <: UnionAPIModel) ? property_type(T, name, json) : property_type(T, name)
function from_json(o::T, name::Symbol, json::AbstractDict{String,Any};stylectx=nothing) where {T <: APIModel}
ftype = (T <: UnionAPIModel) ? property_type(T, name, Dict{String,Any}(json)) : property_type(T, name)
fval = from_json(ftype, json; stylectx)
setfield!(o, name, convert(ftype, fval))
return o
Expand Down
4 changes: 2 additions & 2 deletions src/server.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ to_param_type(::Type{Vector{UInt8}}, val::String; stylectx=nothing) = convert(Ve
to_param_type(::Type{Vector{T}}, val::Vector{T}, _collection_format::Union{String,Nothing}; stylectx=nothing) where {T} = val
to_param_type(::Type{Vector{T}}, json::Vector{Any}; stylectx=nothing) where {T} = [to_param_type(T, x; stylectx) for x in json]

function to_param_type(::Type{Vector{T}}, json::Dict{String, Any}; stylectx=nothing) where {T}
function to_param_type(::Type{Vector{T}}, json::AbstractDict{String, Any}; stylectx=nothing) where {T}
if !isnothing(stylectx) && is_deep_explode(stylectx)
cvt = deep_object_to_array(json)
if isa(cvt, Vector)
Expand All @@ -103,7 +103,7 @@ function to_param_type(::Type{T}, strval::String; stylectx=nothing) where {T <:
from_json(T, JSON.parse(strval); stylectx)
end

function to_param_type(::Type{T}, json::Dict{String,Any}; stylectx=nothing) where {T <: APIModel}
function to_param_type(::Type{T}, json::AbstractDict{String,Any}; stylectx=nothing) where {T <: APIModel}
from_json(T, json; stylectx)
end

Expand Down
Loading