Skip to content

Commit

Permalink
Next iteration of automatic code generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Noctunus committed Oct 1, 2024
1 parent 37989c7 commit b42adfb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 77 deletions.
28 changes: 0 additions & 28 deletions internal/messaging/clients/ping_client_example.go

This file was deleted.

24 changes: 0 additions & 24 deletions internal/messaging/clients/ping_client_method_example.go

This file was deleted.

127 changes: 110 additions & 17 deletions scripts/generateGRPCServiceHandlers.sh
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
#!/bin/bash

function generate() {
TEMPLATES_DIR="templates"
CLIENT_TEMPLATE="${TEMPLATES_DIR}/client.go.tpl"
CLIENT_METHOD_TEMPLATE="${TEMPLATES_DIR}/client_method.go.tpl"
SERVER_TEMPLATE="${TEMPLATES_DIR}/server.go.tpl"
SERVER_METHOD_TEMPLATE="${TEMPLATES_DIR}/server_method.go.tpl"

GEN_OUTPATH_CLIENT="internal/messaging/clients"
GEN_OUTPATH_SERVER="internal/rpc/server"

function generate_with_templates() {
FQPN=$1
SERVICE=$2
PACKAGE=$3
TYPE=$4
GRPC_PACKAGE=$3
TYPE_PACKAGE=$4
local -n _METHODS=$5
local -n _INPUTS=$6
local -n _OUTPUTS=$7
GRPC_INCLUDE=$8
PROTO_INCLUDE=$9
VERSION=${10}
GENERATOR=${11}

echo "Generating with parameters:"
echo "FQPN : $FQPN"
echo "SERVICE : $SERVICE"
echo "PACKAGE : $PACKAGE"
echo "TYPE : $TYPE"
echo "FQPN : $FQPN"
echo "SERVICE : $SERVICE"
echo "GRPC_PACKAGE : $GRPC_PACKAGE"
echo "TYPE_PACKAGE : $TYPE_PACKAGE"
echo "VERSION : $VERSION"
for num in `seq 0 $(( ${#_METHODS[@]} - 1 ))` ; do
echo "METHOD[$num]: ${_METHODS[$num]} ${_INPUTS[$num]} ${_OUTPUTS[$num]}"
# does each method has an independent file? If not how should the template work?
Expand All @@ -27,15 +39,89 @@ function generate() {
# From here on it's very easy in case it's just search replace
# Simply copy the template to the target directory
# And do some sed -i -e "s#{{.FQDN}}#$FQDN#g" -e "s#{{.SERVICE}}#$SERVICE#g" [...] $file
PARAM_REPLACE=""
PARAM_REPLACE+=" -e 's#{{.FQPN}}#$FQPN#g'"
PARAM_REPLACE+=" -e 's#{{.SERVICE}}#$SERVICE#g'"
PARAM_REPLACE+=" -e 's#{{.PACKAGE}}#$PACKAGE#g'"
PARAM_REPLACE+=" -e 's#{{.TYPE}}#$TYPE#g'"
PARAM_REPLACE+=" -e 's#{{.GRPC_INCLUDE}}#$GRPC_INCLUDE#g'"
PARAM_REPLACE+=" -e 's#{{.PROTO_INCLUDE}}#$PROTO_INCLUDE#g'"
GENERAL_PARAM_REPLACE=""
GENERAL_PARAM_REPLACE+=" -e s#{{FQPN}}#$FQPN#g"
GENERAL_PARAM_REPLACE+=" -e s#{{SERVICE}}#$SERVICE#g"
GENERAL_PARAM_REPLACE+=" -e s#{{GRPC_PACKAGE}}#$GRPC_PACKAGE#g"
GENERAL_PARAM_REPLACE+=" -e s#{{TYPE_PACKAGE}}#$TYPE_PACKAGE#g"
GENERAL_PARAM_REPLACE+=" -e s#{{GRPC_INC}}#$GRPC_INCLUDE#g"
GENERAL_PARAM_REPLACE+=" -e s#{{PROTO_INC}}#$PROTO_INCLUDE#g"
GENERAL_PARAM_REPLACE+=" -e s#{{VERSION}}#$VERSION#g"
GENERAL_PARAM_REPLACE+=" -e s#{{GENERATOR}}#$GENERATOR#g"

# Generate client
CLIENT_GEN_FILE="${GEN_OUTPATH_CLIENT}/${TYPE_PACKAGE}_client.go"
echo "Generating client: $CLIENT_GEN_FILE"
cp $CLIENT_TEMPLATE $CLIENT_GEN_FILE
sed -i $GENERAL_PARAM_REPLACE $CLIENT_GEN_FILE
sed -i -e "s#{{TEMPLATE}}#$CLIENT_TEMPLATE#g" $CLIENT_GEN_FILE

# Generate client methods
for num in `seq 0 $(( ${#_METHODS[@]} - 1 ))` ; do
METHOD=${_METHODS[$num]}
INPUT=${_INPUTS[$num]}
OUTPUT=${_OUTPUTS[$num]}
METHOD_PARAM_REPLACE=""
METHOD_PARAM_REPLACE+=" -e s#{{METHOD}}#$METHOD#g"
METHOD_PARAM_REPLACE+=" -e s#{{REQUEST}}#$INPUT#g"
METHOD_PARAM_REPLACE+=" -e s#{{RESPONSE}}#$OUTPUT#g"
METHOD_GEN_FILE="${GEN_OUTPATH_CLIENT}/${TYPE_PACKAGE}_${METHOD}_method.go"
echo "Generating client method: $METHOD_GEN_FILE"
cp $CLIENT_METHOD_TEMPLATE $METHOD_GEN_FILE
sed -i $GENERAL_PARAM_REPLACE $METHOD_GEN_FILE
sed -i $METHOD_PARAM_REPLACE $METHOD_GEN_FILE
sed -i -e "s#{{TEMPLATE}}#$CLIENT_METHOD_TEMPLATE#g" $METHOD_GEN_FILE
done

# Generate server
SERVER_GEN_FILE="${GEN_OUTPATH_SERVER}/${TYPE_PACKAGE}_server.go"
echo "Generating server: $SERVER_GEN_FILE"
cp $SERVER_TEMPLATE $SERVER_GEN_FILE
sed -i $GENERAL_PARAM_REPLACE $SERVER_GEN_FILE
sed -i -e "s#{{TEMPLATE}}#$SERVER_TEMPLATE#g" $SERVER_GEN_FILE

# Generate server methods
for num in `seq 0 $(( ${#_METHODS[@]} - 1 ))` ; do
METHOD=${_METHODS[$num]}
INPUT=${_INPUTS[$num]}
OUTPUT=${_OUTPUTS[$num]}
METHOD_PARAM_REPLACE=""
METHOD_PARAM_REPLACE+=" -e s#{{METHOD}}#$METHOD#g"
METHOD_PARAM_REPLACE+=" -e s#{{REQUEST}}#$INPUT#g"
METHOD_PARAM_REPLACE+=" -e s#{{RESPONSE}}#$OUTPUT#g"
METHOD_GEN_FILE="${GEN_OUTPATH_SERVER}/${TYPE_PACKAGE}_${METHOD}_method.go"
echo "Generating server method: $METHOD_GEN_FILE"
cp $SERVER_METHOD_TEMPLATE $METHOD_GEN_FILE
sed -i $GENERAL_PARAM_REPLACE $METHOD_GEN_FILE
sed -i $METHOD_PARAM_REPLACE $METHOD_GEN_FILE
sed -i -e "s#{{TEMPLATE}}#$SERVER_METHOD_TEMPLATE#g" $METHOD_GEN_FILE
done

## TODO: When done generating also run:
## gofumpt -l -w <generated_files>
}

REGISTER_SERVICES_FILE="internal/rpc/server/register_services.go"

function generate_register_services() {
OUTFILE=$1
local -n _SERVICES=$2

echo "📝 Registering services in $REGISTER_SERVICES_FILE"

echo "// Code generated by '{{GENERATOR}}'. DO NOT EDIT." > $OUTFILE
echo >> $OUTFILE
echo "package server" >> $OUTFILE
echo >> $OUTFILE
echo "func (s *server) registerServices() {" >> $OUTFILE
for service in ${_SERVICES[@]} ; do
echo " register${service}Server(s.grpcServer, s)" >> $OUTFILE
done
echo "}" >> $OUTFILE
}

SCRIPT=$0

BUF_SDK_BASE="buf.build/gen/go/chain4travel/camino-messenger-protocol"
BUF_SDK_URL_GO="${BUF_SDK_BASE}/grpc/go"
echo "⌛ Downloading SDK from $BUF_SDK_URL_GO"
Expand Down Expand Up @@ -73,6 +159,7 @@ SDK_PATH="${GO_PATH}/pkg/mod/buf.build/gen/go/chain4travel/camino-messenger-prot
echo "SDK_PATH: $SDK_PATH"

FILTER=$1
declare -a SERVICES_TO_REGISTER=()

while read file ; do
if [ ! -z "$FILTER" ] ; then
Expand Down Expand Up @@ -122,11 +209,17 @@ while read file ; do
declare -a INCLUDES=()
# First the protocolbuffers
SUFFIX=$(echo ${FQPN%.*} | tr "." "/")
GRPC_INCLUDE="${BUF_SDK_BASE}/protocolbuffers/go/${SUFFIX}"
PROTO_INCLUDE="${BUF_SDK_BASE}/protocolbuffers/go/${SUFFIX}"
# Now the grpc which is quite similar by just adding the package name at the end
PROTO_INCLUDE="${BUF_SDK_BASE}/grpc/go/${SUFFIX}/${PACKAGE}"
GRPC_INCLUDE="${BUF_SDK_BASE}/grpc/go/${SUFFIX}/${PACKAGE}"

VERSION=$(echo "$FQPN" | grep -oP "\.v[0-9]+\." | cut -d"." -f2 )

generate "$FQPN" "$SERVICE" "$PACKAGE" "$TYPE" METHODS INPUTS OUTPUTS $GRPC_INCLUDE $PROTO_INCLUDE
generate_with_templates "$FQPN" "$SERVICE" "$PACKAGE" "$TYPE" METHODS INPUTS OUTPUTS $GRPC_INCLUDE $PROTO_INCLUDE ${VERSION:1} $SCRIPT
SERVICES_TO_REGISTER+=("${SERVICE}V${VERSION}")

echo
done < <(find "$SDK_PATH/cmp/services/" -name "*_grpc.pb.go")

generate_register_services "$REGISTER_SERVICES_FILE" SERVICES_TO_REGISTER

10 changes: 5 additions & 5 deletions templates/client.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ import (
)

const (
{{SERVICE}}V{{VERSION}} = {{FQPN}}
{{SERVICE}}V{{VERSION}} = "{{FQPN}}"
{{SERVICE}}V{{VERSION}}Request types.MessageType = types.MessageType({{SERVICE}}V{{VERSION}} + ".Request")
{{SERVICE}}V{{VERSION}}Response types.MessageType = types.MessageType({{SERVICE}}V{{VERSION}} + ".Response")
)

var _ Client = (*{{SERVICE}}Client)(nil)
var _ Client = (*{{SERVICE}}V{{VERSION}}Client)(nil)

func New{{SERVICE}}V{{VERSION}}(grpcCon *grpc.ClientConn) *{{SERVICE}}Client {
func New{{SERVICE}}V{{VERSION}}(grpcCon *grpc.ClientConn) *{{SERVICE}}V{{VERSION}}Client {
client := {{GRPC_PACKAGE}}.New{{SERVICE}}Client(grpcCon)
return &{{SERVICE}}Client{client: &client}
return &{{SERVICE}}V{{VERSION}}Client{client: &client}
}

type {{SERVICE}}Client struct {
type {{SERVICE}}V{{VERSION}}Client struct {
client *{{GRPC_PACKAGE}}.{{SERVICE}}Client
}
6 changes: 3 additions & 3 deletions templates/client_method.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
)

func (s {{SERVICE}}Client) Call(ctx context.Context, requestIntf protoreflect.ProtoMessage, opts ...grpc.CallOption) (protoreflect.ProtoMessage, types.MessageType, error) {
request, ok := requestIntf.(*{{TYPE_PACKAGE}}.{{REQUEST}}))
func (s {{SERVICE}}V{{VERSION}}Client) Call(ctx context.Context, requestIntf protoreflect.ProtoMessage, opts ...grpc.CallOption) (protoreflect.ProtoMessage, types.MessageType, error) {
request, ok := requestIntf.(*{{TYPE_PACKAGE}}.{{REQUEST}})
if !ok {
return nil, {{SERVICE}}V{{VERSION}}Response, fmt.Errorf("invalid request type")
}
response, err := (*s.client).{{METHOD}})(ctx, request, opts...)
response, err := (*s.client).{{METHOD}}(ctx, request, opts...)
return response, {{SERVICE}}V{{VERSION}}Response, err
}

0 comments on commit b42adfb

Please sign in to comment.