Skip to content

Commit

Permalink
added protobuf extension and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ndortega committed Mar 11, 2024
1 parent 2d4a12f commit 2288225
Show file tree
Hide file tree
Showing 14 changed files with 312 additions and 8 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Pkg = "^1"
Reexport = "^1"
RelocatableFolders = "^1"
Requires = "^1"
ProtoBuf = "^1"
Sockets = "^1"
Statistics = "^1"
StructTypes = "^1"
Expand All @@ -43,10 +44,11 @@ CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
OteraEngine = "b2d7f28f-acd6-4007-8b26-bc27716e5513"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429"
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"

[targets]
test = ["Bonito", "CairoMakie", "Mustache", "OteraEngine", "Pkg", "StructTypes", "Test", "Suppressor", "WGLMakie"]
test = ["Bonito", "CairoMakie", "Mustache", "OteraEngine", "Pkg", "ProtoBuf", "StructTypes", "Suppressor", "Test", "WGLMakie"]
8 changes: 7 additions & 1 deletion src/extensions/load.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ end

function __init__()


################################################################
# Serialization Extensions #
################################################################
@require ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429" include("serialization/protobuf.jl")


################################################################
# Templating Extensions #
################################################################
Expand All @@ -42,6 +49,5 @@ function __init__()
include("plotting/wglmakie.jl")
end
end


end
73 changes: 73 additions & 0 deletions src/extensions/serialization/protobuf.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import HTTP
import .ProtoBuf: encode, decode, ProtoDecoder, ProtoEncoder

export protobuf

"""
protobuf(request::HTTP.Request, type::Type{T}) :: T where {T}
Decode a protobuf message from the body of an HTTP request.
# Arguments
- `request`: An HTTP request object containing the protobuf message in its body.
- `type`: The type of the protobuf message to decode.
# Returns
- The decoded protobuf message of the specified type.
"""
function protobuf(request::HTTP.Request, type::Type{T}) :: T where {T}
io = IOBuffer(request.body)
return decode(ProtoDecoder(io), type)
end


"""
protobuf(content::T, url::String, method::String = "POST") :: HTTP.Request where {T}
Encode a protobuf message into the body of an HTTP request.
# Arguments
- `content`: The protobuf message to encode.
- `url`: The URL to which the request will be sent.
- `method`: The HTTP method for the request (default is "POST").
- `headers`: The HTTP headers for the request (default is an empty list).
# Returns
- An HTTP request object with the encoded protobuf message in its body.
"""
function protobuf(content::T, url::String; method = "GET", headers = []) :: HTTP.Request where {T}
io = IOBuffer()
encode(ProtoEncoder(io), content)
body = take!(io)
# Format the request
request = HTTP.Request(method, url, headers, body)
HTTP.setheader(request, "Content-Type" => "application/octet-stream")
HTTP.setheader(request, "Content-Length" => string(sizeof(body)))
return request
end


"""
protobuf(content::T; status = 200, headers = []) :: HTTP.Response where {T}
Encode a protobuf message into the body of an HTTP response.
# Arguments
- `content`: The protobuf message to encode.
- `status`: The HTTP status code for the response (default is 200).
- `headers`: The HTTP headers for the response (default is an empty list).
# Returns
- An HTTP response object with the encoded protobuf message in its body.
"""
function protobuf(content::T; status = 200, headers = []) :: HTTP.Response where {T}
io = IOBuffer()
encode(ProtoEncoder(io), content)
body = take!(io)
# Format the response
response = HTTP.Response(status, headers, body = body)
HTTP.setheader(response, "Content-Type" => "application/octet-stream")
HTTP.setheader(response, "Content-Length" => string(sizeof(body)))
return response
end

4 changes: 2 additions & 2 deletions src/utilities/bodyparsers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ end
Read the body of a HTTP.Request as JSON with additional arguments for the read/serializer into a custom struct.
"""
function json(req::HTTP.Request, classtype; kwargs...)
function json(req::HTTP.Request, classtype::Type{T}; kwargs...) :: T where {T}
body = IOBuffer(HTTP.payload(req))
return eof(body) ? nothing : JSON3.read(body, classtype; kwargs...)
end
Expand Down Expand Up @@ -96,6 +96,6 @@ end
Read the body of a HTTP.Messages.Response as JSON with additional keyword arguments and serialize it into a custom struct
"""
function json(response::HTTP.Messages.Response, classtype; kwargs...)
function json(response::HTTP.Messages.Response, classtype::Type{T}; kwargs...) :: T where {T}
return JSON3.read(String(response.body), classtype; kwargs...)
end
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions test/extensions/protobuf/messages/people.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
syntax = "proto3";

message Person {
string name = 1;
sint32 age = 2;
}

message People {
repeated Person people = 1;
}
77 changes: 77 additions & 0 deletions test/extensions/protobuf/messages/people_pb.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Autogenerated using ProtoBuf.jl v1.0.15 on 2024-03-10T22:46:41.410
# original file: D:\Programming\JuliaProjects\oxygen-demo\src\people.proto (proto3 syntax)

module people_pb

import ProtoBuf as PB
using ProtoBuf: OneOf
using ProtoBuf.EnumX: @enumx

export Person, People

struct Person
name::String
age::Int32
end
PB.default_values(::Type{Person}) = (;name = "", age = zero(Int32))
PB.field_numbers(::Type{Person}) = (;name = 1, age = 2)

function PB.decode(d::PB.AbstractProtoDecoder, ::Type{<:Person})
name = ""
age = zero(Int32)
while !PB.message_done(d)
field_number, wire_type = PB.decode_tag(d)
if field_number == 1
name = PB.decode(d, String)
elseif field_number == 2
age = PB.decode(d, Int32, Val{:zigzag})
else
PB.skip(d, wire_type)
end
end
return Person(name, age)
end

function PB.encode(e::PB.AbstractProtoEncoder, x::Person)
initpos = position(e.io)
!isempty(x.name) && PB.encode(e, 1, x.name)
x.age != zero(Int32) && PB.encode(e, 2, x.age, Val{:zigzag})
return position(e.io) - initpos
end
function PB._encoded_size(x::Person)
encoded_size = 0
!isempty(x.name) && (encoded_size += PB._encoded_size(x.name, 1))
x.age != zero(Int32) && (encoded_size += PB._encoded_size(x.age, 2, Val{:zigzag}))
return encoded_size
end

struct People
people::Vector{Person}
end
PB.default_values(::Type{People}) = (;people = Vector{Person}())
PB.field_numbers(::Type{People}) = (;people = 1)

function PB.decode(d::PB.AbstractProtoDecoder, ::Type{<:People})
people = PB.BufferedVector{Person}()
while !PB.message_done(d)
field_number, wire_type = PB.decode_tag(d)
if field_number == 1
PB.decode!(d, people)
else
PB.skip(d, wire_type)
end
end
return People(people[])
end

function PB.encode(e::PB.AbstractProtoEncoder, x::People)
initpos = position(e.io)
!isempty(x.people) && PB.encode(e, 1, x.people)
return position(e.io) - initpos
end
function PB._encoded_size(x::People)
encoded_size = 0
!isempty(x.people) && (encoded_size += PB._encoded_size(x.people, 1))
return encoded_size
end
end # module
6 changes: 6 additions & 0 deletions test/extensions/protobuf/messages/test.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
syntax = "proto3";

message MyMessage {
sint32 a = 1;
repeated string b = 2;
}
47 changes: 47 additions & 0 deletions test/extensions/protobuf/messages/test_pb.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Autogenerated using ProtoBuf.jl v1.0.15 on 2024-02-27T23:51:29.590
# original file: D:\Programming\JuliaProjects\oxygen-demo\src\test.proto (proto3 syntax)

module test_pb

import ProtoBuf as PB
using ProtoBuf: OneOf
using ProtoBuf.EnumX: @enumx

export MyMessage

struct MyMessage
a::Int32
b::Vector{String}
end
PB.default_values(::Type{MyMessage}) = (;a = zero(Int32), b = Vector{String}())
PB.field_numbers(::Type{MyMessage}) = (;a = 1, b = 2)

function PB.decode(d::PB.AbstractProtoDecoder, ::Type{<:MyMessage})
a = zero(Int32)
b = PB.BufferedVector{String}()
while !PB.message_done(d)
field_number, wire_type = PB.decode_tag(d)
if field_number == 1
a = PB.decode(d, Int32, Val{:zigzag})
elseif field_number == 2
PB.decode!(d, b)
else
PB.skip(d, wire_type)
end
end
return MyMessage(a, b[])
end

function PB.encode(e::PB.AbstractProtoEncoder, x::MyMessage)
initpos = position(e.io)
x.a != zero(Int32) && PB.encode(e, 1, x.a, Val{:zigzag})
!isempty(x.b) && PB.encode(e, 2, x.b)
return position(e.io) - initpos
end
function PB._encoded_size(x::MyMessage)
encoded_size = 0
x.a != zero(Int32) && (encoded_size += PB._encoded_size(x.a, 1, Val{:zigzag}))
!isempty(x.b) && (encoded_size += PB._encoded_size(x.b, 2))
return encoded_size
end
end # module
77 changes: 77 additions & 0 deletions test/extensions/protobuf/protobuftests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
module ProtobufTests

using Test
using HTTP
using ProtoBuf
using Oxygen: protobuf

include("messages/people_pb.jl");
using .people_pb: People, Person

include("messages/test_pb.jl");
using .test_pb: MyMessage

@testset "Protobuf decoder test" begin
message = MyMessage(-1, ["a", "b"])
req::HTTP.Request = protobuf(message, "/")

decoded_msg = protobuf(req, MyMessage)

@test decoded_msg isa MyMessage
@test decoded_msg.a == -1
@test decoded_msg.b == ["a", "b"]
end

@testset "Protobuf People Decoder test" begin
message = People([
Person("John Doe", 20),
Person("Jane Doe", 25),
Person("Alice", 30),
Person("Bob", 35),
Person("Charlie", 40)
])

req::HTTP.Request = protobuf(message, "/", method="POST")

decoded_msg = protobuf(req, People)

@test decoded_msg isa People
for person in decoded_msg.people
@test person isa Person
end
end


@testset "Protobuf encoder test" begin

message = MyMessage(-1, ["a", "b"])
response = protobuf(message)

@test response isa HTTP.Response
@test response.status == 200
@test response.body isa Vector{UInt8}
@test HTTP.header(response, "Content-Type") == "application/octet-stream"
@test HTTP.header(response, "Content-Length") == string(sizeof(response.body))
end


@testset "Protobuf People encoder test" begin
message = People([
Person("John Doe", 20),
Person("Jane Doe", 25),
Person("Alice", 30),
Person("Bob", 35),
Person("Charlie", 40)
])

response = protobuf(message)

@test response isa HTTP.Response
@test response.status == 200
@test response.body isa Vector{UInt8}
@test HTTP.header(response, "Content-Type") == "application/octet-stream"
@test HTTP.header(response, "Content-Length") == string(sizeof(response.body))
end


end
File renamed without changes.
File renamed without changes.
14 changes: 10 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ module RunTests

include("constants.jl"); using .Constants

#### Extension Tests ####

include("extensions/templatingtests.jl")
include("extensions/cairomakietests.jl")
include("extensions/wglmakietests.jl")
include("extensions/bonitotests.jl")
include("extensions/protobuf/protobuftests.jl")

#### Core Tests ####

include("metricstests.jl")
include("templatingtests.jl")
include("routingfunctionstests.jl")
include("rendertests.jl")
include("cairomakietests.jl")
include("wglmakietests.jl")
include("bonitotests.jl")
include("bodyparsertests.jl")
include("crontests.jl")
include("oxidise.jl")
Expand Down

0 comments on commit 2288225

Please sign in to comment.