Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for query parameter serialization style "deepObject" #78

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

vdayanand
Copy link
Member

@vdayanand vdayanand commented Jul 4, 2024

Introduction

The deepObject style is used for complex object serialization in query parameters for OpenAPI 3.0 (https://swagger.io/docs/specification/serialization). This style enables the representation of nested objects particularly suitable for APIs that need to handle hierarchical data structures.

Description

The deepObject format represents nested objects and arrays by encoding each level of nesting as a separate query parameter, using square brackets to denote the hierarchy. Each parameter consists of a base key followed by one or more nested keys.
Key Elements

  • Nested Objects: Represented by square brackets, where each bracket level denotes a deeper level of the object hierarchy.
  • Arrays: Represented by numeric keys inside square brackets, indicating the index of each element in the array.

Note: OpenAPI spec doesn't define array de serialization for deepObject. So basically we have extended it for arrays

Examples
  1. a[b][c][d]=value1&a[b][c][e]=value2&a[f]=value3 would be intermediately deserialized as
{
  "a": {
    "b": {
      "c": {
        "d": "value1",
        "e": "value2"
      }
    },
    "f": "value3"
  }
}
  1. a[b][0]=value1&&a[b][1]=value2 would be intermediately deserialized as:
{
  "a": {
    "b": [
      "value1",
      "value2"
    ]
  }
}

@vdayanand vdayanand changed the title feat: support for query parameter deserialization of type "deepObject" feat: support for query parameter deserialization style "deepObject" Jul 4, 2024
Copy link
Member

@tanmaykm tanmaykm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good except for some minor comments!
Will be awesome to have the client side implementation too and have an end-to-end test.

key = replace(key, r"\[" => '\0')
key = replace(key, r"\]" => "")
parts = split(key, '\0')
keylist = parts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should rely on \0. Could the lines above be replaced with: keylist = replace.(split(key, "["), "]"=>"")?

if !haskey(current, part)
current[part] = Dict{String, Any}()
end
current = current[part]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use get! instead?

current = get!(current, part) do
    Dict{String,Any}()
end

return deserialized_dict
end

function deserialize_deep_object(qp::Dict)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we rename deserialize_deep_object to deep_dict_repr or something? It might be confusing for someone reading the code that it returns a dict and not a struct instance.

to_param_type(T, json; stylectx)
end
else
to_param_type(T, json; stylectx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_param_type(::Type{Vector{T}}, should be returning a Vector{T}, no?
Should we be throwing an exception instead?

@vdayanand
Copy link
Member Author

Added client support as well. I will add end to end tests

@vdayanand vdayanand changed the title feat: support for query parameter deserialization style "deepObject" feat: support for query parameter serialization style "deepObject" Jul 5, 2024
deep_explode = style == "deepObject" && is_explode
if deep_explode
merge!(params, serialize_to_query_dict(Dict(name=>value)))
return params
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_param should return nothing.

@@ -292,7 +292,12 @@ function set_header_content_type(ctx::Ctx, ctypes::Vector{String})
end

set_param(params::Dict{String,String}, name::String, value::Nothing; collection_format=",") = nothing
function set_param(params::Dict{String,String}, name::String, value; collection_format=",")
function set_param(params::Dict{String,String}, name::String, value; collection_format=",", style="form", is_explode=false)
deep_explode = style == "deepObject" && is_explode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should not be checking is_explode if style == "deepObject"? Because as per spec, deepObject seems to imply explode=true always.

@@ -828,4 +833,21 @@ function extract_filename(resp::Downloads.Response)::String
return string("response", extension_from_mime(MIME(content_type_str)))
end

function serialize_to_query_dict(dict::Dict, parent_key::String = "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename method with deepobject_ prefix?


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

function convert_dict_to_array(src::Dict)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename method with deepobject_ prefix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants