Skip to content

Commit 03737f9

Browse files
committed
add more validations for format and multipleOf
- add validations for `multipleOf` - add validations for some `format` specifiers for string, number and integer types - convert to string when numbers are assigned to string types in models, because some languages may send data with numbers when the format specifier for string type allows it
1 parent ba8d54f commit 03737f9

39 files changed

+260
-23
lines changed

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ keywords = ["Swagger", "OpenAPI", "REST"]
44
license = "MIT"
55
desc = "OpenAPI server and client helper for Julia"
66
authors = ["Tanmay Mohapatra <[email protected]>"]
7-
version = "0.1.2"
7+
version = "0.1.3"
88

99
[deps]
10+
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
1011
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1112
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
1213
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,49 @@ Following validations are incorporated into models:
5454
- minimum length: must be a string value of length greater than or equal to a specified value
5555
- maximum item count: must be a list value with number of items less than or equal to a specified value
5656
- minimum item count: must be a list value with number of items greater than or equal to a specified value
57+
- unique items: items must be unique
58+
- maximum properties count: number of properties must be less than or equal to a specified value
59+
- minimum properties count: number of properties must be greater than or equal to a specified value
5760
- pattern: must match the specified regex pattern
61+
- format: must match the specified format specifier (see subsection below for details)
5862
- enum: value must be from a list of allowed values
63+
- multiple of: must be a multiple of a specified value
5964

6065
Validations are imposed in the constructor and `setproperty!` methods of models.
6166

67+
#### Validations for format specifiers
68+
69+
String, number and integer data types can have an optional format modifier that serves as a hint at the contents and format of the string. Validations for the following OpenAPI defined formats are built in:
70+
71+
| Data Type | Format | Description |
72+
|-----------|-----------|-------------|
73+
| number | float | Floating-point numbers. |
74+
| number | double | Floating-point numbers with double precision. |
75+
| integer | int32 | Signed 32-bit integers (commonly used integer type). |
76+
| integer | int64 | Signed 64-bit integers (long type). |
77+
| string | date | full-date notation as defined by RFC 3339, section 5.6, for example, 2017-07-21 |
78+
| string | date-time | the date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z |
79+
| string | byte | base64-encoded characters, for example, U3dhZ2dlciByb2Nrcw== |
80+
81+
Validations for custom formats can be plugged in by overloading the `OpenAPI.val_format` method.
82+
83+
E.g.:
84+
85+
```julia
86+
# add a new validation named `custom` for the number type
87+
function OpenAPI.val_format(val::AbstractFloat, ::Val{:custom})
88+
return true # do some validations and return result
89+
end
90+
# add a new validation named `custom` for the integer type
91+
function OpenAPI.val_format(val::Integer, ::Val{:custom})
92+
return true # do some validations and return result
93+
end
94+
# add a new validation named `custom` for the string type
95+
function OpenAPI.val_format(val::AbstractString, ::Val{:custom})
96+
return true # do some validations and return result
97+
end
98+
```
99+
62100
### Client APIs
63101

64102
Each client API set is generated into a file named `api_<apiname>.jl`. It is represented as a `struct` and the APIs under it are generated as methods. An API set can be constructed by providing the OpenAPI client instance that it can use for communication.

src/OpenAPI.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module OpenAPI
22

3-
using HTTP, JSON, URIs, Dates, TimeZones
3+
using HTTP, JSON, URIs, Dates, TimeZones, Base64
44

55
import Base: getindex, keys, length, iterate
66
import JSON: lower

src/json.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ function from_json(o::T, name::Symbol, v) where {T <: APIModel}
6161
setfield!(o, name, str2datetime(v))
6262
elseif Date <: ftype
6363
setfield!(o, name, str2date(v))
64+
elseif String <: ftype && isa(v, Real)
65+
# string numbers can have format specifiers that allow numbers, ensure they are converted to strings
66+
setfield!(o, name, string(v))
6467
else
6568
setfield!(o, name, convert(ftype, v))
6669
end
@@ -83,6 +86,10 @@ function from_json(o::T, name::Symbol, v::Vector) where {T <: APIModel}
8386
else
8487
if (vtype <: Vector) && (veltype <: OpenAPI.UnionAPIModel)
8588
setfield!(o, name, map(veltype, v))
89+
elseif (vtype <: Vector) && (veltype <: String)
90+
# ensure that elements are converted to String
91+
# convert is to do the translation to Union{Nothing,String} when necessary
92+
setfield!(o, name, convert(ftype, map(string, v)))
8693
elseif ftype <: OpenAPI.UnionAPIModel
8794
setfield!(o, name, ftype(v))
8895
else

src/val.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ end
2222
function val_pattern(val::AbstractString, pattern::Regex)
2323
return !isnothing(match(pattern, val))
2424
end
25+
val_format(val, format) = true # accept any unhandled format
26+
val_format(val::Union{AbstractString,Integer,AbstractFloat}, format::AbstractString) = val_format(val, Val(Symbol(format)))
27+
val_format(val::AbstractString, ::Val{:date}) = str2date(val) isa Date
28+
val_format(val::AbstractString, ::Val{Symbol("date-time")}) = str2datetime(val) isa DateTime
29+
val_format(val::AbstractString, ::Val{:byte}) = try
30+
base64decode(val)
31+
true
32+
catch
33+
false
34+
end
35+
val_format(val::Integer, ::Val{:int32}) = (typemin(Int32) <= val <= typemax(Int32))
36+
val_format(val::Integer, ::Val{:int64}) = (typemin(Int64) <= val <= typemax(Int64))
37+
val_format(val::AbstractFloat, ::Val{:float}) = (typemin(Float32) <= Float32(val) <= typemax(Float32))
38+
val_format(val::AbstractFloat, ::Val{:double}) = (typemin(Float64) <= Float64(val) <= typemax(Float64))
39+
40+
function val_multiple_of(val::Real, multiple_of::Real)
41+
return isinteger(val / multiple_of)
42+
end
2543

2644
const MSG_INVALID_API_PARAM = Dict{Symbol,Function}([
2745
:maximum => (val,excl)->string("must be a value less than ", excl ? "or equal to " : "", val),
@@ -35,6 +53,8 @@ const MSG_INVALID_API_PARAM = Dict{Symbol,Function}([
3553
:minProperties => (val)->string("number of properties must be greater than or equal to ", val),
3654
:enum => (lst)->string("value is not from the allowed values ", lst),
3755
:pattern => (val)->string("value does not match required pattern"),
56+
:format => (val)->string("value does not match required format"),
57+
:multipleOf => (val)->string("value must be a multiple of ", val),
3858
])
3959

4060
const VAL_API_PARAM = Dict{Symbol,Function}([
@@ -49,6 +69,8 @@ const VAL_API_PARAM = Dict{Symbol,Function}([
4969
:minProperties => val_min_length,
5070
:pattern => val_pattern,
5171
:enum => val_enum,
72+
:format => val_format,
73+
:multipleOf => val_multiple_of,
5274
])
5375

5476
function validate_param(param, operation_or_model, rule, value, args...)

test/client/allany/AllAnyClient/src/models/model_AnyOfMappedPets.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44

55

6-
@doc raw"""
6+
@doc raw"""AnyOfMappedPets
7+
78
AnyOfMappedPets(; value=nothing)
89
"""
910
mutable struct AnyOfMappedPets <: OpenAPI.AnyOfAPIModel

test/client/allany/AllAnyClient/src/models/model_AnyOfPets.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44

55

6-
@doc raw"""
6+
@doc raw"""AnyOfPets
7+
78
AnyOfPets(; value=nothing)
89
"""
910
mutable struct AnyOfPets <: OpenAPI.AnyOfAPIModel

test/client/allany/AllAnyClient/src/models/model_Cat.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# Do not modify this file directly. Modify the OpenAPI specification instead.
33

44

5-
@doc raw"""
5+
@doc raw"""Cat
6+
67
Cat(;
78
pet_type=nothing,
89
hunts=nothing,

test/client/allany/AllAnyClient/src/models/model_CatAllOf.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# Do not modify this file directly. Modify the OpenAPI specification instead.
33

44

5-
@doc raw"""
5+
@doc raw"""Cat_allOf
6+
67
CatAllOf(;
78
hunts=nothing,
89
age=nothing,

test/client/allany/AllAnyClient/src/models/model_Dog.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# Do not modify this file directly. Modify the OpenAPI specification instead.
33

44

5-
@doc raw"""
5+
@doc raw"""Dog
6+
67
Dog(;
78
pet_type=nothing,
89
bark=nothing,

0 commit comments

Comments
 (0)