Files
tasks/common/routing/route.go
Alex ShtinandGitHub 8e8f1b0f7c Remove license header from every file (#7689)
## What changed?
<!-- Describe what has changed in this PR -->
Remove license header from every file. Because it is really hard to
follow in this PR here is the summary:
1. License header is removed from all `.go` and `.proto` files
:fireworks::fireworks:🎆.
2. `LICENSE` file in the root directory has only Temporal and Uber
copyrights.
3. 5 other `LICENSE` files added to the packages which have copyrights
different from Temporal and Uber: Datadog, Xargin, "Mat Ryer, Tyler
Bunnell and contributors".
4. `license_file` flag is removed from all code generation tools.
5. `copyright_file` flag is removed from `go:generate mockgen`
directive.
6. All copyright related targets are removed from `Makefile`.
7. Updated Temporal copyright year to 2025 everywhere.

## Why?
<!-- Tell your future self why have you made these changes -->
I double checked with legal department that it is not needed to have
license header in every file. One file per repo is enough. I put all
copyrights to the root `LICENSE` file and removed header from all other
files. Also updated tools and `Makefile`.
2025-05-01 18:50:21 -07:00

150 lines
4.8 KiB
Go

// Package routing provides utilities to define a number of [Route] instances, which can be...
//
// 1. Used by servers to...
//
// 1.a. Register with [github.com/gorilla/mux.Router] instances via the [Route.Representation] method.
//
// 1.b. Deserialize HTTP path variables into a struct with the [Route.Deserialize] method.
//
// 2. Used by clients to construct HTTP paths for requests by calling the [Route.Path] method.
package routing
import (
"net/url"
"strings"
)
// Route represents a series of HTTP path components.
type Route[T any] struct {
components []Component[T]
}
// Component represents a single HTTP path component, either a constant slug or a variable parameter.
type Component[T any] interface {
// Representation is the string representation of the component for usage in a path definition, e.g. "v1" for a
// constant slug or "{namespace}" for a variable. This should be compatible with the format specified in
// the [github.com/gorilla/mux] package.
Representation() string
// Serialize returns the actual value of the slug when used in an HTTP path, e.g. "v1" for a constant slug or
// "test-namespace" for a variable.
Serialize(params T) string
// Deserialize mutates the given params object with the value of the component when parsing an HTTP path, e.g.
// setting the value of a variable to "test-namespace". If the component is a constant slug, this method should
// be a no-op.
Deserialize(vars map[string]string, t *T)
}
// NewRoute returns a new [Route] instance with the given components.
func NewRoute[T any](components ...Component[T]) Route[T] {
return Route[T]{components: components}
}
// RouteBuilder is a builder for the [Route] interface.
type RouteBuilder[T any] struct {
components []Component[T]
}
// NewBuilder creates a new [RouteBuilder] instance, which can be used to define a new [Route] via a fluent API.
func NewBuilder[T any]() *RouteBuilder[T] {
return &RouteBuilder[T]{}
}
// With adds a series of [Component] instances to the [Route].
func (r *RouteBuilder[T]) With(c ...Component[T]) *RouteBuilder[T] {
r.components = append(r.components, c...)
return r
}
// Constant adds a [Constant] component to the [Route].
func (r *RouteBuilder[T]) Constant(values ...string) *RouteBuilder[T] {
return r.With(Constant[T](values...))
}
// StringVariable adds a [StringVariable] component to the [Route].
func (r *RouteBuilder[T]) StringVariable(name string, getter func(*T) *string) *RouteBuilder[T] {
return r.With(StringVariable[T](name, getter))
}
// Build returns a read-only [Route].
func (r *RouteBuilder[T]) Build() Route[T] {
return NewRoute[T](r.components...)
}
// Representation returns the [github.com/gorilla/mux] compatible string representation of the route for usage in a
// path definition. It does not add a leading or trailing slash to the representation, but it won't remove them if
// they're present in the components. We do this because it's easier to add a slash depending on the context than to
// remove it.
func (r Route[T]) Representation() string {
return r.serialize(func(c Component[T]) string {
return c.Representation()
})
}
// Path returns the serialized path of the route with the given params. There will be no leading or trailing slashes,
// similar to the behavior of the [Route.Representation] method.
func (r Route[T]) Path(t T) string {
return r.serialize(func(c Component[T]) string {
return c.Serialize(t)
})
}
func (r Route[T]) serialize(f func(c Component[T]) string) string {
var sb strings.Builder
for i, c := range r.components {
if i > 0 {
sb.WriteString("/")
}
sb.WriteString(f(c))
}
return sb.String()
}
// Deserialize the given vars into a new instance of the params type, T.
func (r Route[T]) Deserialize(vars map[string]string) T {
var t T
for _, c := range r.components {
c.Deserialize(vars, &t)
}
return t
}
// Constant returns a [Component] that represents a series of constant HTTP path components in a Route.
// They will be joined via strings when used to construct a path or path representation.
func Constant[T any](values ...string) constant[T] {
return values
}
type constant[T any] []string
func (s constant[T]) Representation() string {
return strings.Join(s, "/")
}
func (s constant[T]) Serialize(T) string {
return strings.Join(s, "/")
}
func (s constant[T]) Deserialize(map[string]string, *T) {}
// StringVariable returns a [Component] that represents a string variable in a Route.
func StringVariable[T any](name string, getter func(*T) *string) stringVariable[T] {
return stringVariable[T]{name, getter}
}
type stringVariable[T any] struct {
name string
getter func(*T) *string
}
func (s stringVariable[T]) Representation() string {
return "{" + s.name + "}"
}
func (s stringVariable[T]) Serialize(t T) string {
return url.PathEscape(*s.getter(&t))
}
func (s stringVariable[T]) Deserialize(vars map[string]string, t *T) {
*s.getter(t) = vars[s.name]
}