Skip to content

Commit

Permalink
Automatically add prefixes to enum values if not provided
Browse files Browse the repository at this point in the history
  • Loading branch information
dorner committed Aug 15, 2024
1 parent 63a00b3 commit 4dbf8de
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## UNRELEASED

- Automatically add prefixes to enum values if not provided

# 0.1.17 - 2024-07-30
- Ignore unknown fields in JSON decoding

Expand Down
35 changes: 34 additions & 1 deletion lib/grpc_rest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,45 @@ def assign_value(proto, path, value)
proto.public_send(:"#{tokens.last}=", value) if proto.respond_to?(:"#{tokens.last}=")
end

# https://stackoverflow.com/a/2158473/5199431
def longest_common_substring(arr)
return "" if arr.empty?

result = 0
first_val = arr[0]
(0...first_val.length).each do |k|
all_matched = true
character = first_val[k]
arr.each { |str| all_matched &= (character == str[k]) }
break unless all_matched
result += 1
end
first_val.slice(0, result)
end

def handle_enum_values(descriptor, value)
names = descriptor.subtype.to_h.keys.map(&:to_s)
prefix = longest_common_substring(names)
if prefix.present? && !value.starts_with?(prefix)
"#{prefix}#{value}"
else
value
end
end

def map_proto_type(proto, params)
proto.to_a.each do |descriptor|
field = descriptor.name
val = params[field]
next if val.nil?
next if descriptor.subtype.is_a?(Google::Protobuf::EnumDescriptor)
if descriptor.subtype.is_a?(Google::Protobuf::EnumDescriptor)
if descriptor.label == :repeated
params[field] = val.map { |v| handle_enum_values(descriptor, v)}
else
params[field] = handle_enum_values(descriptor, val)
end
next
end

case descriptor.type
when *%i(int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64)
Expand Down
7 changes: 7 additions & 0 deletions protoc-gen-rails/testdata/test_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ message TestRequest {
google.protobuf.Value bare_value = 9;
repeated SubRecord sub_records = 10;
int32 some_int = 11;
TestEnum some_enum = 12;
}

message SubRecord {
Expand All @@ -30,6 +31,12 @@ message TestResponse {
string full_response = 2;
}

enum TestEnum {
TEST_ENUM_UNSPECIFIED = 0;
TEST_ENUM_FOO = 1;
TEST_ENUM_BAR = 2;
}

service MyService {
rpc Test(TestRequest) returns (TestResponse) {
option (google.api.http) = {
Expand Down
20 changes: 17 additions & 3 deletions spec/grpc_rest_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def test_4(req)
end

describe 'full body splat' do
it 'should be successful' do
params = {
let(:params) do
{
test_id: 'abc',
some_int: "65",
foobar: 'xyz',
Expand All @@ -91,12 +91,26 @@ def test_4(req)
list_value: ['F', 'Y', 'I'],
bare_value: 45,
timestamp_field: '2024-04-03 01:02:03 UTC',
some_enum: 'TEST_ENUM_FOO'
}
end

it 'should be successful' do
post '/test4', params: params, as: :json
expect(response).to be_successful
expect(response.parsed_body).to eq({
'someInt' => 4,
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,"someInt":65,"someEnum":"TEST_ENUM_FOO"})
})
end

it 'should be successful without the enum prefix' do
params[:some_enum] = 'FOO'
post '/test4', params: params, as: :json
expect(response).to be_successful
expect(response.parsed_body).to eq({
'someInt' => 4,
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,\"someInt\":65})
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,"someInt":65,"someEnum":"TEST_ENUM_FOO"})
})
end
end
Expand Down
3 changes: 2 additions & 1 deletion spec/test_service_pb.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4dbf8de

Please sign in to comment.