emulator

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Host is the interface to bind. It defaults to loopback (127.0.0.1): the emulator
	// authenticates nothing, so it must not be reachable off the machine unless the
	// user deliberately widens it (a container that maps the port needs 0.0.0.0).
	Host string

	// Port is the TCP port to listen on.
	Port int

	// Seed is a directory of JSON fixtures to preload, or "" for an empty tenant.
	Seed string

	// Verbose logs one line per request to Log.
	Verbose bool

	// Log is where request logs and startup notices go — stderr, so stdout stays the
	// data contract even here.
	Log io.Writer

	// Components is where the emulator looks for its transport sub-components: the
	// processes that deliver an event to a broker. A nil registry means none, which is
	// an emulator that delivers webhooks and nothing else — a perfectly ordinary way to
	// run it.
	Components *component.Registry

	// TransportEnv is the configuration to hand those components, by variable name.
	// Only the names a component's manifest declares are actually set, so
	// --pubsub-emulator-host reaches the Pub/Sub transport as the PUBSUB_EMULATOR_HOST
	// it already reads, and reaches nothing else.
	TransportEnv map[string]string

	// WebhookAllowRemote lets webhook delivery call a callbackUrl outside the local
	// network. It is off by default so a subscription fixture naming a real endpoint is
	// skipped rather than fired made-up events at.
	WebhookAllowRemote bool
	// contains filtered or unexported fields
}

Config configures a Server.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is a running emulator: a Fiber app answering the whole API surface from one in-memory store.

func New

func New(cfg Config) (*Server, error)

New builds a server: it registers every operation in the spec, wires the store, and preloads any seed data. It does not listen yet.

func (*Server) Eventing added in v0.6.0

func (s *Server) Eventing() []TargetStatus

Eventing reports each subscription target type, and what will happen to a subscription that names it.

The three the API defines are always reported, including the ones nothing delivers — a stored subscription that silently never fires is the failure this notice exists to prevent, and leaving the unavailable ones out is how it would happen. A third-party transport's own target type is reported too: it is the same silent failure, and a notice that only knew the three built-in types would hide exactly the broker the community protocol was added to reach.

func (*Server) Listen

func (s *Server) Listen(ctx context.Context, ready func()) error

Listen binds the port and serves until ctx is cancelled, then shuts down gracefully and returns nil. It returns a non-nil error when the port could not be bound (e.g. already taken) or the server otherwise failed.

ready, if non-nil, is called the moment the port is bound — before Listen starts blocking to serve — so a caller can print a "point fft here" recipe only once it is actually true, instead of racing a bind failure with a recipe that looks like it worked.

ctx is the command's context, which the root cancels on SIGINT/SIGTERM — so Ctrl-C drains the server and exits 0.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the emulator's in-memory tenant: a set of collections, each a map of entities by id with a stable insertion order for pagination. One RWMutex guards the whole thing — a dev-time emulator has no need for finer locking, and one lock is one fewer thing to reason about.

func NewStore

func NewStore(metas map[string]collectionMeta) *Store

NewStore returns an empty store that knows each collection's response metadata (its items-key), inferred from the spec.

func (*Store) Create

func (s *Store) Create(name string, doc entityDoc) (map[string]any, bool)

Create stores a new entity, assigning it an id (unless the body carries one) and version 1 (unless the body carries a version), and returns the stored document. This honors an incoming id/version so seed() can replay a fixture's captured values; the live HTTP create handler strips both from the body before calling this, matching the real API's server-assigned id/version semantics. The exported methods speak the concrete map[string]any rather than the internal entityDoc alias, which is the same type.

The second return is false when the collection is at [maxEntitiesPerCollection] and the entity was not stored.

func (*Store) Delete

func (s *Store) Delete(name, id string) bool

Delete removes an entity, reporting whether it was there.

func (*Store) FindBy

func (s *Store) FindBy(name, field, value string) (string, bool)

FindBy returns the id of the first entity in the collection whose string field equals value, in insertion order. It backs URN path resolution: the API addresses a facility as urn:fft:facility:tenantFacilityId:<value>, and this is how the emulator turns that selector back into the entity the store keeps.

func (*Store) Get

func (s *Store) Get(name, id string) (map[string]any, bool)

Get returns the entity, or false when the collection does not hold it.

func (*Store) List

func (s *Store) List(name string) []map[string]any

List returns every entity in the collection, in insertion order. It clones on every call — the paginators then slice the result — which is O(n) per page and so O(n²) across a full walk. That is deliberate simplicity: a dev emulator holds tens of entities, not the millions where this would matter.

func (*Store) Update

func (s *Store) Update(name, id string, doc entityDoc, patch bool) (map[string]any, error)

Update replaces (PUT) or merges (PATCH) an entity and bumps its version.

It enforces the body-carried optimistic lock: when the incoming document names a version and it does not match the stored one, the update is refused with a *conflictError so the client re-reads and retries. A missing version is taken as "whatever is current" rather than a conflict, so a PATCH need not echo it.

type TargetStatus added in v0.6.0

type TargetStatus struct {
	// Target is the target type, e.g. GOOGLE_CLOUD_PUB_SUB.
	Target string

	// Status is one line, in the transport's own words where there is one. A
	// transport that came from a component said this at the handshake, so the notice
	// reports where a broker actually is without the emulator knowing anything about
	// brokers.
	Status string

	// Live reports whether anything will be delivered to this target type at all.
	Live bool
}

TargetStatus is what the startup notice says about one subscription target type: whether an event with that target will be delivered, and where.

Jump to

Keyboard shortcuts

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