github.heygears.com/openimsdk/tools@v0.0.49/a2r/api2rpc.go (about)

     1  // Copyright © 2023 OpenIM. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package a2r
    16  
    17  import (
    18  	"context"
    19  	"github.com/openimsdk/tools/checker"
    20  	"io"
    21  	"net/http"
    22  
    23  	"github.com/gin-gonic/gin"
    24  	"github.com/gin-gonic/gin/binding"
    25  	"github.com/openimsdk/tools/apiresp"
    26  	"github.com/openimsdk/tools/errs"
    27  	"github.com/openimsdk/tools/utils/jsonutil"
    28  	"google.golang.org/grpc"
    29  )
    30  
    31  type Option[A, B any] struct {
    32  	// BindAfter is called after the req is bind from ctx.
    33  	BindAfter func(*A) error
    34  	// RespAfter is called after the resp is return from rpc.
    35  	RespAfter func(*B) error
    36  }
    37  
    38  func Call[A, B, C any](rpc func(client C, ctx context.Context, req *A, options ...grpc.CallOption) (*B, error), client C, c *gin.Context, opts ...*Option[A, B]) {
    39  	req, err := ParseRequestNotCheck[A](c)
    40  	if err != nil {
    41  		apiresp.GinError(c, err)
    42  		return
    43  	}
    44  	for _, opt := range opts {
    45  		if opt.BindAfter == nil {
    46  			continue
    47  		}
    48  		if err := opt.BindAfter(req); err != nil {
    49  			apiresp.GinError(c, err) // args option error
    50  			return
    51  		}
    52  	}
    53  	if err := checker.Validate(req); err != nil {
    54  		apiresp.GinError(c, err) // args option error
    55  		return
    56  	}
    57  	resp, err := rpc(client, c, req)
    58  	if err != nil {
    59  		apiresp.GinError(c, err) // rpc call failed
    60  		return
    61  	}
    62  	for _, opt := range opts {
    63  		if opt.RespAfter == nil {
    64  			continue
    65  		}
    66  		if err := opt.RespAfter(resp); err != nil {
    67  			apiresp.GinError(c, err) // resp option error
    68  			return
    69  		}
    70  	}
    71  	apiresp.GinSuccess(c, resp) // rpc call success
    72  }
    73  
    74  func ParseRequestNotCheck[T any](c *gin.Context) (*T, error) {
    75  	var req T
    76  	if err := c.ShouldBindWith(&req, jsonBind); err != nil {
    77  		return nil, errs.NewCodeError(errs.ArgsError, err.Error())
    78  	}
    79  	return &req, nil
    80  }
    81  
    82  func ParseRequest[T any](c *gin.Context) (*T, error) {
    83  	req, err := ParseRequestNotCheck[T](c)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	if err := checker.Validate(&req); err != nil {
    88  		return nil, err
    89  	}
    90  	return req, nil
    91  }
    92  
    93  type jsonBinding struct{}
    94  
    95  var jsonBind binding.Binding = jsonBinding{}
    96  
    97  func (jsonBinding) Name() string {
    98  	return "json"
    99  }
   100  
   101  func (b jsonBinding) Bind(req *http.Request, obj any) error {
   102  	if req == nil || req.Body == nil {
   103  		return errs.New("invalid request").Wrap()
   104  	}
   105  	body, err := io.ReadAll(req.Body)
   106  	if err != nil {
   107  		return errs.WrapMsg(err, "read request body failed", "method", req.Method, "url", req.URL.String())
   108  	}
   109  	return errs.Wrap(b.BindBody(body, obj))
   110  }
   111  
   112  func (jsonBinding) BindBody(body []byte, obj any) error {
   113  	if err := jsonutil.JsonUnmarshal(body, obj); err != nil {
   114  		return err
   115  	}
   116  	if binding.Validator == nil {
   117  		return nil
   118  	}
   119  	return errs.Wrap(binding.Validator.ValidateStruct(obj))
   120  }