Skip to content
43 changes: 43 additions & 0 deletions apisix/plugins/grpc-transcode/response.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ local string = string
local ngx_decode_base64 = ngx.decode_base64
local ipairs = ipairs
local pcall = pcall
local type = type
local pairs = pairs
local setmetatable = setmetatable

pb.option "decode_default_array"
-- Protobuf repeated field label value
local PROTOBUF_REPEATED_LABEL = 3
local repeated_label = PROTOBUF_REPEATED_LABEL

local function fetch_proto_array_names(proto_obj)
local names = {}
if type(proto_obj) == "table" then
for k,v in pairs(proto_obj) do
if type(v) == "table" then
local sub_names = fetch_proto_array_names(v)
for sub_name,_ in pairs(sub_names) do
names[sub_name] = 1
end
end
end
if proto_obj["label"] == repeated_label then
names[proto_obj["name"]] = 1
end
end
return names
end

local function set_default_array(tab, array_names)
if type(tab) ~= "table" then
return
end
for k, v in pairs(tab) do
if type(v) == "table" then
if array_names[k] == 1 then
setmetatable(v, core.json.array_mt)
end
set_default_array(v, array_names)
end
end
end


local function handle_error_response(status_detail_type, proto)
Expand Down Expand Up @@ -132,6 +172,9 @@ return function(ctx, proto, service, method, pb_option, show_status_in_body, sta
return err_msg
end

local array_names = fetch_proto_array_names(proto)
set_default_array(decoded, array_names)

local response, err = core.json.encode(decoded)
if not response then
err_msg = "failed to json_encode response body"
Expand Down
154 changes: 154 additions & 0 deletions t/plugin/grpc-transcode4.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
# ensure that the JSON module of Perl is installed in your test environment.
# If it is not installed, sudo cpanm JSON.
use JSON;

no_long_string();
no_shuffle();
no_root_location();

add_block_preprocessor(sub {
my ($block) = @_;

if (!$block->request) {
$block->set_value("request", "GET /t");
}
});

run_tests;

__DATA__

=== TEST 1: set rule
--- config
location /t {
content_by_lua_block {
local http = require "resty.http"
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/protos/1',
ngx.HTTP_PUT,
[[{
"content" : "syntax = \"proto3\";
package user;
service UserService {
rpc GetUserInfo(UserRequest) returns (UserResponse) {}
}

enum Gender {
GENDER_UNSPECIFIED = 0;
GENDER_MALE = 1;
GENDER_FEMALE = 2;
}
message Job {
string items = 1;
}
message UserRequest {
string name = 1;
int32 age = 2;
}

message UserResponse {
Gender gender = 1;
repeated string items = 2;
string message = 3;
Job job = 4;
}"
}]]
)

if code >= 300 then
ngx.say(body)
return
end

local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["POST"],
"uri": "/grpctest",
"plugins": {
"grpc-transcode": {
"proto_id": "1",
"service": "user.UserService",
"method": "GetUserInfo"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
Copy link
Member

Choose a reason for hiding this comment

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

I can't found this test backend in APISIX e2e framework, I think this new added test cased can't be pass due to #12462 , try to merge master branch and re-run CI again.

Copy link
Author

Choose a reason for hiding this comment

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

When running tests, I used a custom grpc server instead of the original grpc server in apisix. Should I merge the master branch into my local deveop branch?

}
}
}]]
)

if code >= 300 then
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed



=== TEST 2: hit route
--- request
POST /grpctest
{"name":"testUser0","age":0}
--- more_headers
Content-Type: application/json
--- response_body_json
{"gender":"GENDER_MALE","message":"You are an experienced user!","items":["Senior member","Exclusive service"],"job":{"items":"Intern engineer"}}



=== TEST 3: hit route
--- request
POST /grpctest
{"name":"testUser1","age":1}
--- more_headers
Content-Type: application/json
--- response_body_json
{"gender":"GENDER_FEMALE","message":"Welcome new users!","job":{"items":"junior engineer"},"items":[]}



=== TEST 4: hit route
--- request
POST /grpctest
{"name":"testUser2","age":2}
--- more_headers
Content-Type: application/json
--- response_body_json
{"items":[],"message":"You are an experienced user!","job":{"items":"senior engineer"},"gender":"GENDER_UNSPECIFIED"}



=== TEST 5: hit route
--- request
POST /grpctest
{"name":"testUserDefault","age":100}
--- more_headers
Content-Type: application/json
--- response_body_json
{"gender":"GENDER_UNSPECIFIED","items":[],"message":""}
Loading