feat: support send report to a gRPC server (#431)

This commit is contained in:
Ziyi Li 2024-05-17 13:33:12 +08:00 committed by GitHub
parent 8b2c8f289c
commit 50798ffcb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 698 additions and 3 deletions

View File

@ -114,7 +114,7 @@ See also https://github.com/LinuxSuRen/api-testing/tree/master/sample`,
flags.DurationVarP(&opt.duration, "duration", "", 0, "Running duration")
flags.DurationVarP(&opt.requestTimeout, "request-timeout", "", time.Minute, "Timeout for per request")
flags.BoolVarP(&opt.requestIgnoreError, "request-ignore-error", "", false, "Indicate if ignore the request error")
flags.StringVarP(&opt.report, "report", "", "", "The type of target report. Supported: markdown, md, html, json, discard, std, prometheus, http")
flags.StringVarP(&opt.report, "report", "", "", "The type of target report. Supported: markdown, md, html, json, discard, std, prometheus, http, grpc")
flags.StringVarP(&opt.reportFile, "report-file", "", "", "The file path of the report")
flags.BoolVarP(&opt.reportIgnore, "report-ignore", "", false, "Indicate if ignore the report output")
flags.StringVarP(&opt.reportTemplate, "report-template", "", "", "The template used to render the report")
@ -166,6 +166,11 @@ func (o *runOption) preRunE(cmd *cobra.Command, args []string) (err error) {
case "http":
templateOption := runner.NewTemplateOption(o.reportTemplate, "json")
o.reportWriter = runner.NewHTTPResultWriter(http.MethodPost, o.reportDest, nil, templateOption)
case "grpc":
if o.reportDest == "" {
err = fmt.Errorf("report gRPC server url is required for prometheus report")
}
o.reportWriter = runner.NewGRPCResultWriter(o.context, o.reportDest)
default:
err = fmt.Errorf("not supported report type: '%s'", o.report)
}

View File

@ -245,7 +245,7 @@ func (s *gRPCTestCaseRunner) WithSuite(suite *testing.TestSuite) {
// not need this parameter
}
func invokeRequest(ctx context.Context, md protoreflect.MethodDescriptor, payload string, conn *grpc.ClientConn) (respones []string, err error) {
func invokeRequest(ctx context.Context, md protoreflect.MethodDescriptor, payload string, conn *grpc.ClientConn) (response []string, err error) {
resps := make([]*dynamicpb.Message, 0)
if md.IsStreamingClient() || md.IsStreamingServer() {
reqs, err := getStreamMessagepb(md.Input(), payload)
@ -483,7 +483,6 @@ func getByReflect(ctx context.Context, r *gRPCTestCaseRunner, fullName protorefl
if err != nil {
return nil, err
}
req := &grpc_reflection_v1.ServerReflectionRequest{
Host: "",
MessageRequest: &grpc_reflection_v1.ServerReflectionRequest_FileContainingSymbol{

120
pkg/runner/writer_grpc.go Normal file
View File

@ -0,0 +1,120 @@
/*
Copyright 2024 API Testing Authors.
Licensed 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.
*/
package runner
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"github.com/linuxsuren/api-testing/pkg/apispec"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
type grpcResultWriter struct {
context context.Context
targetUrl string
}
// NewGRPCResultWriter creates a new grpcResultWriter
func NewGRPCResultWriter(ctx context.Context, url string) ReportResultWriter {
return &grpcResultWriter{
context: ctx,
targetUrl: url,
}
}
// Output writes the JSON base report to target writer
func (w *grpcResultWriter) Output(result []ReportResult) (err error) {
server, err := w.getHost()
if err != nil {
log.Fatalln(err)
}
log.Println("will send report to:" + server)
conn, err := getConnection(server)
if err != nil {
log.Println("Error when connecting to grpc server", err)
return err
}
defer conn.Close()
md, err := w.getMethodDescriptor(conn)
if err != nil {
if err == protoregistry.NotFound {
return fmt.Errorf("api %q is not found on grpc server", w.targetUrl)
}
return err
}
jsonPayload, _ := json.Marshal(
map[string][]ReportResult{
"data": result,
})
payload := string(jsonPayload)
resp, err := invokeRequest(w.context, md, payload, conn)
if err != nil {
log.Fatalln(err)
}
log.Println("getting response back:", resp)
return
}
// use server reflection to get the method descriptor
func (w *grpcResultWriter) getMethodDescriptor(conn *grpc.ClientConn) (protoreflect.MethodDescriptor, error) {
fullName, err := splitFullQualifiedName(w.targetUrl)
if err != nil {
return nil, err
}
var dp protoreflect.Descriptor
dp, err = getByReflect(w.context, nil, fullName, conn)
if err != nil {
return nil, err
}
if dp.IsPlaceholder() {
return nil, protoregistry.NotFound
}
if md, ok := dp.(protoreflect.MethodDescriptor); ok {
return md, nil
}
return nil, protoregistry.NotFound
}
func (w *grpcResultWriter) getHost() (host string, err error) {
qn := regexFullQualifiedName.FindStringSubmatch(w.targetUrl)
if len(qn) == 0 {
return "", errors.New("can not get host from url")
}
return qn[1], nil
}
// get connection with gRPC server
func getConnection(host string) (conn *grpc.ClientConn, err error) {
conn, err = grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials()))
return
}
// WithAPIConverage sets the api coverage
func (w *grpcResultWriter) WithAPIConverage(apiConverage apispec.APIConverage) ReportResultWriter {
return w
}
func (w *grpcResultWriter) WithResourceUsage([]ResourceUsage) ReportResultWriter {
return w
}

View File

@ -0,0 +1,68 @@
/*
Copyright 2024 API Testing Authors.
Licensed 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.
*/
package runner
import (
"context"
"testing"
testWriter "github.com/linuxsuren/api-testing/pkg/runner/writer_templates"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
func TestGRPCResultWriter(t *testing.T) {
t.Run("test request", func(t *testing.T) {
s := grpc.NewServer()
testServer := &testWriter.ReportServer{}
testWriter.RegisterReportWriterServer(s, testServer)
reflection.RegisterV1(s)
l := runServer(t, s)
url := l.Addr().String() + "/writer_templates.ReportWriter/SendReportResult"
ctx := context.Background()
writer := NewGRPCResultWriter(ctx, url)
err := writer.Output([]ReportResult{{
Name: "test",
API: "/api",
Max: 1,
Average: 2,
Error: 3,
Count: 1,
}})
assert.NoError(t, err)
s.Stop()
})
t.Run("test reflect unsupported on server", func(t *testing.T) {
s := grpc.NewServer()
testServer := &testWriter.ReportServer{}
testWriter.RegisterReportWriterServer(s, testServer)
l := runServer(t, s)
url := l.Addr().String() + "/writer_templates.ReportWriter/SendReportResult"
ctx := context.Background()
writer := NewGRPCResultWriter(ctx, url)
err := writer.Output([]ReportResult{{
Name: "test",
API: "/api",
Max: 1,
Average: 2,
Error: 3,
Count: 1,
}})
assert.NotNil(t, err)
})
}

View File

@ -0,0 +1,18 @@
package writer_templates
import (
"context"
"log"
)
type ReportServer struct {
UnimplementedReportWriterServer
}
func (s *ReportServer) SendReportResult(ctx context.Context, req *ReportResultRepeated) (*Empty, error) {
// print received data
for _, result := range req.Data {
log.Printf("Received report: %+v", result)
}
return &Empty{}, nil
}

View File

@ -0,0 +1,348 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.1
// protoc v4.25.3
// source: writer_templates/writer.proto
package writer_templates
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ReportResultRepeated struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data []*ReportResult `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
}
func (x *ReportResultRepeated) Reset() {
*x = ReportResultRepeated{}
if protoimpl.UnsafeEnabled {
mi := &file_writer_templates_writer_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReportResultRepeated) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReportResultRepeated) ProtoMessage() {}
func (x *ReportResultRepeated) ProtoReflect() protoreflect.Message {
mi := &file_writer_templates_writer_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReportResultRepeated.ProtoReflect.Descriptor instead.
func (*ReportResultRepeated) Descriptor() ([]byte, []int) {
return file_writer_templates_writer_proto_rawDescGZIP(), []int{0}
}
func (x *ReportResultRepeated) GetData() []*ReportResult {
if x != nil {
return x.Data
}
return nil
}
type ReportResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
API string `protobuf:"bytes,2,opt,name=API,proto3" json:"API,omitempty"`
Count int32 `protobuf:"varint,3,opt,name=Count,proto3" json:"Count,omitempty"`
Average int64 `protobuf:"varint,4,opt,name=Average,proto3" json:"Average,omitempty"`
Max int64 `protobuf:"varint,5,opt,name=Max,proto3" json:"Max,omitempty"`
Min int64 `protobuf:"varint,6,opt,name=Min,proto3" json:"Min,omitempty"`
QPS int32 `protobuf:"varint,7,opt,name=QPS,proto3" json:"QPS,omitempty"`
Error int32 `protobuf:"varint,8,opt,name=Error,proto3" json:"Error,omitempty"`
LastErrorMessage string `protobuf:"bytes,9,opt,name=LastErrorMessage,proto3" json:"LastErrorMessage,omitempty"`
}
func (x *ReportResult) Reset() {
*x = ReportResult{}
if protoimpl.UnsafeEnabled {
mi := &file_writer_templates_writer_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReportResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReportResult) ProtoMessage() {}
func (x *ReportResult) ProtoReflect() protoreflect.Message {
mi := &file_writer_templates_writer_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReportResult.ProtoReflect.Descriptor instead.
func (*ReportResult) Descriptor() ([]byte, []int) {
return file_writer_templates_writer_proto_rawDescGZIP(), []int{1}
}
func (x *ReportResult) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ReportResult) GetAPI() string {
if x != nil {
return x.API
}
return ""
}
func (x *ReportResult) GetCount() int32 {
if x != nil {
return x.Count
}
return 0
}
func (x *ReportResult) GetAverage() int64 {
if x != nil {
return x.Average
}
return 0
}
func (x *ReportResult) GetMax() int64 {
if x != nil {
return x.Max
}
return 0
}
func (x *ReportResult) GetMin() int64 {
if x != nil {
return x.Min
}
return 0
}
func (x *ReportResult) GetQPS() int32 {
if x != nil {
return x.QPS
}
return 0
}
func (x *ReportResult) GetError() int32 {
if x != nil {
return x.Error
}
return 0
}
func (x *ReportResult) GetLastErrorMessage() string {
if x != nil {
return x.LastErrorMessage
}
return ""
}
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_writer_templates_writer_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Empty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_writer_templates_writer_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_writer_templates_writer_proto_rawDescGZIP(), []int{2}
}
var File_writer_templates_writer_proto protoreflect.FileDescriptor
var file_writer_templates_writer_proto_rawDesc = []byte{
0x0a, 0x1d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
0x65, 0x73, 0x2f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x10, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
0x73, 0x22, 0x4a, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x04, 0x64, 0x61, 0x74,
0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72,
0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72,
0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xdc, 0x01,
0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12,
0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x41, 0x50, 0x49, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x05, 0x52, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x41, 0x76,
0x65, 0x72, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x41, 0x76, 0x65,
0x72, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28,
0x03, 0x52, 0x03, 0x4d, 0x61, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x69, 0x6e, 0x18, 0x06, 0x20,
0x01, 0x28, 0x03, 0x52, 0x03, 0x4d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x51, 0x50, 0x53, 0x18,
0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x51, 0x50, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72,
0x12, 0x2a, 0x0a, 0x10, 0x4c, 0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x4c, 0x61, 0x73, 0x74,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x07, 0x0a, 0x05,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x63, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x57,
0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x53, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x70,
0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x26, 0x2e, 0x77, 0x72, 0x69, 0x74,
0x65, 0x72, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x70,
0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x1a, 0x17, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c,
0x61, 0x74, 0x65, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75,
0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f,
0x70, 0x6b, 0x67, 0x2f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x77, 0x72, 0x69, 0x74, 0x65,
0x72, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_writer_templates_writer_proto_rawDescOnce sync.Once
file_writer_templates_writer_proto_rawDescData = file_writer_templates_writer_proto_rawDesc
)
func file_writer_templates_writer_proto_rawDescGZIP() []byte {
file_writer_templates_writer_proto_rawDescOnce.Do(func() {
file_writer_templates_writer_proto_rawDescData = protoimpl.X.CompressGZIP(file_writer_templates_writer_proto_rawDescData)
})
return file_writer_templates_writer_proto_rawDescData
}
var file_writer_templates_writer_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_writer_templates_writer_proto_goTypes = []interface{}{
(*ReportResultRepeated)(nil), // 0: writer_templates.ReportResultRepeated
(*ReportResult)(nil), // 1: writer_templates.ReportResult
(*Empty)(nil), // 2: writer_templates.Empty
}
var file_writer_templates_writer_proto_depIdxs = []int32{
1, // 0: writer_templates.ReportResultRepeated.data:type_name -> writer_templates.ReportResult
0, // 1: writer_templates.ReportWriter.SendReportResult:input_type -> writer_templates.ReportResultRepeated
2, // 2: writer_templates.ReportWriter.SendReportResult:output_type -> writer_templates.Empty
2, // [2:3] is the sub-list for method output_type
1, // [1:2] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_writer_templates_writer_proto_init() }
func file_writer_templates_writer_proto_init() {
if File_writer_templates_writer_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_writer_templates_writer_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReportResultRepeated); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_writer_templates_writer_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReportResult); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_writer_templates_writer_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_writer_templates_writer_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_writer_templates_writer_proto_goTypes,
DependencyIndexes: file_writer_templates_writer_proto_depIdxs,
MessageInfos: file_writer_templates_writer_proto_msgTypes,
}.Build()
File_writer_templates_writer_proto = out.File
file_writer_templates_writer_proto_rawDesc = nil
file_writer_templates_writer_proto_goTypes = nil
file_writer_templates_writer_proto_depIdxs = nil
}

View File

@ -0,0 +1,28 @@
syntax = "proto3";
option go_package = "github.com/linuxsuren/api-testing/pkg/runner/writer_templates";
package writer_templates;
service ReportWriter{
rpc SendReportResult(ReportResultRepeated)returns(Empty);
}
message ReportResultRepeated{
repeated ReportResult data= 1;
}
message ReportResult {
string Name = 1;
string API = 2;
int32 Count = 3;
int64 Average = 4;
int64 Max = 5;
int64 Min = 6;
int32 QPS = 7;
int32 Error = 8;
string LastErrorMessage = 9;
}
message Empty {
}

View File

@ -0,0 +1,109 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.3
// source: writer_templates/writer.proto
package writer_templates
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
ReportWriter_SendReportResult_FullMethodName = "/writer_templates.ReportWriter/SendReportResult"
)
// ReportWriterClient is the client API for ReportWriter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ReportWriterClient interface {
SendReportResult(ctx context.Context, in *ReportResultRepeated, opts ...grpc.CallOption) (*Empty, error)
}
type reportWriterClient struct {
cc grpc.ClientConnInterface
}
func NewReportWriterClient(cc grpc.ClientConnInterface) ReportWriterClient {
return &reportWriterClient{cc}
}
func (c *reportWriterClient) SendReportResult(ctx context.Context, in *ReportResultRepeated, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, ReportWriter_SendReportResult_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ReportWriterServer is the server API for ReportWriter service.
// All implementations must embed UnimplementedReportWriterServer
// for forward compatibility
type ReportWriterServer interface {
SendReportResult(context.Context, *ReportResultRepeated) (*Empty, error)
mustEmbedUnimplementedReportWriterServer()
}
// UnimplementedReportWriterServer must be embedded to have forward compatible implementations.
type UnimplementedReportWriterServer struct {
}
func (UnimplementedReportWriterServer) SendReportResult(context.Context, *ReportResultRepeated) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendReportResult not implemented")
}
func (UnimplementedReportWriterServer) mustEmbedUnimplementedReportWriterServer() {}
// UnsafeReportWriterServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ReportWriterServer will
// result in compilation errors.
type UnsafeReportWriterServer interface {
mustEmbedUnimplementedReportWriterServer()
}
func RegisterReportWriterServer(s grpc.ServiceRegistrar, srv ReportWriterServer) {
s.RegisterService(&ReportWriter_ServiceDesc, srv)
}
func _ReportWriter_SendReportResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReportResultRepeated)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReportWriterServer).SendReportResult(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ReportWriter_SendReportResult_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReportWriterServer).SendReportResult(ctx, req.(*ReportResultRepeated))
}
return interceptor(ctx, in, info, handler)
}
// ReportWriter_ServiceDesc is the grpc.ServiceDesc for ReportWriter service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ReportWriter_ServiceDesc = grpc.ServiceDesc{
ServiceName: "writer_templates.ReportWriter",
HandlerType: (*ReportWriterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SendReportResult",
Handler: _ReportWriter_SendReportResult_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "writer_templates/writer.proto",
}