grpc

package
v0.0.0-...-8c930d8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package grpc is the gRPC listener's API for a project's own code. The generated pb package registers the services of the models declaring GRPC() with Register and runs their actions through the call functions, CreateCall and its kind for the standard actions, ServiceCall for the actions with a payload or result of their own (see call.go) and ServerStreamCall and its kind for the Stream actions (see stream.go); the context functions read and establish what a call carries — its credential, its caller and the HTTP action it maps to (see context.go) — and StatusError maps a failure to the status the call answers with (see status.go). The listener itself and its chain of interceptors are the framework's own; the interceptors a project mounts are registered through package interceptor, the counterpart of package middleware.

Index

Constants

View Source
const MethodStream = grpcserver.MethodStream

MethodStream is what the registration describes the rpc of a Stream action with in place of an HTTP method, Route answering it for such a call, and so the action word an authorization policy grants a stream by.

Variables

This section is empty.

Functions

func Bearer

func Bearer(ctx context.Context) (credential string, ok bool)

Bearer returns the credential the call's authorization metadata carries as "Bearer <credential>", a token or a session id, and whether it carries one: the check jwt.ParseTokenFromHeader makes of the Authorization header. Any other scheme, or no authorization metadata at all, is no credential.

func BidiStreamCall

func BidiStreamCall[M types.Model, REQ types.Request, RSP types.Response](route string) func(ctx context.Context, params map[string]string, recv func() (REQ, error), send func(RSP) error) error

BidiStreamCall returns the call of the Stream action on route streaming both ways: given the route parameters, the function receiving the next request and the function sending one response, it runs the service's Stream method with a BidiStream on the two, and answers with nil once the stream is over, or with the status a failure maps to.

func ClientStreamCall

func ClientStreamCall[M types.Model, REQ types.Request, RSP types.Response](route string) func(ctx context.Context, params map[string]string, recv func() (REQ, error)) (RSP, error)

ClientStreamCall returns the call of the Stream action on route whose request is streamed: given the route parameters and the function receiving the next request, io.EOF once the client finished, it runs the service's Stream method with a ClientStream reading through the function, and answers with the response, or with the status a failure maps to.

func CreateCall

func CreateCall[M types.Model](route string) func(ctx context.Context, params map[string]string, m M) (M, error)

CreateCall returns the create call of M on route: given the route parameters and the model to create, it answers with the model created.

func CreateManyCall

func CreateManyCall[M types.Model](route string) func(ctx context.Context, params map[string]string, items []M) ([]M, error)

CreateManyCall returns the batch create call of M on route: given the route parameters and the items, it answers with the items created.

func DeleteCall

func DeleteCall[M types.Model](route string) func(ctx context.Context, params map[string]string, id string) error

DeleteCall returns the delete call of M on route: given the route parameters and the id, it answers nothing.

func DeleteManyCall

func DeleteManyCall[M types.Model](route string) func(ctx context.Context, params map[string]string, ids []string) error

DeleteManyCall returns the batch delete call of M on route: given the route parameters and the ids, it answers nothing.

func Document

func Document(field string, b []byte) ([]byte, error)

Document returns b, the bytes of a JSON document a model field holds as a json.RawMessage or a datatypes.JSON, nil for none, and refuses bytes that are no JSON document with invalidValue naming field, "not a JSON document" the detail: the HTTP listener reads the document out of the body it decoded, which encoding/json refused if the document was not JSON, and what a message carries is written out as one. The generated FromProto reads every JSON document through it.

func Element

func Element(path string, i int) string

Element returns the path of the element at index i of the repeated field at path, points[1], what a generated conversion names a refused element by.

func Entry

func Entry[K comparable](path string, key K) string

Entry returns the path of the value under key of the map field at path, costs.k1, and by_rank[7] for a key of digits alone, what a generated conversion names a refused map value by.

func Finite

func Finite[T ~float32 | ~float64, V ~float32 | ~float64](field string, v V) (T, error)

Finite returns v as the floating-point type T a model field holds, a named type over float32 or float64 included, and refuses NaN and the infinities with invalidValue naming field, "NaN is not a finite number" the detail: a message carries them where JSON has no spelling for them, so the HTTP listener never lets one in and could not write one out. The generated FromProto reads every float32 and float64 through it wherever it sits: a field, an optional one, an element of a slice or a value of a map.

func FirstMessage

func FirstMessage[T any](recv func() (T, error)) (T, error)

FirstMessage returns the first message of a request stream read through recv, the Recv of the stream the plugin generated: what the generated handler of a client or bidirectional stream on a route with parameters reads ahead of the call, the parameters being carried by that message. A stream the client ended before sending one is refused with InvalidArgument; any other error of recv is returned as it is.

func GetCall

func GetCall[M types.Model](route string) func(ctx context.Context, params map[string]string, id string, query Query) (M, error)

GetCall returns the get call of M on route: given the route parameters, the id and the expansion query, it answers with the model found.

func ItemError

func ItemError(i int, err error) error

ItemError names the item at index i of a batch in err, the refusal a FromProto answered the item's record with: the field refused becomes the item's, items[1].rank, in the message and in the google.rpc.BadRequest detail alike, as the fields a validator refuses are named in a batch; any other error is answered as it is. The generated handler of a CreateMany, UpdateMany or PatchMany rpc answers each item's decoding through it.

func ListCall

func ListCall[M types.Model](route string) func(ctx context.Context, params map[string]string, query Query) ([]M, int, error)

ListCall returns the list call of M on route: given the route parameters and the query, it answers with the items of the page and the total, 0 under cursor pagination.

func Map

func Map(s *structpb.Struct) map[string]any

Map returns the object s holds as a map, and nil for nil. A NaN or an infinity among its numbers comes back as the string "NaN", "Infinity" or "-Infinity", the spelling the JSON mapping of a Value gives it, which a JSON body carries as it is.

func Narrow

func Narrow[T ~int8 | ~int16 | ~uint8 | ~uint16, V ~int32 | ~uint32](field string, v V) (T, error)

Narrow returns v as the narrower integer type T a model field holds, int8 for the int32 its message carries, and refuses a value T cannot hold, 300 folded into an int8 would come back as 44, with invalidValue naming field, "300 is out of range" the detail. The generated FromProto reads every int8, int16, uint8 and uint16 field, and every map key of one of those types, through it.

func Number

func Number(field, s string) (json.Number, error)

Number returns s as the json.Number a model field holds, and refuses with invalidValue naming field, `"abc" is not a JSON number` the detail, a string that is no JSON number literal, text, a quoted number or the empty string among them: the message carries the number as a string, and one that is not a number would be written out as one. encoding/json judges the literal, so the two listeners refuse the same strings. The generated FromProto reads every json.Number through it: a field the message leaves empty, "", stands for the unset value and is read as it is, while an element of a repeated field or the value of a map entry, which has no unset state, is read through it whatever it holds.

func NumberText

func NumberText(n json.Number) string

NumberText returns the text a message carries for n, a json.Number held as an element of a repeated field or the value of a map entry: n through UTF8 like any string, and the empty number, what an HTTP body's null leaves in one, as "0", the number encoding/json writes it as, so that the message read back through Number is taken. A field's empty number is the field unset, which the generated ToProto writes as it is.

func PatchCall

func PatchCall[M types.Model](route string) func(ctx context.Context, params map[string]string, id string, m M, paths []string) (M, error)

PatchCall returns the patch call of M on route: given the route parameters, the id, the values and the paths of the update mask, which name the fields to apply as the message names them, each applied as a whole, a path naming a field the framework manages passed over, and must leave at least one to apply, it answers with the record patched; a message carrying no record is refused.

func PatchItem

func PatchItem[M types.Model](i int, params, itemParams map[string]string, id string, m M) (M, error)

PatchItem readies the record of the item at index i of a batch patch, what the generated handler of a PatchMany rpc reads each item through, an item being the request of a single Patch: the item names its record by id, written onto the record whatever id the record carries, as a Patch call patches the record its message names, and the route parameters it carries, keyed as the request's params are, may be left empty or repeat the request's. An item naming no id, or naming a parameter otherwise than the request does, is refused with InvalidArgument; an item carrying no record is answered as it is, for PatchManyCall to refuse.

func PatchManyCall

func PatchManyCall[M types.Model](route string) func(ctx context.Context, params map[string]string, items []M, paths [][]string) ([]M, error)

PatchManyCall returns the batch patch call of M on route: given the route parameters, the items and the paths of each item's update mask, in order, it answers with the records patched.

func PathError

func PathError(path string, err error) error

PathError names the field err, the refusal a FromProto answered a value with, refers to behind path, meta.score, in the message and in the google.rpc.BadRequest detail alike; any other error is answered as it is. A generated conversion answers the refusal of a nested message, of an element of a repeated field (see Element) and of a value of a map (see Entry) through it.

func Register

func Register[S any](register func(grpc.ServiceRegistrar, S), server S, methods ...Method)

Register queues the service server serves to be registered on the listener through register, the RegisterXxxServiceServer function the protobuf plugin generated for it, the way the generated pb.gen.go of every package under pb/ registers the services of the models declaring GRPC(), each through the type serving it:

grpc.Register[NoteServiceServer](RegisterNoteServiceServer, noteService{},
	grpc.Method{Name: NoteService_CreateNote_FullMethodName, HTTPMethod: http.MethodPost, Route: "/api/notes"},
	grpc.Method{Name: NoteService_GetNote_FullMethodName, HTTPMethod: http.MethodGet, Route: "/api/notes/:id"},
)

S is the server interface the plugin generated, spelled out because the value registered is of the type serving it, which inference cannot tell apart from the interface. methods describe the service's rpcs. It runs at package initialization, before bootstrap starts the listeners, and panics once the listener runs.

func Route

func Route(ctx context.Context) (httpMethod, route string)

Route returns the HTTP method and route the call's action is served at over HTTP, "GET" and "/api/records" for a call of ListRecord, as the generated registration described the rpc (see Method). Both are empty outside a call and for the framework's own services, health and reflection.

func SameParams

func SameParams(i int, params, msgParams map[string]string) error

SameParams holds the route parameters msgParams of the message at index i of a request stream, the first message being 1, to params, the ones the first message carried: what the generated handler of a client or bidirectional stream on a route with parameters reads every message after the first through. A parameter the message leaves empty or names as the first did agrees; one it names otherwise is refused with InvalidArgument naming the message, the parameter and both values.

func ServerStreamCall

func ServerStreamCall[M types.Model, REQ types.Request, RSP types.Response](route string) func(ctx context.Context, params map[string]string, req REQ, send func(RSP) error) error

ServerStreamCall returns the call of the Stream action on route whose response is streamed: given the route parameters, the request the message decoded into and the function sending one response, it runs the service's Stream method with a ServerStream sending through the function, and answers with nil once the stream is over, or with the status a failure maps to, the way ServiceCall does.

func ServiceCall

func ServiceCall[M types.Model, REQ types.Request, RSP types.Response](phase apiaction.Phase, route string) func(ctx context.Context, params map[string]string, query Query, req REQ) (RSP, error)

ServiceCall returns the call of the phase service's method for the action of phase on route, for the generated handler of the rpc of an action declaring a Payload or Result of its own: given the route parameters, the query of a List or Get and the payload the request message decoded into, it validates the payload the way the HTTP handler validates a bound body, runs the method on a service context answering the parameters, the query and the caller, and answers with its result, or with the status its error maps to. phase is one of the actions gRPC serves; the phase of an HTTP-only action panics, as the route registers.

func StatusError

func StatusError(err error) error

StatusError returns the status error a call answers err with: a service error (see gst.NewError) answers with its own status and message, mapped to the gRPC code the way the HTTP listener maps it to a response; any other error answers Internal with a fixed message, its text kept out of the answer, so log it before mapping it. nil stays nil.

func Struct

func Struct(m map[string]any) *structpb.Struct

Struct returns the Struct holding m, the JSON object encoding/json encodes m to, and nil for a nil map. It panics on a value encoding/json cannot encode, like Value.

func Text

func Text[T any, P interface {
	*T
	encoding.TextUnmarshaler
}](field, s string) (T, error)

Text returns s, the text a value of the model field's type T writes with its MarshalText, read into a T through its UnmarshalText, the way encoding/json reads a JSON string into a type with the methods: a netip.Addr from "10.0.0.1". A text the type refuses is refused with invalidValue naming field, `"10.0.0" cannot be read as a netip.Addr: ParseAddr("10.0.0"): IPv4 address too short` the detail. The generated FromProto reads every value of a type with a text method and no JSON one through it (see jsonshape.Project.TextMethod): a field the message leaves empty, "", stands for the unset value and decodes into the zero value without it, while an optional field, an element of a repeated field or the value of a map entry, which hold what they are given, are read through it whatever they hold.

func TextOf

func TextOf(v encoding.TextMarshaler) (string, error)

TextOf returns the text v writes with its MarshalText, through UTF8 like any string: the string encoding/json writes for a type with the method. A value the type cannot write as text, a project enum holding a value its method does not know, is an error the generated ToProto answers and the call answers Internal, logged in the access log, the way the HTTP listener answers a response it cannot encode. The generated ToProto writes every value of a type with a text method and no JSON one through it.

func Time

func Time(field string, ts *timestamppb.Timestamp) (time.Time, error)

Time returns the time ts holds, in UTC, and the zero time for nil; a Timestamp outside the range a Timestamp may hold, the years 1 to 9999, is refused with invalidValue naming field and timestamppb's own account of it, "timestamp (seconds:253402300800) after 9999-12-31", its proto prefix dropped, the way the HTTP listener refuses a time it cannot parse. The generated FromProto reads every time through it.

func TimeOfDay

func TimeOfDay(field string, d *durationpb.Duration) (time.Duration, error)

TimeOfDay returns the duration since midnight d holds, the time of day a message carries as one, and none, midnight, for nil, which a message leaves unset. A duration below none or of a whole day or more names no time of day and is refused with invalidValue naming field, "25h0m0s is not a time of day" the detail, and one no duration can be made of with durationpb's own account of it, "duration (nanos:1000000000) has out-of-range nanos", the way the HTTP listener refuses a time of day it cannot read, one time.TimeOnly does not spell. The generated FromProto reads every time of day through it, the datatypes.Time of a model made of the duration it answers.

func Timestamp

func Timestamp(t time.Time) *timestamppb.Timestamp

Timestamp returns the Timestamp of t, and nil for the zero time, which a message leaves unset. Where a nil would stand for something else, in a repeated field, a map value or an optional field, the generated ToProto writes the zero time as the Timestamp of 0001-01-01 instead, which comes back as the zero time.

func UTF8

func UTF8(s string) string

UTF8 returns s with every byte that is no part of valid UTF-8 replaced by U+FFFD, the way encoding/json writes a string out: a message carries valid UTF-8 alone and would fail to encode otherwise, failing the whole response for one value, where the JSON encoder replaces the bytes and goes on. The generated ToProto writes every string through it wherever it sits: a field, an optional one, an element of a slice, a key or a value of a map. A valid string comes back as it is.

func UpdateCall

func UpdateCall[M types.Model](route string) func(ctx context.Context, params map[string]string, id string, m M) (M, error)

UpdateCall returns the update call of M on route: given the route parameters, the id and the replacement, it answers with the replacement as stored.

func UpdateManyCall

func UpdateManyCall[M types.Model](route string) func(ctx context.Context, params map[string]string, items []M) ([]M, error)

UpdateManyCall returns the batch update call of M on route: given the route parameters and the items, it answers with the items as stored.

func Value

func Value(v any) *structpb.Value

Value returns the Value holding v, the JSON value encoding/json encodes v to, and nil for nil: a Go value travels in a Value as it travels in a JSON body. It panics on a value encoding/json cannot encode, a channel or a function, which the HTTP envelope answers 500 instead; the recovery interceptor answers Internal and logs it.

func WithBearer

func WithBearer(ctx context.Context, credential string) context.Context

WithBearer returns ctx carrying credential as the call's authorization metadata, "Bearer <credential>", the form Bearer reads: a session id for the session interceptor, a token for a JWT one. Every call made on the returned context presents it.

func WithCaller

func WithCaller(ctx context.Context, caller Caller) context.Context

WithCaller returns ctx with caller established as who is calling: the request metadata the service context and the flows read names the caller, and so does the call's access-log entry. Hand the returned context on to the handler once who is calling has been verified; a gRPC context cannot be written the way a gin context can, which is why the caller travels this way.

Types

type BidiStream

type BidiStream[REQ types.Request, RSP types.Response] = types.BidiStream[REQ, RSP]

BidiStream is the request and response stream of a Stream action declaring both a StreamingPayload and a StreamingResult: Recv and Send as on the two above, independent of each other.

type Caller

type Caller = grpcserver.Caller

Caller is who a call is made by, as WithCaller established it: the user by name and id, the session and the tenant.

func CallerOf

func CallerOf(ctx context.Context) Caller

CallerOf returns the caller WithCaller established for the call, the zero Caller before one is established.

type ClientStream

type ClientStream[REQ types.Request] = types.ClientStream[REQ]

ClientStream is the request stream of a Stream action declaring a StreamingPayload: the service's Stream method reads each request through Recv, which answers io.EOF once the client finished sending.

type Filter

type Filter = controller.Filter

Filter is one filter of a Query: with an operator the field[op]=value of the HTTP query, {Field: "age", Op: "gt", Values: []string{"20"}} being age[gt]=20; without one the bare key, field=value. The in and notin operators take their members as the list Values is, where the HTTP query takes them comma-separated.

func Filters

func Filters[F interface {
	GetField() string
	GetOp() string
	GetValues() []string
}](filters []F) []Filter

Filters returns the filters a List request carries, each a Filter message of the request as gg gen derives it, as the Query takes them, and nil for none.

type Interceptor

type Interceptor = grpcserver.Interceptor

Interceptor is an interceptor mounted through interceptor.Register or interceptor.RegisterAuth: it reads what the call carries, its metadata, its caller (see CallerOf) and the action it maps to (see Route), and returns the context the call goes on with, the caller established on it (see WithCaller), or the error refusing the call: a status error answers as it is, a gst.Error with the code its HTTP status maps to and its client-safe message (see StatusError), any other error as Internal with the server failure message. The one form serves a unary call and a stream alike, an authentication running once, ahead of the first message.

type Method

type Method = grpcserver.Method

Method describes one rpc of a registered service the way the generated registration file declares it: its full name, whether its action declares Public(), and the HTTP method and route the same action is served at over HTTP, which Route answers for a call of the rpc.

type Query

type Query = controller.Query

Query is what the request message of a List or Get rpc carries for the query parameters of the same action over HTTP, as the generated messages name them: the filters, orderings, pagination, cursor and expansion of a List, the expansion of a Get. The call functions read it exactly as the HTTP listener reads the query string, so both transports refuse the same requests alike, a filter given twice with the listener's refusal of a repeated parameter, but for a filter left without a value or with an empty one, which a call refuses while an empty parameter of a query string filters by nothing; the zero value of a field is the parameter not sent.

gRPC has no URL, so what an HTTP request carries in its path and query string the request message carries in its fields, the route parameters first and these after them. A List request carries the filters and the fields of the controls the model reads: the orderings and the expansion of a model embedding model.Query, the page of one embedding model.Pagination, the cursor of one embedding model.Cursor and the size of either, the way the HTTP listener refuses the parameters of the others; a custom List carries every field, its service reading the query. For a model Record embedding model.Query on the route records, gg gen derives

message ListRecordRequest {
  repeated Filter filters = 1;
  repeated string sort_by = 2;
  uint32 page = 3;
  uint32 size = 4;
  string cursor_field = 5;
  string cursor_value = 6;
  bool cursor_next = 7;
  repeated string expand = 8;
  uint32 depth = 9;

  message Filter {
    string field = 1;
    string op = 2;
    repeated string values = 3;
  }
}

message GetRecordRequest {
  string id = 1;
  repeated string expand = 2;
  uint32 depth = 3;
}

and, for a route with parameters, their string fields ahead of these, record for records/:record/items. Listing a Record with the fields status, age and name over HTTP as

GET /api/records?status[in]=active,archived&age[gt]=20&name=alice&_sort_by=created_at desc&_page=2&_size=20

is calling ListRecord with the request message (in the JSON grpcurl speaks)

{
  "filters": [
    {"field": "status", "op": "in", "values": ["active", "archived"]},
    {"field": "age", "op": "gt", "values": ["20"]},
    {"field": "name", "values": ["alice"]}
  ],
  "sort_by": ["created_at desc"],
  "page": 2,
  "size": 20
}

which the generated handler hands the call as

Query{
	Filters: []Filter{
		{Field: "status", Op: "in", Values: []string{"active", "archived"}},
		{Field: "age", Op: "gt", Values: []string{"20"}},
		{Field: "name", Values: []string{"alice"}},
	},
	SortBy: []string{"created_at desc"},
	Page:   2,
	Size:   20,
}

A Get carries only the expansion: {"id": "r-1", "expand": ["children"], "depth": 2} is GET /api/records/r-1?_expand=children&_depth=2.

type ServerStream

type ServerStream[RSP types.Response] = types.ServerStream[RSP]

ServerStream is the response stream of a Stream action declaring a StreamingResult: the service's Stream method sends each response through Send, which fails once the client went away or the call was canceled.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址