From ec945970eec13a1fc5df35e7eba9131d384ad0cc Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 07:51:21 +0000 Subject: [PATCH] docs(api): add AX usage examples Co-Authored-By: Virgil --- api.go | 20 +++++++++++ authentik.go | 24 +++++++++++++ bridge.go | 36 ++++++++++++++++++++ client.go | 4 +++ graphql.go | 12 +++++++ group.go | 16 +++++++++ i18n.go | 20 +++++++++++ middleware.go | 12 +++++++ openapi.go | 4 +++ options.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ response.go | 8 +++++ spec_registry.go | 6 ++++ sse.go | 4 +++ transport.go | 8 +++++ 14 files changed, 262 insertions(+) diff --git a/api.go b/api.go index fc5963d..f547f7d 100644 --- a/api.go +++ b/api.go @@ -91,11 +91,21 @@ func (e *Engine) Addr() string { } // Groups returns a copy of all registered route groups. +// +// Example: +// +// groups := engine.Groups() func (e *Engine) Groups() []RouteGroup { return slices.Clone(e.groups) } // GroupsIter returns an iterator over all registered route groups. +// +// Example: +// +// for group := range engine.GroupsIter() { +// _ = group +// } func (e *Engine) GroupsIter() iter.Seq[RouteGroup] { groups := slices.Clone(e.groups) return slices.Values(groups) @@ -115,6 +125,10 @@ func (e *Engine) Register(group RouteGroup) { // Channels returns all WebSocket channel names from registered StreamGroups. // Groups that do not implement StreamGroup are silently skipped. +// +// Example: +// +// channels := engine.Channels() func (e *Engine) Channels() []string { var channels []string for _, g := range e.groups { @@ -126,6 +140,12 @@ func (e *Engine) Channels() []string { } // ChannelsIter returns an iterator over WebSocket channel names from registered StreamGroups. +// +// Example: +// +// for channel := range engine.ChannelsIter() { +// _ = channel +// } func (e *Engine) ChannelsIter() iter.Seq[string] { groups := slices.Clone(e.groups) return func(yield func(string) bool) { diff --git a/authentik.go b/authentik.go index f42aa38..40b1161 100644 --- a/authentik.go +++ b/authentik.go @@ -14,6 +14,10 @@ import ( ) // AuthentikConfig holds settings for the Authentik forward-auth integration. +// +// Example: +// +// cfg := api.AuthentikConfig{Issuer: "https://auth.example.com/", ClientID: "core-api"} type AuthentikConfig struct { // Issuer is the OIDC issuer URL (e.g. https://auth.example.com/application/o/my-app/). Issuer string @@ -32,6 +36,10 @@ type AuthentikConfig struct { // AuthentikUser represents an authenticated user extracted from Authentik // forward-auth headers or a validated JWT. +// +// Example: +// +// user := &api.AuthentikUser{Username: "alice", Groups: []string{"admins"}} type AuthentikUser struct { Username string `json:"username"` Email string `json:"email"` @@ -43,6 +51,10 @@ type AuthentikUser struct { } // HasGroup reports whether the user belongs to the named group. +// +// Example: +// +// user.HasGroup("admins") func (u *AuthentikUser) HasGroup(group string) bool { return slices.Contains(u.Groups, group) } @@ -53,6 +65,10 @@ const authentikUserKey = "authentik_user" // GetUser retrieves the AuthentikUser from the Gin context. // Returns nil when no user has been set (unauthenticated request or // middleware not active). +// +// Example: +// +// user := api.GetUser(c) func GetUser(c *gin.Context) *AuthentikUser { val, exists := c.Get(authentikUserKey) if !exists { @@ -204,6 +220,10 @@ func authentikMiddleware(cfg AuthentikConfig, publicPaths func() []string) gin.H // RequireAuth is Gin middleware that rejects unauthenticated requests. // It checks for a user set by the Authentik middleware and returns 401 // when none is present. +// +// Example: +// +// r.GET("/private", api.RequireAuth(), handler) func RequireAuth() gin.HandlerFunc { return func(c *gin.Context) { if GetUser(c) == nil { @@ -218,6 +238,10 @@ func RequireAuth() gin.HandlerFunc { // RequireGroup is Gin middleware that rejects requests from users who do // not belong to the specified group. Returns 401 when no user is present // and 403 when the user lacks the required group membership. +// +// Example: +// +// r.GET("/admin", api.RequireGroup("admins"), handler) func RequireGroup(group string) gin.HandlerFunc { return func(c *gin.Context) { user := GetUser(c) diff --git a/bridge.go b/bridge.go index 4850e65..0b74e38 100644 --- a/bridge.go +++ b/bridge.go @@ -23,6 +23,10 @@ import ( ) // ToolDescriptor describes a tool that can be exposed as a REST endpoint. +// +// Example: +// +// desc := api.ToolDescriptor{Name: "ping", Description: "Ping the service"} type ToolDescriptor struct { Name string // Tool name, e.g. "file_read" (becomes POST path segment) Description string // Human-readable description @@ -33,6 +37,10 @@ type ToolDescriptor struct { // ToolBridge converts tool descriptors into REST endpoints and OpenAPI paths. // It implements both RouteGroup and DescribableGroup. +// +// Example: +// +// bridge := api.NewToolBridge("/mcp") type ToolBridge struct { basePath string name string @@ -75,12 +83,24 @@ func (b *ToolBridge) Add(desc ToolDescriptor, handler gin.HandlerFunc) { } // Name returns the bridge identifier. +// +// Example: +// +// name := bridge.Name() func (b *ToolBridge) Name() string { return b.name } // BasePath returns the URL prefix for all tool endpoints. +// +// Example: +// +// path := bridge.BasePath() func (b *ToolBridge) BasePath() string { return b.basePath } // RegisterRoutes mounts POST /{tool_name} for each registered tool. +// +// Example: +// +// bridge.RegisterRoutes(rg) func (b *ToolBridge) RegisterRoutes(rg *gin.RouterGroup) { for _, t := range b.tools { rg.POST("/"+t.descriptor.Name, t.handler) @@ -88,6 +108,10 @@ func (b *ToolBridge) RegisterRoutes(rg *gin.RouterGroup) { } // Describe returns OpenAPI route descriptions for all registered tools. +// +// Example: +// +// descs := bridge.Describe() func (b *ToolBridge) Describe() []RouteDescription { tools := b.snapshotTools() descs := make([]RouteDescription, 0, len(tools)) @@ -98,6 +122,12 @@ func (b *ToolBridge) Describe() []RouteDescription { } // DescribeIter returns an iterator over OpenAPI route descriptions for all registered tools. +// +// Example: +// +// for rd := range bridge.DescribeIter() { +// _ = rd +// } func (b *ToolBridge) DescribeIter() iter.Seq[RouteDescription] { tools := b.snapshotTools() return func(yield func(RouteDescription) bool) { @@ -124,6 +154,12 @@ func (b *ToolBridge) Tools() []ToolDescriptor { } // ToolsIter returns an iterator over all registered tool descriptors. +// +// Example: +// +// for desc := range bridge.ToolsIter() { +// _ = desc +// } func (b *ToolBridge) ToolsIter() iter.Seq[ToolDescriptor] { tools := b.snapshotTools() return func(yield func(ToolDescriptor) bool) { diff --git a/client.go b/client.go index 4ef503f..59922e3 100644 --- a/client.go +++ b/client.go @@ -56,6 +56,10 @@ type openAPIParameter struct { } // OpenAPIClientOption configures a runtime OpenAPI client. +// +// Example: +// +// client := api.NewOpenAPIClient(api.WithSpec("./openapi.yaml")) type OpenAPIClientOption func(*OpenAPIClient) // WithSpec sets the filesystem path to the OpenAPI document. diff --git a/graphql.go b/graphql.go index 636d5c6..1deff9d 100644 --- a/graphql.go +++ b/graphql.go @@ -23,9 +23,17 @@ type graphqlConfig struct { } // GraphQLOption configures a GraphQL endpoint. +// +// Example: +// +// opts := []api.GraphQLOption{api.WithPlayground(), api.WithGraphQLPath("/gql")} type GraphQLOption func(*graphqlConfig) // WithPlayground enables the GraphQL Playground UI at {path}/playground. +// +// Example: +// +// api.WithGraphQL(schema, api.WithPlayground()) func WithPlayground() GraphQLOption { return func(cfg *graphqlConfig) { cfg.playground = true @@ -34,6 +42,10 @@ func WithPlayground() GraphQLOption { // WithGraphQLPath sets a custom URL path for the GraphQL endpoint. // The default path is "/graphql". +// +// Example: +// +// api.WithGraphQL(schema, api.WithGraphQLPath("/gql")) func WithGraphQLPath(path string) GraphQLOption { return func(cfg *graphqlConfig) { cfg.path = normaliseGraphQLPath(path) diff --git a/group.go b/group.go index 00d8ddb..7ee8798 100644 --- a/group.go +++ b/group.go @@ -10,6 +10,10 @@ import ( // RouteGroup registers API routes onto a Gin router group. // Subsystems implement this interface to declare their endpoints. +// +// Example: +// +// var g api.RouteGroup = &myGroup{} type RouteGroup interface { // Name returns a human-readable identifier for the group. Name() string @@ -22,6 +26,10 @@ type RouteGroup interface { } // StreamGroup optionally declares WebSocket channels a subsystem publishes to. +// +// Example: +// +// var sg api.StreamGroup = &myStreamGroup{} type StreamGroup interface { // Channels returns the list of channel names this group streams on. Channels() []string @@ -30,6 +38,10 @@ type StreamGroup interface { // DescribableGroup extends RouteGroup with OpenAPI metadata. // RouteGroups that implement this will have their endpoints // included in the generated OpenAPI specification. +// +// Example: +// +// var dg api.DescribableGroup = &myDescribableGroup{} type DescribableGroup interface { RouteGroup // Describe returns endpoint descriptions for OpenAPI generation. @@ -38,6 +50,10 @@ type DescribableGroup interface { // DescribableGroupIter extends DescribableGroup with an iterator-based // description source for callers that want to avoid slice allocation. +// +// Example: +// +// var dg api.DescribableGroupIter = &myDescribableGroup{} type DescribableGroupIter interface { DescribableGroup // DescribeIter returns endpoint descriptions for OpenAPI generation. diff --git a/i18n.go b/i18n.go index 7b526ca..316a8c1 100644 --- a/i18n.go +++ b/i18n.go @@ -22,6 +22,14 @@ const i18nCatalogKey = "i18n.catalog" const i18nDefaultLocaleKey = "i18n.default_locale" // I18nConfig configures the internationalisation middleware. +// +// Example: +// +// cfg := api.I18nConfig{ +// DefaultLocale: "en", +// Supported: []string{"en", "fr"}, +// Messages: map[string]map[string]string{"fr": {"greeting": "Bonjour"}}, +// } type I18nConfig struct { // DefaultLocale is the fallback locale when the Accept-Language header // is absent or does not match any supported locale. Defaults to "en". @@ -43,6 +51,10 @@ type I18nConfig struct { // with quality weighting support. The detected locale is stored in the Gin // context and can be retrieved by handlers via GetLocale(). // +// Example: +// +// api.New(api.WithI18n(api.I18nConfig{Supported: []string{"en", "fr"}})) +// // If messages are configured, handlers can look up localised strings via // GetMessage(). This is a lightweight bridge — the go-i18n grammar engine // can replace the message map later. @@ -103,6 +115,10 @@ func i18nMiddleware(matcher language.Matcher, cfg I18nConfig) gin.HandlerFunc { // GetLocale returns the detected locale for the current request. // Returns "en" if the i18n middleware was not applied. +// +// Example: +// +// locale := api.GetLocale(c) func GetLocale(c *gin.Context) string { if v, ok := c.Get(i18nContextKey); ok { if s, ok := v.(string); ok { @@ -115,6 +131,10 @@ func GetLocale(c *gin.Context) string { // GetMessage looks up a localised message by key for the current request. // Returns the message string and true if found, or empty string and false // if the key does not exist or the i18n middleware was not applied. +// +// Example: +// +// msg, ok := api.GetMessage(c, "greeting") func GetMessage(c *gin.Context, key string) (string, bool) { if v, ok := c.Get(i18nMessagesKey); ok { if msgs, ok := v.(map[string]string); ok { diff --git a/middleware.go b/middleware.go index d555fa3..35e9120 100644 --- a/middleware.go +++ b/middleware.go @@ -110,6 +110,10 @@ func requestIDMiddleware() gin.HandlerFunc { // GetRequestID returns the request ID assigned by requestIDMiddleware. // Returns an empty string when the middleware was not applied. +// +// Example: +// +// id := api.GetRequestID(c) func GetRequestID(c *gin.Context) string { if v, ok := c.Get(requestIDContextKey); ok { if s, ok := v.(string); ok { @@ -121,6 +125,10 @@ func GetRequestID(c *gin.Context) string { // GetRequestDuration returns the elapsed time since requestIDMiddleware started // handling the request. Returns 0 when the middleware was not applied. +// +// Example: +// +// d := api.GetRequestDuration(c) func GetRequestDuration(c *gin.Context) time.Duration { if v, ok := c.Get(requestStartContextKey); ok { if started, ok := v.(time.Time); ok && !started.IsZero() { @@ -133,6 +141,10 @@ func GetRequestDuration(c *gin.Context) time.Duration { // GetRequestMeta returns request metadata collected by requestIDMiddleware. // The returned meta includes the request ID and elapsed duration when // available. It returns nil when neither value is available. +// +// Example: +// +// meta := api.GetRequestMeta(c) func GetRequestMeta(c *gin.Context) *Meta { meta := &Meta{} diff --git a/openapi.go b/openapi.go index 10f8c8b..f1f4660 100644 --- a/openapi.go +++ b/openapi.go @@ -191,6 +191,10 @@ func (sb *SpecBuilder) Build(groups []RouteGroup) ([]byte, error) { // BuildIter generates the complete OpenAPI 3.1 JSON spec from a route-group // iterator. The iterator is snapshotted before building so the result stays // stable even if the source changes during rendering. +// +// Example: +// +// data, err := (&api.SpecBuilder{Title: "Service"}).BuildIter(api.RegisteredSpecGroupsIter()) func (sb *SpecBuilder) BuildIter(groups iter.Seq[RouteGroup]) ([]byte, error) { return sb.Build(collectRouteGroups(groups)) } diff --git a/options.go b/options.go index 80952ed..ea7f4a6 100644 --- a/options.go +++ b/options.go @@ -27,6 +27,10 @@ import ( ) // Option configures an Engine during construction. +// +// Example: +// +// engine, _ := api.New(api.WithAddr(":8080")) type Option func(*Engine) // WithAddr sets the listen address for the server. @@ -42,6 +46,10 @@ func WithAddr(addr string) Option { // WithBearerAuth adds bearer token authentication middleware. // Requests to /health and the Swagger UI path are exempt. +// +// Example: +// +// api.New(api.WithBearerAuth("secret")) func WithBearerAuth(token string) Option { return func(e *Engine) { e.middlewares = append(e.middlewares, bearerAuthMiddleware(token, func() []string { @@ -56,6 +64,10 @@ func WithBearerAuth(token string) Option { // WithRequestID adds middleware that assigns an X-Request-ID to every response. // Client-provided IDs are preserved; otherwise a random hex ID is generated. +// +// Example: +// +// api.New(api.WithRequestID()) func WithRequestID() Option { return func(e *Engine) { e.middlewares = append(e.middlewares, requestIDMiddleware()) @@ -80,6 +92,10 @@ func WithResponseMeta() Option { // Pass "*" to allow all origins, or supply specific origin URLs. // Standard methods (GET, POST, PUT, PATCH, DELETE, OPTIONS) and common // headers (Authorization, Content-Type, X-Request-ID) are permitted. +// +// Example: +// +// api.New(api.WithCORS("*")) func WithCORS(allowOrigins ...string) Option { return func(e *Engine) { cfg := cors.Config{ @@ -100,6 +116,10 @@ func WithCORS(allowOrigins ...string) Option { } // WithMiddleware appends arbitrary Gin middleware to the engine. +// +// Example: +// +// api.New(api.WithMiddleware(loggingMiddleware)) func WithMiddleware(mw ...gin.HandlerFunc) Option { return func(e *Engine) { e.middlewares = append(e.middlewares, mw...) @@ -109,6 +129,10 @@ func WithMiddleware(mw ...gin.HandlerFunc) Option { // WithStatic serves static files from the given root directory at urlPrefix. // Directory listing is disabled; only individual files are served. // Internally this uses gin-contrib/static as Gin middleware. +// +// Example: +// +// api.New(api.WithStatic("/assets", "./public")) func WithStatic(urlPrefix, root string) Option { return func(e *Engine) { e.middlewares = append(e.middlewares, static.Serve(urlPrefix, static.LocalFile(root, false))) @@ -130,6 +154,10 @@ func WithWSHandler(h http.Handler) Option { // WithWSPath sets a custom URL path for the WebSocket endpoint. // The default path is "/ws". +// +// Example: +// +// api.New(api.WithWSPath("/socket")) func WithWSPath(path string) Option { return func(e *Engine) { e.wsPath = normaliseWSPath(path) @@ -139,6 +167,10 @@ func WithWSPath(path string) Option { // WithAuthentik adds Authentik forward-auth middleware that extracts user // identity from X-authentik-* headers set by a trusted reverse proxy. // The middleware is permissive: unauthenticated requests are allowed through. +// +// Example: +// +// api.New(api.WithAuthentik(api.AuthentikConfig{TrustedProxy: true})) func WithAuthentik(cfg AuthentikConfig) Option { return func(e *Engine) { e.middlewares = append(e.middlewares, authentikMiddleware(cfg, func() []string { @@ -151,6 +183,10 @@ func WithAuthentik(cfg AuthentikConfig) Option { // The middleware appends Deprecation, optional Sunset, optional Link, and // X-API-Warn headers without clobbering any existing header values. Use it to // deprecate an entire route group or API version. +// +// Example: +// +// api.New(api.WithSunset("2026-12-31", "https://api.example.com/v2")) func WithSunset(sunsetDate, replacement string) Option { return func(e *Engine) { e.middlewares = append(e.middlewares, ApiSunset(sunsetDate, replacement)) @@ -159,6 +195,10 @@ func WithSunset(sunsetDate, replacement string) Option { // WithSwagger enables the Swagger UI at /swagger/ by default. // The title, description, and version populate the OpenAPI info block. +// +// Example: +// +// api.New(api.WithSwagger("Service", "Public API", "1.0.0")) func WithSwagger(title, description, version string) Option { return func(e *Engine) { e.swaggerTitle = title @@ -170,6 +210,10 @@ func WithSwagger(title, description, version string) Option { // WithSwaggerPath sets a custom URL path for the Swagger UI. // The default path is "/swagger". +// +// Example: +// +// api.New(api.WithSwaggerPath("/docs")) func WithSwaggerPath(path string) Option { return func(e *Engine) { e.swaggerPath = normaliseSwaggerPath(path) @@ -296,6 +340,10 @@ func WithSwaggerExternalDocs(description, url string) Option { // // WARNING: pprof exposes sensitive runtime data and should only be // enabled in development or behind authentication in production. +// +// Example: +// +// api.New(api.WithPprof()) func WithPprof() Option { return func(e *Engine) { e.pprofEnabled = true @@ -310,6 +358,10 @@ func WithPprof() Option { // WARNING: expvar exposes runtime internals (memory allocation, // goroutine counts, command-line arguments) and should only be // enabled in development or behind authentication in production. +// +// Example: +// +// api.New(api.WithExpvar()) func WithExpvar() Option { return func(e *Engine) { e.expvarEnabled = true @@ -321,6 +373,10 @@ func WithExpvar() Option { // X-Content-Type-Options nosniff, and Referrer-Policy strict-origin-when-cross-origin. // SSL redirect is not enabled so the middleware works behind a reverse proxy // that terminates TLS. +// +// Example: +// +// api.New(api.WithSecure()) func WithSecure() Option { return func(e *Engine) { e.middlewares = append(e.middlewares, secure.New(secure.Config{ @@ -337,6 +393,10 @@ func WithSecure() Option { // WithGzip adds gzip response compression middleware via gin-contrib/gzip. // An optional compression level may be supplied (e.g. gzip.BestSpeed, // gzip.BestCompression). If omitted, gzip.DefaultCompression is used. +// +// Example: +// +// api.New(api.WithGzip()) func WithGzip(level ...int) Option { return func(e *Engine) { l := gzip.DefaultCompression @@ -350,6 +410,10 @@ func WithGzip(level ...int) Option { // WithBrotli adds Brotli response compression middleware using andybalholm/brotli. // An optional compression level may be supplied (e.g. BrotliBestSpeed, // BrotliBestCompression). If omitted, BrotliDefaultCompression is used. +// +// Example: +// +// api.New(api.WithBrotli()) func WithBrotli(level ...int) Option { return func(e *Engine) { l := BrotliDefaultCompression @@ -363,6 +427,10 @@ func WithBrotli(level ...int) Option { // WithSlog adds structured request logging middleware via gin-contrib/slog. // Each request is logged with method, path, status code, latency, and client IP. // If logger is nil, slog.Default() is used. +// +// Example: +// +// api.New(api.WithSlog(nil)) func WithSlog(logger *slog.Logger) Option { return func(e *Engine) { if logger == nil { @@ -384,6 +452,10 @@ func WithSlog(logger *slog.Logger) Option { // // A zero or negative duration effectively disables the timeout (the handler // runs without a deadline) — this is safe and will not panic. +// +// Example: +// +// api.New(api.WithTimeout(5 * time.Second)) func WithTimeout(d time.Duration) Option { return func(e *Engine) { if d <= 0 { @@ -456,6 +528,10 @@ func WithRateLimit(limit int) Option { // gin-contrib/sessions using a cookie-based store. The name parameter // sets the session cookie name (e.g. "session") and secret is the key // used for cookie signing and encryption. +// +// Example: +// +// api.New(api.WithSessions("session", []byte("secret"))) func WithSessions(name string, secret []byte) Option { return func(e *Engine) { store := cookie.NewStore(secret) @@ -468,6 +544,10 @@ func WithSessions(name string, secret []byte) Option { // holding the desired model and policy rules. The middleware extracts the // subject from HTTP Basic Authentication, evaluates it against the request // method and path, and returns 403 Forbidden when the policy denies access. +// +// Example: +// +// api.New(api.WithAuthz(enforcer)) func WithAuthz(enforcer *casbin.Enforcer) Option { return func(e *Engine) { e.middlewares = append(e.middlewares, authz.NewAuthorizer(enforcer)) @@ -487,6 +567,10 @@ func WithAuthz(enforcer *casbin.Enforcer) Option { // // Requests with a missing, malformed, or invalid signature are rejected with // 401 Unauthorised or 400 Bad Request. +// +// Example: +// +// api.New(api.WithHTTPSign(secrets)) func WithHTTPSign(secrets httpsign.Secrets, opts ...httpsign.Option) Option { return func(e *Engine) { auth := httpsign.NewAuthenticator(secrets, opts...) @@ -512,6 +596,10 @@ func WithSSE(broker *SSEBroker) Option { // WithSSEPath sets a custom URL path for the SSE endpoint. // The default path is "/events". +// +// Example: +// +// api.New(api.WithSSEPath("/stream")) func WithSSEPath(path string) Option { return func(e *Engine) { e.ssePath = normaliseSSEPath(path) diff --git a/response.go b/response.go index 5f98b54..45928ce 100644 --- a/response.go +++ b/response.go @@ -18,6 +18,10 @@ type Response[T any] struct { } // Error describes a failed API request. +// +// Example: +// +// err := api.Error{Code: "invalid_input", Message: "Name is required"} type Error struct { Code string `json:"code"` Message string `json:"message"` @@ -25,6 +29,10 @@ type Error struct { } // Meta carries pagination and request metadata. +// +// Example: +// +// meta := api.Meta{RequestID: "req_123", Duration: "12ms"} type Meta struct { RequestID string `json:"request_id,omitempty"` Duration string `json:"duration,omitempty"` diff --git a/spec_registry.go b/spec_registry.go index fd8b40a..d6eed0b 100644 --- a/spec_registry.go +++ b/spec_registry.go @@ -76,6 +76,12 @@ func RegisteredSpecGroups() []RouteGroup { // // The iterator snapshots the current registry contents so callers can range // over it without holding the registry lock. +// +// Example: +// +// for g := range api.RegisteredSpecGroupsIter() { +// _ = g +// } func RegisteredSpecGroupsIter() iter.Seq[RouteGroup] { specRegistry.mu.RLock() groups := slices.Clone(specRegistry.groups) diff --git a/sse.go b/sse.go index 9646b56..8430f34 100644 --- a/sse.go +++ b/sse.go @@ -192,6 +192,10 @@ func (b *SSEBroker) Handler() gin.HandlerFunc { } // ClientCount returns the number of currently connected SSE clients. +// +// Example: +// +// n := broker.ClientCount() func (b *SSEBroker) ClientCount() int { b.mu.RLock() defer b.mu.RUnlock() diff --git a/transport.go b/transport.go index 43b6753..33c5a9b 100644 --- a/transport.go +++ b/transport.go @@ -8,6 +8,10 @@ import "strings" // // It is intentionally small and serialisable so callers can inspect the active HTTP // surface without rebuilding an OpenAPI document. +// +// Example: +// +// cfg := api.TransportConfig{SwaggerPath: "/swagger", WSPath: "/ws"} type TransportConfig struct { SwaggerPath string GraphQLPath string @@ -22,6 +26,10 @@ type TransportConfig struct { // // The result snapshots the Engine state at call time and normalises any configured // URL paths using the same rules as the runtime handlers. +// +// Example: +// +// cfg := engine.TransportConfig() func (e *Engine) TransportConfig() TransportConfig { if e == nil { return TransportConfig{}