appengine

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package appengine is the AppEngine API (docs/DESIGN.md §3.7, ROADMAP §8.2): the operator/application surface for device status, interface data queries, server-owned publishing, groups, and the live event socket. It is wire-shaped to upstream astarte_appengine_api so astartectl and applications work unmodified, reading through the store and writing server-owned values through the engine.

Index

Constants

View Source
const DefaultDeviceLimit = 100

DefaultDeviceLimit is the device-list page size when the caller gives none.

Variables

View Source
var (
	ErrInvalidAlias         = errors.New("Invalid alias")           //nolint:staticcheck // ST1005: detail strings are upstream astarte_appengine_api 1.2.2 wire text
	ErrAliasAlreadyInUse    = errors.New("Alias already in use")    //nolint:staticcheck // ST1005: upstream wire text
	ErrAliasTagNotFound     = errors.New("Alias tag not found")     //nolint:staticcheck // ST1005: upstream wire text
	ErrInvalidAttributes    = errors.New("Invalid attributes")      //nolint:staticcheck // ST1005: upstream wire text
	ErrAttributeKeyNotFound = errors.New("Attribute key not found") //nolint:staticcheck // ST1005: upstream wire text
)

Device-PATCH error taxonomy, frozen to upstream astarte_appengine_api 1.2.2 (FallbackController + ErrorView): the detail strings are what astartectl and the dashboard match on.

View Source
var ErrDeviceAlreadyInGroup = fmt.Errorf("Device already in group: %w", store.ErrAlreadyExists) //nolint:staticcheck // ST1005: upstream wire text

ErrDeviceAlreadyInGroup marks re-adding an existing member (maps to 409 "Device already in group"); it too satisfies store.ErrAlreadyExists.

View Source
var ErrGroupAlreadyExists = fmt.Errorf("Group already exists: %w", store.ErrAlreadyExists) //nolint:staticcheck // ST1005: upstream wire text

ErrGroupAlreadyExists marks a duplicate group name (maps to 409 "Group already exists"); it also satisfies store.ErrAlreadyExists so generic duplicate handling keeps working.

View Source
var ErrGroupNotFound = errors.New("Group not found") //nolint:staticcheck // ST1005: upstream wire text

ErrGroupNotFound marks a missing group (maps to 404 "Group not found").

View Source
var ErrPathNotFound = errors.New("appengine: path not found")

ErrPathNotFound marks an object-aggregated series whose concrete path holds no rows at all (upstream answers 404 "Path not found"; the interface root renders {} instead).

View Source
var ErrValidation = errors.New("appengine: validation failed")

ErrValidation wraps a well-formed request that violates a rule (maps to 422).

Functions

This section is empty.

Types

type API

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

API is the /appengine/v1 HTTP surface (docs/ROADMAP.md §8.2 file 7.8), guarded by a realm JWT carrying a_aea.

func NewAPI

func NewAPI(svc *Service, mw *auth.Middleware) *API

NewAPI wires the AppEngine service to its HTTP surface.

func (*API) Mount

func (a *API) Mount(mux *http.ServeMux)

Mount registers the routes on mux (paths wire-frozen to upstream astarte_appengine_api).

type DevicePage added in v0.2.0

type DevicePage struct {
	IDs      []string
	Statuses []*DeviceStatus
	Next     string
}

DevicePage is one device-list page: IDs, or full statuses when details was requested. Next is the next-page cursor ("" at the end).

type DevicePatch

type DevicePatch struct {
	Aliases              map[string]*string
	Attributes           map[string]*string
	CredentialsInhibited *bool
}

DevicePatch carries the JSON-merge-style PATCH fields (upstream DevicePatch): a nil pointer leaves a field unchanged, a present map patches aliases/attributes (a nil map value removes that key).

type DeviceStatus

type DeviceStatus struct {
	ID                   string                        `json:"id"`
	Connected            bool                          `json:"connected"`
	Introspection        map[string]introspectionEntry `json:"introspection"`
	Aliases              map[string]string             `json:"aliases"`
	Attributes           map[string]string             `json:"attributes"`
	Groups               []string                      `json:"groups"`
	CredentialsInhibited bool                          `json:"credentials_inhibited"`
	TotalReceivedMsgs    int64                         `json:"total_received_msgs"`
	TotalReceivedBytes   int64                         `json:"total_received_bytes"`
	FirstRegistration    *time.Time                    `json:"first_registration"`
	FirstCredentialsReq  *time.Time                    `json:"first_credentials_request"`
	LastConnection       *time.Time                    `json:"last_connection"`
	LastDisconnection    *time.Time                    `json:"last_disconnection"`
	LastSeenIP           string                        `json:"last_seen_ip,omitempty"`
	PreviousInterfaces   map[string]introspectionEntry `json:"previous_interfaces,omitempty"`
}

DeviceStatus is the AppEngine device status body (upstream DeviceStatusView): a JSON-friendly projection of a devices row.

type FieldErrors added in v0.2.0

type FieldErrors map[string][]string

FieldErrors carries per-field validation failures rendered as the Phoenix-changeset envelope {"errors":{"<field>":["<message>",...]}} (422).

func (FieldErrors) Error added in v0.2.0

func (fe FieldErrors) Error() string

type QueryOpts

type QueryOpts struct {
	Since                *time.Time
	SinceAfter           *time.Time
	To                   *time.Time
	Limit                int
	Descending           bool
	DownsamplePoints     int
	Format               string
	DownsampleKey        string
	RetrieveMetadata     bool
	AllowBigIntegers     *bool
	AllowSafeBigIntegers *bool
}

QueryOpts are the datastream query parameters (upstream since/since_after/ to/limit/downsample_to plus the rendering options). The zero value reads the whole series ascending and renders structured.

DownsamplePoints is the request's `downsample_to` value — a target *point count*, which is upstream's semantics. The bucket width is derived locally inside datastreamData once the series' own time span is known.

Format is one of structured/table/disjoint_tables ("structured" when the request omitted it). AllowBigIntegers/AllowSafeBigIntegers are nil when the request did not name them; they affect object-document rendering only.

type Sample

type Sample struct {
	Value     any       `json:"value"`
	Timestamp time.Time `json:"timestamp"`
}

Sample is one datastream point rendered for the wire.

type ServerData

type ServerData interface {
	PublishServerValue(ctx context.Context, realm string, id deviceid.ID, iface, path string, value json.RawMessage, ts *time.Time) error
	UnsetServerProperty(ctx context.Context, realm string, id deviceid.ID, iface, path string) error
}

ServerData is the engine port for server-owned writes (docs/ROADMAP.md §8.2 file 7.7). *engine.Engine satisfies it; tests substitute a fake.

type Service

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

Service implements the AppEngine business logic over the store and engine.

func NewService

func NewService(st *store.Store, sd ServerData, log *slog.Logger) *Service

NewService builds the service. sd may be nil (read-only deployments); log defaults to slog.Default().

func (*Service) AddGroupDevice

func (s *Service) AddGroupDevice(ctx context.Context, realm, name, deviceID string) error

AddGroupDevice adds a device to a group.

func (*Service) CreateGroup

func (s *Service) CreateGroup(ctx context.Context, realm, name string, devices []string) error

CreateGroup creates a group with its initial device membership (upstream POST /groups requires a non-empty device list). Blank-name and empty-device rejections live at the handler level as field errors; here every listed id must parse AND name an existing device row — a single failure rejects the whole body with the upstream changeset message, before any row is created.

func (*Service) DevicesStats added in v0.2.0

func (s *Service) DevicesStats(ctx context.Context, realm string) (total, connected int64, err error)

DevicesStats counts the realm's devices (upstream GET /stats/devices).

func (*Service) GetData

func (s *Service) GetData(ctx context.Context, realm, deviceID, ifaceName, path string, opts QueryOpts) (any, error)

GetData reads an interface endpoint (upstream GET .../interfaces/{iface} [/{path}]), dispatching on the interface type: a datastream series (with the query options) or a properties snapshot.

func (*Service) GetDataByAlias added in v0.2.0

func (s *Service) GetDataByAlias(ctx context.Context, realm, alias, ifaceName, path string, opts QueryOpts) (any, error)

GetDataByAlias reads an interface endpoint on an alias-addressed device (upstream GET /devices-by-alias/{alias}/interfaces/{iface}[/{path}]).

func (*Service) GetDataInGroup added in v0.2.0

func (s *Service) GetDataInGroup(ctx context.Context, realm, groupName, deviceID, ifaceName, path string, opts QueryOpts) (any, error)

GetDataInGroup reads an interface endpoint on a group member (upstream GET /groups/{group}/devices/{device}/interfaces/{iface}[/{path}]).

func (*Service) GetDevice

func (s *Service) GetDevice(ctx context.Context, realm, deviceID string) (*DeviceStatus, error)

GetDevice returns one device's status (upstream GET /devices/{id}).

func (*Service) GetDeviceByAlias

func (s *Service) GetDeviceByAlias(ctx context.Context, realm, alias string) (*DeviceStatus, error)

GetDeviceByAlias returns the status of the device owning an alias (upstream GET /devices-by-alias/{alias}).

func (*Service) GetGroup added in v0.2.0

func (s *Service) GetGroup(ctx context.Context, realm, name string) error

GetGroup resolves one group by name (upstream GET /groups/{group}).

func (*Service) GetGroupDevice added in v0.2.0

func (s *Service) GetGroupDevice(ctx context.Context, realm, groupName, deviceID string) (*DeviceStatus, error)

GetGroupDevice returns one member device's status (upstream GET /groups/{group}/devices/{device}).

func (*Service) ListDevices

func (s *Service) ListDevices(ctx context.Context, realm string, after string, limit int, details bool) (*DevicePage, error)

ListDevices returns one page of devices (upstream GET /devices). after is the cursor (exclusive); limit <= 0 selects DefaultDeviceLimit; details switches the page from bare IDs to full status objects (the dashboard's device list).

func (*Service) ListGroupDevices

func (s *Service) ListGroupDevices(ctx context.Context, realm, name string, details bool, fromToken string, limit int) (*DevicePage, error)

ListGroupDevices returns one page of the devices in a group — bare IDs, or full status objects with details (the dashboard's group page). The bare listing paginates with an OFFSET cursor carried inside a UUID-v1-format from_token (upstream's own tokens are insertion-time v1 uuids; any well-formed v1 string is accepted); details stays unpaginated.

func (*Service) ListGroups

func (s *Service) ListGroups(ctx context.Context, realm string) ([]string, error)

ListGroups returns the realm's group names.

func (*Service) ListInterfaces added in v0.2.0

func (s *Service) ListInterfaces(ctx context.Context, realm, deviceID string) ([]string, error)

ListInterfaces returns a device's introspection interface names, sorted ascending (upstream GET .../devices/{d}/interfaces).

func (*Service) ListInterfacesByAlias added in v0.2.0

func (s *Service) ListInterfacesByAlias(ctx context.Context, realm, alias string) ([]string, error)

ListInterfacesByAlias is ListInterfaces addressed by alias.

func (*Service) ListInterfacesInGroup added in v0.2.0

func (s *Service) ListInterfacesInGroup(ctx context.Context, realm, groupName, deviceID string) ([]string, error)

ListInterfacesInGroup is ListInterfaces inside a group (membership gate first).

func (*Service) PatchDevice

func (s *Service) PatchDevice(ctx context.Context, realm, deviceID string, p DevicePatch) (*DeviceStatus, error)

PatchDevice applies a device patch (upstream PATCH /devices/{id}).

func (*Service) PatchDeviceByAlias added in v0.2.0

func (s *Service) PatchDeviceByAlias(ctx context.Context, realm, alias string, p DevicePatch) (*DeviceStatus, error)

PatchDeviceByAlias applies a device patch addressed by alias (upstream PATCH /devices-by-alias/{alias}). Unknown alias behaves like GetDeviceByAlias.

func (*Service) PatchGroupDevice added in v0.2.0

func (s *Service) PatchGroupDevice(ctx context.Context, realm, groupName, deviceID string, p DevicePatch) (*DeviceStatus, error)

PatchGroupDevice applies a device patch inside a group (upstream PATCH /groups/{group}/devices/{device}). Unknown group → ErrGroupNotFound; non-member device behaves like an unknown device.

func (*Service) PublishData

func (s *Service) PublishData(ctx context.Context, realm, deviceID, ifaceName, path string, value json.RawMessage, ts *time.Time) error

PublishData writes a server-owned value (upstream PUT/POST .../interfaces/{iface}/{path}) through the engine. value is the unwrapped "data" JSON; ts is the optional explicit timestamp.

func (*Service) PublishDataByAlias added in v0.2.0

func (s *Service) PublishDataByAlias(ctx context.Context, realm, alias, ifaceName, path string, value json.RawMessage, ts *time.Time) error

PublishDataByAlias publishes through an alias address; the resolution gates access and carries the real device ID to the engine write.

func (*Service) PublishDataInGroup added in v0.2.0

func (s *Service) PublishDataInGroup(ctx context.Context, realm, groupName, deviceID, ifaceName, path string, value json.RawMessage, ts *time.Time) error

PublishDataInGroup publishes through a group address.

func (*Service) RemoveGroupDevice

func (s *Service) RemoveGroupDevice(ctx context.Context, realm, name, deviceID string) error

RemoveGroupDevice removes a device from a group.

func (*Service) UnsetProperty

func (s *Service) UnsetProperty(ctx context.Context, realm, deviceID, ifaceName, path string) error

UnsetProperty unsets a server-owned property (upstream DELETE .../interfaces/{iface}/{path}).

func (*Service) UnsetPropertyByAlias added in v0.2.0

func (s *Service) UnsetPropertyByAlias(ctx context.Context, realm, alias, ifaceName, path string) error

UnsetPropertyByAlias unsets through an alias address.

func (*Service) UnsetPropertyInGroup added in v0.2.0

func (s *Service) UnsetPropertyInGroup(ctx context.Context, realm, groupName, deviceID, ifaceName, path string) error

UnsetPropertyInGroup unsets through a group address.

type Tabular added in v0.2.0

type Tabular struct {
	Data     any            `json:"-"`
	Metadata map[string]any `json:"metadata"`
}

Tabular carries a format=table payload plus its metadata object; serveData detects it and renders {data, metadata} instead of bare data.

Directories

Path Synopsis
Package channels implements the upstream Phoenix Channels V2 wire protocol served at /appengine/v1/socket/websocket.
Package channels implements the upstream Phoenix Channels V2 wire protocol served at /appengine/v1/socket/websocket.
Package stream is the AppEngine live event socket (docs/DESIGN.md §3.7, §1.1 deviation; ROADMAP §8.2 file 7.9): a WebSocket (with an SSE fallback) at /astrate/v1/{realm}/socket fed by the engine's in-process fan-out bus.
Package stream is the AppEngine live event socket (docs/DESIGN.md §3.7, §1.1 deviation; ROADMAP §8.2 file 7.9): a WebSocket (with an SSE fallback) at /astrate/v1/{realm}/socket fed by the engine's in-process fan-out bus.

Jump to

Keyboard shortcuts

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