mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
Implements pkg/sdk/client — the Temporal-replacement surface base,
commerce, and ta call when migrating off go.temporal.io/sdk/client.
Transport is luxfi/zap; zero go.temporal.io/* and zero
google.golang.org/grpc imports.
Opcode allocation (append-only, never reuse):
0x0050-0x005F pkg/tasks legacy (submit / schedule), reserved
0x0060-0x006F workflow lifecycle (start, signal, cancel,
terminate, describe, list)
0x0070-0x007F schedule ops (create, list, delete, pause)
0x0080-0x008F namespace ops (register, describe, list)
0x0090-0x009F health + meta
0x00A0-0x00AF worker poll / respond (moved up from 0x0070
to avoid collision with schedule ops)
Wire (v1): request and response bodies are a JSON document wrapped
in a ZAP envelope with three fields:
field 0 (Bytes) — body
field 8 (Uint32) — status (0/200 = ok)
field 12 (Bytes) — error detail on non-zero status
Opcode is stamped on the ZAP frame flags (high byte). Native ZAP
serde replaces JSON in a follow-up without changing opcodes.
Files:
- client.go — Client interface (Close/ExecuteWorkflow/SignalWorkflow/
CancelWorkflow/Terminate/Describe/List + schedule +
namespace + health), Dial, Transport abstraction,
luxfi/zap default transport, envelope codec.
- options.go — Options, StartWorkflowOptions (RetryPolicy via
pkg/sdk/temporal, no upstream import).
- workflow.go — ExecuteWorkflow, Signal/Cancel/Terminate/Describe/List,
WorkflowRun with reflect-based type-name resolution.
- namespace.go — Register/Describe/List + wire duration-ms codec.
- schedule.go — Create/List/Delete/Pause + wire duration-ms codec.
- transport.go — WorkerTransport + PollWorkflowTaskRequest etc. +
the worker-opcode constants (now 0x00A0+).
- client_test.go — Dial/Close test, ExecuteWorkflow round-trip with a
fake Transport capturing the request and replaying
a canned response (same pattern as pkg/tasks).
Worker bridge helpers (worker_bridge.go, worker_transport.go) were
added concurrently on feat/zap-internal; they import and wrap the
Transport this commit defines.
WorkflowRun.Get gap: the v1 schema does not yet declare a
GetWorkflowHistory / GetWorkflowResult RPC, so Get polls
DescribeWorkflow and returns ErrNotImplementedLocally when the
caller asks for a result from a completed workflow. This is marked
in the doc comment and will be filled in when pkg/sdk/worker lands
the history-fetch opcode.
Verification:
go vet ./pkg/sdk/client/... — clean
go test ./pkg/sdk/client/... — 7/7 pass
go list -deps ./pkg/sdk/client/... — no go.temporal.io/*, no
google.golang.org/grpc
133 lines
4.1 KiB
Go
133 lines
4.1 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// Namespace mirrors schema/tasks.zap Namespace for the v1 JSON wire.
|
|
type Namespace struct {
|
|
Info NamespaceInfo `json:"info"`
|
|
Config NamespaceConfig `json:"config"`
|
|
}
|
|
|
|
// NamespaceInfo mirrors schema/tasks.zap NamespaceInfo.
|
|
type NamespaceInfo struct {
|
|
Name string `json:"name"`
|
|
State NamespaceState `json:"state"`
|
|
Description string `json:"description,omitempty"`
|
|
OwnerEmail string `json:"owner_email,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
}
|
|
|
|
// NamespaceConfig mirrors schema/tasks.zap NamespaceConfig. Retention is
|
|
// exposed as a Go Duration on the Go surface and marshalled to/from
|
|
// milliseconds on the wire (see retentionJSON below).
|
|
type NamespaceConfig struct {
|
|
Retention time.Duration `json:"-"`
|
|
}
|
|
|
|
// retentionJSON captures the ms-granular wire shape without leaking
|
|
// temporal.io types.
|
|
type namespaceConfigWire struct {
|
|
RetentionMs int64 `json:"retention_ms,omitempty"`
|
|
}
|
|
|
|
// MarshalJSON implements custom encoding for the wire shape.
|
|
func (n NamespaceConfig) MarshalJSON() ([]byte, error) {
|
|
return jsonMarshal(namespaceConfigWire{RetentionMs: n.Retention.Milliseconds()})
|
|
}
|
|
|
|
// UnmarshalJSON implements custom decoding for the wire shape.
|
|
func (n *NamespaceConfig) UnmarshalJSON(b []byte) error {
|
|
var w namespaceConfigWire
|
|
if err := jsonUnmarshal(b, &w); err != nil {
|
|
return err
|
|
}
|
|
n.Retention = time.Duration(w.RetentionMs) * time.Millisecond
|
|
return nil
|
|
}
|
|
|
|
// NamespaceState mirrors the Int8 state enum.
|
|
type NamespaceState int8
|
|
|
|
const (
|
|
NamespaceStateUnspecified NamespaceState = 0
|
|
NamespaceStateRegistered NamespaceState = 1
|
|
NamespaceStateDeprecated NamespaceState = 2
|
|
NamespaceStateDeleted NamespaceState = 3
|
|
)
|
|
|
|
// RegisterNamespaceRequest is the argument to RegisterNamespace. Mirrors
|
|
// schema/tasks.zap: server receives the full Namespace.
|
|
type RegisterNamespaceRequest struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
OwnerEmail string `json:"owner_email,omitempty"`
|
|
Retention time.Duration `json:"-"`
|
|
}
|
|
|
|
type registerNamespaceWire struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
OwnerEmail string `json:"owner_email,omitempty"`
|
|
RetentionMs int64 `json:"retention_ms,omitempty"`
|
|
}
|
|
|
|
type describeNamespaceRequest struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type describeNamespaceResponse struct {
|
|
Namespace Namespace `json:"namespace"`
|
|
}
|
|
|
|
type listNamespacesRequest struct {
|
|
PageSize int32 `json:"page_size,omitempty"`
|
|
NextPageToken []byte `json:"next_page_token,omitempty"`
|
|
}
|
|
|
|
// ListNamespacesResponse is returned by ListNamespaces.
|
|
type ListNamespacesResponse struct {
|
|
Namespaces []Namespace `json:"namespaces"`
|
|
NextPageToken []byte `json:"next_page_token,omitempty"`
|
|
}
|
|
|
|
// RegisterNamespace implements Client.
|
|
func (c *clientImpl) RegisterNamespace(ctx context.Context, req *RegisterNamespaceRequest) error {
|
|
if req == nil || req.Name == "" {
|
|
return errors.New("hanzo/tasks/client: namespace name is required")
|
|
}
|
|
wire := registerNamespaceWire{
|
|
Name: req.Name,
|
|
Description: req.Description,
|
|
OwnerEmail: req.OwnerEmail,
|
|
RetentionMs: req.Retention.Milliseconds(),
|
|
}
|
|
return c.roundTrip(ctx, opRegisterNamespace, wire, nil)
|
|
}
|
|
|
|
// DescribeNamespace implements Client.
|
|
func (c *clientImpl) DescribeNamespace(ctx context.Context, name string) (*Namespace, error) {
|
|
if name == "" {
|
|
return nil, errors.New("hanzo/tasks/client: namespace name is required")
|
|
}
|
|
var resp describeNamespaceResponse
|
|
if err := c.roundTrip(ctx, opDescribeNamespace, describeNamespaceRequest{Name: name}, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
out := resp.Namespace
|
|
return &out, nil
|
|
}
|
|
|
|
// ListNamespaces implements Client.
|
|
func (c *clientImpl) ListNamespaces(ctx context.Context, pageSize int32, nextPageToken []byte) (*ListNamespacesResponse, error) {
|
|
var resp ListNamespacesResponse
|
|
req := listNamespacesRequest{PageSize: pageSize, NextPageToken: nextPageToken}
|
|
if err := c.roundTrip(ctx, opListNamespaces, req, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|