snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
test / Lint (push) Failing after 3s
test / Test with Go 1.26.8 (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:17:21 +05:00
commit b352a9a30d
472 changed files with 108375 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
package main
func generateHandlersConsumer() {
generateToFile(
headerHandlersConsuming, templateFuncHandlersConsuming, "internal/api/consuming_gen.go",
nil,
[]string{
"Publish", "Broadcast", "Subscribe", "Unsubscribe", "Disconnect", "HistoryRemove", "Refresh",
},
)
}
var headerHandlersConsuming = `// Code generated by internal/gen/api/main.go. DO NOT EDIT.
package api
import (
"context"
. "github.com/centrifugal/centrifugo/v6/internal/apiproto"
"github.com/centrifugal/centrifugo/v6/internal/metrics"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
`
var templateFuncHandlersConsuming = `
func (h *ConsumingHandler) handle{{ .RequestCapitalized }}(ctx context.Context, data []byte) (*{{ .RequestCapitalized }}Result, error) {
req, err := requestDecoder.Decode{{ .RequestCapitalized }}(data)
if err != nil {
return nil, ErrInvalidData
}
resp := h.api.{{ .RequestCapitalized }}(ctx, req)
if h.config.UseOpenTelemetry && resp.Error != nil {
span := trace.SpanFromContext(ctx)
span.SetStatus(codes.Error, resp.Error.Error())
}
if resp.Error != nil {
metrics.IncAPIError(h.api.config.Protocol, "{{ .RequestSnake }}", resp.Error.Code)
return nil, resp.Error
}
return resp.Result, nil
}
`

View File

@@ -0,0 +1,46 @@
package main
func generateHandlersGRPC() {
generateToFile(headerHandlersGRPC, templateFuncHandlersGRPC, "internal/api/grpc_handler_gen.go", nil, nil)
}
var headerHandlersGRPC = `// Code generated by internal/gen/api/main.go. DO NOT EDIT.
package api
import (
"context"
. "github.com/centrifugal/centrifugo/v6/internal/apiproto"
"github.com/centrifugal/centrifugo/v6/internal/metrics"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/status"
)
`
var templateFuncHandlersGRPC = `
// {{ .RequestCapitalized }} ...
func (s *grpcAPIService) {{ .RequestCapitalized }}(ctx context.Context, req *{{ .RequestCapitalized }}Request) (*{{ .RequestCapitalized }}Response, error) {
resp := s.api.{{ .RequestCapitalized }}(ctx, req)
{{- if ne .RequestCapitalized "Batch" }}
if s.config.UseOpenTelemetry && resp.Error != nil {
span := trace.SpanFromContext(ctx)
span.SetStatus(codes.Error, resp.Error.Error())
}
if resp.Error != nil && s.useTransportErrorMode(ctx) {
metrics.IncAPIError(s.api.config.Protocol, "{{ .RequestSnake }}", resp.Error.Code)
statusCode := MapErrorToGRPCCode(resp.Error)
transportError, _ := status.New(statusCode, resp.Error.Message).WithDetails(resp.Error)
return nil, transportError.Err()
}
{{- end}}
{{- if ne .RequestCapitalized "Batch" }}
if resp.Error != nil {
metrics.IncAPIError(s.api.config.Protocol, "{{ .RequestSnake }}", resp.Error.Code)
}
{{- end}}
return resp, nil
}
`

View File

@@ -0,0 +1,71 @@
package main
func generateHandlersHTTP() {
generateToFile(headerHandlersHTTP, templateFuncHandlersHTTP, "internal/api/handler_gen.go", nil, nil)
}
var headerHandlersHTTP = `// Code generated by internal/gen/api/main.go. DO NOT EDIT.
package api
import (
"io"
"net/http"
. "github.com/centrifugal/centrifugo/v6/internal/apiproto"
"github.com/centrifugal/centrifugo/v6/internal/metrics"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
`
var templateFuncHandlersHTTP = `
func (s *Handler) handle{{ .RequestCapitalized }}(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
metrics.IncAPIErrorStringCode(s.api.config.Protocol, "{{ .RequestSnake }}", "read_body")
s.handleReadDataError(r, w, err)
return
}
req, err := requestDecoder.Decode{{ .RequestCapitalized }}(data)
if err != nil {
metrics.IncAPIErrorStringCode(s.api.config.Protocol, "{{ .RequestSnake }}", "unmarshal")
s.handleUnmarshalError(r, w, err)
return
}
resp := s.api.{{ .RequestCapitalized }}(r.Context(), req)
{{- if ne .RequestCapitalized "Batch" }}
if s.config.UseOpenTelemetry && resp.Error != nil {
span := trace.SpanFromContext(r.Context())
span.SetStatus(codes.Error, resp.Error.Error())
}
if resp.Error != nil && s.useTransportErrorMode(r) {
metrics.IncAPIError(s.api.config.Protocol, "{{ .RequestSnake }}", resp.Error.Code)
statusCode := MapErrorToHTTPCode(resp.Error)
data, _ = EncodeError(resp.Error)
s.writeJsonCustomStatus(w, statusCode, data)
return
}
{{- end}}
data, err = responseEncoder.Encode{{ .RequestCapitalized }}(resp)
if err != nil {
metrics.IncAPIErrorStringCode(s.api.config.Protocol, "{{ .RequestSnake }}", "marshal")
s.handleMarshalError(r, w, err)
return
}
{{- if ne .RequestCapitalized "Batch" }}
if resp.Error != nil {
metrics.IncAPIError(s.api.config.Protocol, "{{ .RequestSnake }}", resp.Error.Code)
}
{{- end}}
s.writeJson(w, data)
}
`

View File

@@ -0,0 +1,34 @@
package main
func generateRequestDecoder() {
generateToFile(headerRequestDecoder, templateFuncRequestDecoder, "internal/apiproto/decode_request_gen.go", nil, nil)
}
var headerRequestDecoder = `// Code generated by internal/gen/api/main.go. DO NOT EDIT.
package apiproto
import "encoding/json"
var _ RequestDecoder = (*JSONRequestDecoder)(nil)
// JSONRequestDecoder ...
type JSONRequestDecoder struct{}
// NewJSONRequestDecoder ...
func NewJSONRequestDecoder() *JSONRequestDecoder {
return &JSONRequestDecoder{}
}
`
var templateFuncRequestDecoder = `
// Decode{{ .RequestCapitalized }} ...
func (d *JSONRequestDecoder) Decode{{ .RequestCapitalized }}(data []byte) (*{{ .RequestCapitalized }}Request, error) {
var p {{ .RequestCapitalized }}Request
err := json.Unmarshal(data, &p)
if err != nil {
return nil, err
}
return &p, nil
}
`

View File

@@ -0,0 +1,26 @@
package main
func generateResponseEncoder() {
generateToFile(headerResponseEncoder, templateFuncResponseEncoder, "internal/apiproto/encode_response_gen.go", nil, nil)
}
var headerResponseEncoder = `// Code generated by internal/gen/api/main.go. DO NOT EDIT.
package apiproto
import "encoding/json"
// JSONResponseEncoder ...
type JSONResponseEncoder struct{}
func NewJSONResponseEncoder() *JSONResponseEncoder {
return &JSONResponseEncoder{}
}
`
var templateFuncResponseEncoder = `
// Encode{{ .RequestCapitalized }} ...
func (e *JSONResponseEncoder) Encode{{ .RequestCapitalized }}(response *{{ .RequestCapitalized }}Response) ([]byte, error) {
return json.Marshal(response)
}
`

View File

@@ -0,0 +1,29 @@
package main
func generateResultEncoder() {
generateToFile(headerResultEncoder, templateFuncResultEncoder, "internal/apiproto/encode_result_gen.go", []string{"Batch"}, nil)
}
var headerResultEncoder = `// Code generated by internal/gen/api/main.go. DO NOT EDIT.
package apiproto
import "encoding/json"
var _ ResultEncoder = (*JSONResultEncoder)(nil)
// JSONResultEncoder ...
type JSONResultEncoder struct{}
// NewJSONResultEncoder ...
func NewJSONResultEncoder() *JSONResultEncoder {
return &JSONResultEncoder{}
}
`
var templateFuncResultEncoder = `
// Encode{{ .RequestCapitalized }} ...
func (e *JSONResultEncoder) Encode{{ .RequestCapitalized }}(res *{{ .RequestCapitalized }}Result) ([]byte, error) {
return json.Marshal(res)
}
`

2
internal/gen/api/generate.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
go run internal/gen/api/*.go

59
internal/gen/api/main.go Normal file
View File

@@ -0,0 +1,59 @@
package main
import (
"bytes"
"os"
"slices"
"strings"
"text/template"
"github.com/centrifugal/centrifugo/v6/internal/gen"
)
func main() {
generateHandlersHTTP()
generateHandlersGRPC()
generateHandlersConsumer()
generateRequestDecoder()
generateResponseEncoder()
generateResultEncoder()
}
type TemplateData struct {
RequestCapitalized string
RequestLower string
RequestSnake string
}
func generateToFile(header, funcTmpl, outFile string, excludeRequests []string, includeRequests []string) {
tmpl := template.Must(template.New("").Parse(funcTmpl))
var buf bytes.Buffer
buf.WriteString(header)
for _, req := range gen.Requests {
if slices.Contains(excludeRequests, req) {
continue
}
if len(includeRequests) > 0 && !slices.Contains(includeRequests, req) {
continue
}
err := tmpl.Execute(&buf, TemplateData{
RequestCapitalized: req,
RequestLower: strings.ToLower(req),
RequestSnake: gen.CamelToSnake(req),
})
if err != nil {
panic(err)
}
}
file, err := os.Create(outFile) //nolint:gosec // Used only for code generation.
if err != nil {
panic(err)
}
defer func() {
_ = file.Close()
}()
_, _ = buf.WriteTo(file)
}

25
internal/gen/types.go Normal file
View File

@@ -0,0 +1,25 @@
package gen
var Requests = []string{
"Batch",
"Publish",
"Broadcast",
"Subscribe",
"Unsubscribe",
"Disconnect",
"Presence",
"PresenceStats",
"History",
"HistoryRemove",
"Info",
"RPC",
"Refresh",
"Channels",
"MapPublish",
"MapRemove",
"MapReadState",
"MapReadStream",
"MapStats",
"MapClear",
"SharedPollPublish",
}

12
internal/gen/utils.go Normal file
View File

@@ -0,0 +1,12 @@
package gen
import (
"regexp"
"strings"
)
func CamelToSnake(s string) string {
re := regexp.MustCompile("([a-z0-9])([A-Z])")
snake := re.ReplaceAllString(s, "${1}_${2}")
return strings.ToLower(snake)
}

View File

@@ -0,0 +1,28 @@
package gen
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCamelToSnake(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"CamelCaseString", "camel_case_string"},
{"TestID123", "test_id123"},
{"NoChange", "no_change"},
{"RPC", "rpc"},
{"", ""},
{"already_snake_case", "already_snake_case"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := CamelToSnake(tt.input)
require.Equal(t, tt.expected, result, "Unexpected result for input: %s", tt.input)
})
}
}