Skip to content

Commit ba4572c

Browse files
David BachNHDalyDrvi
authored
ProtoBuf metadata (#60)
* Update CHANGELOG * Bump version * Add EnumX and ProtocolBuffers packages * ProtoBuf codegen * Add methods to handle and show new ProtoBuf metadata * Ensure Accept: application/x-protobuf header for requests * Deserialize metadata using ProtoBuf decoder * Add tests * Add v2_async_response to test * Move encode workaround out of generated file * Add comment about strings and symbol constant types * Fix typo * JuliaFormatter * Migrate to ProtoBuf.jl 1.0 * Fix merge conflict: support show for empty tuples. * Fix merge conflict * Regenerate protos from new schema * Remove SYMBOL reference * Change show representation of RelationId * Adapt tests Co-authored-by: Nathan Daly <[email protected]> Co-authored-by: Tomáš Drvoštěp <[email protected]>
1 parent ad63e2d commit ba4572c

File tree

15 files changed

+972
-55
lines changed

15 files changed

+972
-55
lines changed

CHANGELOG.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
# Changelog
22

3-
## main
3+
## v0.2.0
4+
* Deprecate metadata JSON format.
5+
* Add support to deserialize ProtoBuf metadata.
6+
* `get_transaction_metadata` returns a `MetadataInfo`, see `src/proto`
7+
and `src/gen/relationalai/protocol/` for more information.
48

5-
* Add find_user to api.jl
6-
* Rename examples/get-userid.jl to examples/find-user.jl
7-
* Fixed bug in support for custom extra `headers` in SDK. For example:
8-
```
9-
create_engine(ctx, engine; size = size, headers=["my-custom-header" => "custom header value"])
10-
```
9+
## v0.1.5
10+
* Add support for optional audience field to Config
11+
12+
## v0.1.4
13+
* Retry retryable HTTP errors
14+
15+
## v0.1.0
16+
* Implement V2 `show_result` method
17+
18+
## v0.0.4
19+
* Properly filter transaction results based on multi-part content type
20+
* Set `HTTP.jl` compat to `1.0`
21+
22+
## v0.0.3
23+
* New access token for each request
24+
* Anticipate access token expiration
1125

1226
## v0.0.2
1327

@@ -16,3 +30,12 @@ create_engine(ctx, engine; size = size, headers=["my-custom-header" => "custom h
1630
- If you cancel the polling via `ctrl-C`, the error log will print the transaction ID, so you can still
1731
recover the transaction or cancel it.
1832
* Consistent return format (`Dict`) from `exec_async()` and `exec()`, regardless of whether you get synchronous results ([#24](https://github.com/RelationalAI/rai-sdk-julia/pull/24)).
33+
34+
## main
35+
36+
* Add find_user to api.jl
37+
* Rename examples/get-userid.jl to examples/find-user.jl
38+
* Fixed bug in support for custom extra `headers` in SDK. For example:
39+
```
40+
create_engine(ctx, engine; size = size, headers=["my-custom-header" => "custom header value"])
41+
```

Project.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
name = "RAI"
22
uuid = "9c30249a-7e08-11ec-0e99-a323e937e79f"
3-
version = "0.1.8"
3+
version = "0.2.0"
44

55
[deps]
66
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
77
Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45"
88
ConfParser = "88353bc9-fd38-507d-a820-d3b43837d6b9"
99
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
10+
EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
1011
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
1112
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
1213
Mocking = "78c3b35d-d492-501b-9361-3d52fe80e533"
1314
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
15+
ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429"
1416
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
1517

1618
[compat]
1719
ArgParse = "1"
1820
Arrow = "2"
1921
ConfParser = "0.1"
22+
EnumX = "1"
2023
HTTP = "1.0"
2124
JSON3 = "1"
2225
Mocking = "0.7"
26+
ProtoBuf = "1"
2327
Tables = "1"
2428
julia = "1.6"
2529

src/RAI.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ export
9696
include("creds.jl")
9797
include("config.jl")
9898
include("rest.jl")
99+
# Generated with:
100+
# ProtoBuf.protojl(readdir("src/proto"), "src/proto", "src/gen", parametrize_oneofs=true)
101+
include("gen/relationalai/relationalai.jl")
102+
import .relationalai.protocol
103+
include("metadata.jl")
99104
include("response.jl")
100105
include("api.jl")
101106
include("results.jl")

src/api.jl

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
# Julia language.
2020

2121
import Arrow
22+
import ProtoBuf
2223
using Base.Threads: @spawn
2324
import Dates
2425
import JSON3
@@ -564,10 +565,15 @@ function exec_async(ctx::Context, database::AbstractString, engine::AbstractStri
564565
end
565566
body = JSON3.write(tx_body)
566567
path = _mkurl(ctx, PATH_ASYNC_TRANSACTIONS)
568+
headers = _ensure_proto_accept_header(get(kw, :headers, []))
567569
rsp = @mock request(ctx, "POST", path; body = body, kw...)
568570
return _parse_response(rsp)
569571
end
570572

573+
# We **only** support ProtoBuf metadata, so we overwrite the `Accept` header.
574+
_ensure_proto_accept_header(headers) =
575+
collect(merge(Dict(headers), Dict("Accept" => "application/x-protobuf")))
576+
571577
function _parse_response(rsp)
572578
content_type = HTTP.header(rsp, "Content-Type")
573579
if lowercase(content_type) == "application/json"
@@ -605,8 +611,12 @@ end
605611

606612
function get_transaction_metadata(ctx::Context, id::AbstractString; kw...)
607613
path = PATH_ASYNC_TRANSACTIONS * "/$id/metadata"
608-
rsp = _get(ctx, path; kw...)
609-
return rsp
614+
path = _mkurl(ctx, path)
615+
headers = _ensure_proto_accept_header(get(kw, :headers, []))
616+
rsp = request(ctx, "GET", path; kw..., headers)
617+
d = ProtoBuf.ProtoDecoder(IOBuffer(rsp.body));
618+
metadata = ProtoBuf.decode(d, protocol.MetadataInfo)
619+
return metadata
610620
end
611621

612622
function get_transaction_problems(ctx::Context, id::AbstractString; kw...)
@@ -631,12 +641,16 @@ function _parse_multipart_fastpath_sync_response(msg)
631641
# ... HTTP.parse_multipart_form() copies the bytes into IOBuffers.
632642
parts = _parse_multipart_form(msg)
633643
@assert parts[1].name == "transaction"
634-
@assert parts[2].name == "metadata"
635644

636645
transaction = JSON3.read(parts[1])
637-
metadata = JSON3.read(parts[2])
646+
647+
metadata_idx = findfirst(p->p.name == "metadata.proto", parts)
648+
d = ProtoBuf.ProtoDecoder(parts[metadata_idx].data);
649+
metadata = ProtoBuf.decode(d, protocol.MetadataInfo)
650+
638651
problems_idx = findfirst(p->p.name == "problems", parts)
639652
problems = JSON3.read(parts[problems_idx])
653+
640654
results = _extract_multipart_results_response(parts)
641655

642656
return TransactionResponse(transaction, metadata, problems, results)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Autogenerated using ProtoBuf.jl v1.0.5 on 2022-08-31T16:58:14.621
2+
# original file: /Users/david/Projects/rai-sdk-julia/src/proto/message.proto (proto3 syntax)
3+
4+
import ProtoBuf as PB
5+
using ProtoBuf: OneOf
6+
using EnumX: @enumx
7+
8+
export RelationMetadata, MetadataInfo
9+
10+
struct RelationMetadata
11+
relation_id::Union{Nothing,RelationId}
12+
file_name::String
13+
end
14+
PB.default_values(::Type{RelationMetadata}) = (;relation_id = nothing, file_name = "")
15+
PB.field_numbers(::Type{RelationMetadata}) = (;relation_id = 1, file_name = 2)
16+
17+
function PB.decode(d::PB.AbstractProtoDecoder, ::Type{<:RelationMetadata})
18+
relation_id = Ref{Union{Nothing,RelationId}}(nothing)
19+
file_name = ""
20+
while !PB.message_done(d)
21+
field_number, wire_type = PB.decode_tag(d)
22+
if field_number == 1
23+
PB.decode!(d, relation_id)
24+
elseif field_number == 2
25+
file_name = PB.decode(d, String)
26+
else
27+
PB.skip(d, wire_type)
28+
end
29+
end
30+
return RelationMetadata(relation_id[], file_name)
31+
end
32+
33+
function PB.encode(e::PB.AbstractProtoEncoder, x::RelationMetadata)
34+
initpos = position(e.io)
35+
!isnothing(x.relation_id) && PB.encode(e, 1, x.relation_id)
36+
!isempty(x.file_name) && PB.encode(e, 2, x.file_name)
37+
return position(e.io) - initpos
38+
end
39+
function PB._encoded_size(x::RelationMetadata)
40+
encoded_size = 0
41+
!isnothing(x.relation_id) && (encoded_size += PB._encoded_size(x.relation_id, 1))
42+
!isempty(x.file_name) && (encoded_size += PB._encoded_size(x.file_name, 2))
43+
return encoded_size
44+
end
45+
46+
struct MetadataInfo
47+
relations::Vector{RelationMetadata}
48+
end
49+
PB.default_values(::Type{MetadataInfo}) = (;relations = Vector{RelationMetadata}())
50+
PB.field_numbers(::Type{MetadataInfo}) = (;relations = 1)
51+
52+
function PB.decode(d::PB.AbstractProtoDecoder, ::Type{<:MetadataInfo})
53+
relations = PB.BufferedVector{RelationMetadata}()
54+
while !PB.message_done(d)
55+
field_number, wire_type = PB.decode_tag(d)
56+
if field_number == 1
57+
PB.decode!(d, relations)
58+
else
59+
PB.skip(d, wire_type)
60+
end
61+
end
62+
return MetadataInfo(relations[])
63+
end
64+
65+
function PB.encode(e::PB.AbstractProtoEncoder, x::MetadataInfo)
66+
initpos = position(e.io)
67+
!isempty(x.relations) && PB.encode(e, 1, x.relations)
68+
return position(e.io) - initpos
69+
end
70+
function PB._encoded_size(x::MetadataInfo)
71+
encoded_size = 0
72+
!isempty(x.relations) && (encoded_size += PB._encoded_size(x.relations, 1))
73+
return encoded_size
74+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module protocol
2+
3+
include("schema_pb.jl")
4+
include("message_pb.jl")
5+
6+
end # module protocol

0 commit comments

Comments
 (0)