github.com/cloudwego/kitex@v0.9.0/pkg/generic/proto/json.go (about)

     1  /*
     2   * Copyright 2023 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package proto
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/cloudwego/dynamicgo/conv"
    24  	dconvj2p "github.com/cloudwego/dynamicgo/conv/j2p"
    25  	dconvp2j "github.com/cloudwego/dynamicgo/conv/p2j"
    26  	dproto "github.com/cloudwego/dynamicgo/proto"
    27  
    28  	"github.com/cloudwego/kitex/pkg/remote/codec/perrors"
    29  	"github.com/cloudwego/kitex/pkg/utils"
    30  )
    31  
    32  // NewWriteJSON build WriteJSON according to ServiceDescriptor
    33  func NewWriteJSON(svc *dproto.ServiceDescriptor, method string, isClient bool, convOpts *conv.Options) (*WriteJSON, error) {
    34  	fnDsc := svc.LookupMethodByName(method)
    35  	if fnDsc == nil {
    36  		return nil, fmt.Errorf("missing method: %s in service: %s", method, svc.Name())
    37  	}
    38  
    39  	// from the proto.ServiceDescriptor, get the TypeDescriptor
    40  	typeDescriptor := fnDsc.Input()
    41  	if !isClient {
    42  		typeDescriptor = fnDsc.Output()
    43  	}
    44  
    45  	ws := &WriteJSON{
    46  		dynamicgoConvOpts: convOpts,
    47  		dynamicgoTypeDsc:  typeDescriptor,
    48  		isClient:          isClient,
    49  	}
    50  	return ws, nil
    51  }
    52  
    53  // WriteJSON implement of MessageWriter
    54  type WriteJSON struct {
    55  	dynamicgoConvOpts *conv.Options
    56  	dynamicgoTypeDsc  *dproto.TypeDescriptor
    57  	isClient          bool
    58  }
    59  
    60  var _ MessageWriter = (*WriteJSON)(nil)
    61  
    62  // Write converts msg to protobuf wire format and returns an output bytebuffer
    63  func (m *WriteJSON) Write(ctx context.Context, msg interface{}) (interface{}, error) {
    64  	var s string
    65  	if msg == nil {
    66  		s = "{}"
    67  	} else {
    68  		// msg is string
    69  		var ok bool
    70  		s, ok = msg.(string)
    71  		if !ok {
    72  			return nil, perrors.NewProtocolErrorWithType(perrors.InvalidData, "decode msg failed, is not string")
    73  		}
    74  	}
    75  
    76  	cv := dconvj2p.NewBinaryConv(*m.dynamicgoConvOpts)
    77  
    78  	// get protobuf-encode bytes
    79  	actualMsgBuf, err := cv.Do(ctx, m.dynamicgoTypeDsc, utils.StringToSliceByte(s))
    80  	if err != nil {
    81  		return nil, perrors.NewProtocolErrorWithErrMsg(err, fmt.Sprintf("protobuf marshal message failed: %s", err.Error()))
    82  	}
    83  	return actualMsgBuf, nil
    84  }
    85  
    86  // NewReadJSON build ReadJSON according to ServiceDescriptor
    87  func NewReadJSON(svc *dproto.ServiceDescriptor, isClient bool, convOpts *conv.Options) (*ReadJSON, error) {
    88  	// extract svc to be used to convert later
    89  	return &ReadJSON{
    90  		dynamicgoConvOpts: convOpts,
    91  		dynamicgoSvcDsc:   svc,
    92  		isClient:          isClient,
    93  	}, nil
    94  }
    95  
    96  // ReadJSON implement of MessageReaderWithMethod
    97  type ReadJSON struct {
    98  	dynamicgoConvOpts *conv.Options
    99  	dynamicgoSvcDsc   *dproto.ServiceDescriptor
   100  	isClient          bool
   101  }
   102  
   103  var _ MessageReader = (*ReadJSON)(nil)
   104  
   105  // Read reads data from actualMsgBuf and convert to json string
   106  func (m *ReadJSON) Read(ctx context.Context, method string, actualMsgBuf []byte) (interface{}, error) {
   107  	// create dynamic message here, once method string has been extracted
   108  	fnDsc := m.dynamicgoSvcDsc.LookupMethodByName(method)
   109  	if fnDsc == nil {
   110  		return nil, fmt.Errorf("missing method: %s in service: %s", method, m.dynamicgoSvcDsc.Name())
   111  	}
   112  
   113  	// from the dproto.ServiceDescriptor, get the TypeDescriptor
   114  	typeDescriptor := fnDsc.Output()
   115  	if !m.isClient {
   116  		typeDescriptor = fnDsc.Input()
   117  	}
   118  
   119  	cv := dconvp2j.NewBinaryConv(*m.dynamicgoConvOpts)
   120  	out, err := cv.Do(context.Background(), typeDescriptor, actualMsgBuf)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	return string(out), nil
   126  }