mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
App, Group, Operation. A group is a prefix and what holds under it, an operation
is one typed route declared there, and the verb methods are generic methods —
which Go 1.27 allows on a concrete type and still refuses on an interface. So In
and Out come from the handler and the caller writes no type arguments:
users := app.Group("/v1/users")
users.Post("/", api.Create)
mint := users.Group("/mint").With(middleware.Mint(gate))
mint.Post("/deposit", api.Deposit)
A Group is a CLOSED HANDLE: two unexported fields, and no method that hands back
the app, the parent or the fiber underneath. A subsystem given the group at its
own prefix cannot reach above it, so confinement is a property of the type
instead of a rule something has to police — which is what a host needs in order
to hand out a bounded surface and know what comes back.
That is bought by giving up the router interface, deliberately. Router and
OpTarget are gone, and with them the ability to implement a route table outside
this package: a decorator is no longer a router, a gate is middleware on a group,
and a bound is a path. The five package-level verb functions go the same way —
a registration is a method on the thing it registers into.
Raw carries what is not a typed operation: a stream, an upgrade, a probe, a
foreign subtree. It names its method, and it is the longer spelling because
opting out of the typed model is the exception and should read like one.
Co-authored-by: Hanzo Dev <[email protected]>
57 lines
1.4 KiB
Go
57 lines
1.4 KiB
Go
// Package buckets writes registrations in every form zip offers, so a test can
|
|
// ask which package each one is filed under.
|
|
package buckets
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/zap-proto/zip"
|
|
"github.com/zap-proto/zip/internal/attribution/meter"
|
|
)
|
|
|
|
// Path is the import path of this package, which is the answer every op
|
|
// registered here must give.
|
|
const Path = "github.com/zap-proto/zip/internal/attribution/buckets"
|
|
|
|
// In asks for a bucket.
|
|
type In struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// Out is one bucket.
|
|
type Out struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// API serves buckets.
|
|
type API struct{}
|
|
|
|
// Make makes a bucket.
|
|
func (API) Make(ctx context.Context, in *In) (*Out, error) { return &Out{Name: in.Name}, nil }
|
|
|
|
// List lists them.
|
|
func (API) List(ctx context.Context, in *In) (*Out, error) { return &Out{}, nil }
|
|
|
|
// Read reads one.
|
|
func (API) Read(ctx context.Context, in *In) (*Out, error) { return &Out{}, nil }
|
|
|
|
// Register declares the surface three ways: at the root, on a group, and on a
|
|
// group over a handler another package composed.
|
|
func Register(app *zip.App) {
|
|
var api API
|
|
app.Post("/plain", api.Make)
|
|
|
|
g := app.Group("/scope")
|
|
g.Get("/direct", api.List)
|
|
g.Get("/paid", meter.Paid(api.Read))
|
|
}
|
|
|
|
// Clash declares one address twice through a group, so a test can read which
|
|
// line the conflict names.
|
|
func Clash(app *zip.App) {
|
|
var api API
|
|
g := app.Group("/scope")
|
|
g.Get("/twice", api.List)
|
|
g.Get("/twice", api.Read)
|
|
}
|