source

package
v1.0.54 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package source wraps the open-dingtalk Stream SDK and exposes a small blocking Start interface plus a connection state machine. The SDK is the only place in the bus that talks to the cloud; the rest of the bus stays vendor-agnostic and would be drop-in replaceable for a different Stream provider.

Index

Constants

View Source
const (
	PortalTicketModeNormal = "normal"
	PortalTicketModeCustom = "custom"
)

Variables

View Source
var IdleAfter = 60 * time.Second

IdleAfter is the duration of no events after which StateConnected transitions to StateIdle. Exposed for tests to bypass real-time waits.

Functions

This section is empty.

Types

type AccessTokenProvider added in v1.0.54

type AccessTokenProvider func(context.Context) (string, error)

type Config

type Config struct {
	ClientID     string
	ClientSecret string
	// PortalTicket switches the source to the portal-managed user Stream
	// ticket flow. When set, Start fetches endpoint+ticket over HTTP and
	// dials the returned WebSocket directly instead of asking the SDK to
	// open an app-credential connection.
	PortalTicket *PortalTicketConfig
	// Now is injected for tests. Defaults to time.Now when nil.
	Now func() time.Time
}

Config carries the credentials and behavioural knobs needed to construct a DingtalkSource. ClientID/ClientSecret are required for app-credential SDK mode. Portal ticket normal mode uses portal-side managed credentials, so it does not require local app credentials.

type DingtalkSource

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

DingtalkSource is the cloud-side adapter. It owns one StreamClient and one state Machine; lifecycle is bounded by the context passed to Start.

func New

func New(cfg Config, _ ...SourceOption) (*DingtalkSource, error)

New constructs a DingtalkSource. Returns an error if required Config fields are missing — keep the boundary tight so misconfiguration fails loudly rather than at first-event time.

func (*DingtalkSource) Start

func (s *DingtalkSource) Start(ctx context.Context, emit dwsevent.EmitFn) error

Start opens the Stream WebSocket and blocks until ctx is cancelled or a fatal SDK error occurs. Events are delivered to emit synchronously from the SDK callback goroutine — emit MUST return immediately (the SDK's processLoop is single-goroutine; see plan invariant #1 and P0 §1 row 2).

Blocking semantics: the underlying StreamClient.Start returns as soon as dial succeeds (it spawns its own processLoop goroutine), so we wait on ctx.Done() before returning. cli.Close() is always called on exit; ctx.Err is returned for context cancellation, otherwise the underlying error.

func (*DingtalkSource) State

func (s *DingtalkSource) State() Snapshot

State returns the current Snapshot of the connection state machine.

type ForceRefreshTokenFn added in v1.0.54

type ForceRefreshTokenFn func(ctx context.Context, rejectedToken string) (string, error)

ForceRefreshTokenFn rotates an access token that the server has just rejected (HTTP 401). It receives the exact rejected token so the caller's compare-and-refresh logic can skip the refresh when another goroutine has already rotated it, and returns the fresh token to retry with. Optional: when nil a 401 stays fatal, matching the previous behavior.

type Machine

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

Machine tracks the connection state. Methods are safe for concurrent use.

func NewMachine

func NewMachine() *Machine

NewMachine returns a Machine in the StateDisconnected initial state.

func (*Machine) OnConnected

func (m *Machine) OnConnected()

OnConnected moves to StateConnected. Called when SDK Start returns nil.

func (*Machine) OnConnecting

func (m *Machine) OnConnecting()

OnConnecting moves to StateConnecting. Called by Source.Start before the SDK dial completes.

func (*Machine) OnEvent

func (m *Machine) OnEvent()

OnEvent records the timestamp of the most recently received event. Resets any Reconnecting or Idle state back to Connected (the connection is demonstrably alive).

func (*Machine) OnReconnect

func (m *Machine) OnReconnect()

OnReconnect marks a reconnect signal observed (from SDK log or future hook). Increments the counter and stamps the time.

func (*Machine) OnStopped

func (m *Machine) OnStopped()

OnStopped marks the SDK as closed (graceful exit or Close()).

func (*Machine) Snapshot

func (m *Machine) Snapshot() Snapshot

Snapshot returns the current state view atomically.

func (*Machine) State

func (m *Machine) State() State

State returns the current effective state, applying time-based idle detection on top of the stored state. This is the public read accessor used by `event status` formatting.

type PersonalConfig

type PersonalConfig struct {
	AccessToken         string
	AccessTokenProvider AccessTokenProvider
	ForceRefreshToken   ForceRefreshTokenFn
	ClientID            string
	ClientSecret        string
	SourceID            string
	TicketURL           string
	TicketMode          string
	HTTPClient          *http.Client
	WebSocketDialer     *websocket.Dialer
	Now                 func() time.Time
	ReconnectMin        time.Duration
	ReconnectMax        time.Duration
}

type PersonalSource

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

func NewPersonal

func NewPersonal(cfg PersonalConfig) (*PersonalSource, error)

func (*PersonalSource) Start

func (s *PersonalSource) Start(ctx context.Context, emit dwsevent.EmitFn) error

func (*PersonalSource) State

func (s *PersonalSource) State() Snapshot

type PortalTicketConfig

type PortalTicketConfig struct {
	TicketURL           string
	AccessToken         string
	AccessTokenProvider AccessTokenProvider
	ForceRefreshToken   ForceRefreshTokenFn
	SourceID            string
	Mode                string
	ClientID            string
	ClientSecret        string
	UserAgent           string
	HTTPClient          *http.Client
	WebSocketDialer     *websocket.Dialer
	ReconnectMin        time.Duration
	ReconnectMax        time.Duration
	DisableReconnect    bool
}

PortalTicketConfig describes the portal-managed user Stream ticket flow. normal mode uses portal-side managed credentials; custom mode asks portal to open the user connection with the caller-provided clientId/clientSecret.

func (*PortalTicketConfig) Valid

func (c *PortalTicketConfig) Valid() error

type Snapshot

type Snapshot struct {
	State           State      `json:"state"`
	StateSource     SourceKind `json:"state_source"`
	LastEventAt     time.Time  `json:"last_event_at,omitempty"`
	LastReconnectAt time.Time  `json:"last_reconnect_at,omitempty"`
	ReconnectCount  int        `json:"reconnect_count"`
}

Snapshot returns the full state view for status rendering: state + source + last event/reconnect timestamps + reconnect count.

type SourceKind

type SourceKind string

SourceKind identifies whether the state was reported by an authoritative SDK hook or inferred from observable side-channels.

const (
	// SourceHook is reserved for future SDK versions that expose
	// ConnectionState / keepalive callbacks. v1 does not emit this.
	SourceHook SourceKind = "hook"
	// SourceInferred is the only value emitted by v1.
	SourceInferred SourceKind = "inferred"
)

type SourceOption

type SourceOption func(*sourceConfig)

SourceOptions is the functional-option type reserved for future overlay extensions (e.g. inject trace IDs into emit, swap the underlying client for a fake). v1 takes no options; the type exists so callers can write `New(cfg)` today and `New(cfg, WithFoo())` tomorrow without an API break. See plan §7 Edition 扩展点.

type State

type State string

State enumerates the connection states surfaced by `dws event status`.

Stream SDK v0.9.1 does not expose a user-registerable ConnectionState hook (verified P0 §1 escape-hatch table row #4), so the state machine runs in "inferred" mode: it derives state from observable signals — Start() return value, last-event-received timestamp, last-error timestamp, SDK reconnect log lines parsed by the source layer. The Source field `state_source` (env-style annotation) is emitted alongside the state so downstream tools can distinguish authoritative from inferred values when SDK hook support arrives in a future release.

const (
	// StateDisconnected is the zero state before Start has been called.
	StateDisconnected State = "disconnected"
	// StateConnecting is set on entry to Start, before SDK dial completes.
	StateConnecting State = "connecting"
	// StateConnected is set when Start returns nil (dial succeeded).
	StateConnected State = "connected"
	// StateIdle is set when the connection is still up but no event has been
	// observed for a quiet window (default 60s). Distinguishes "everything
	// fine, just no traffic" from "looks connected but broken".
	StateIdle State = "idle"
	// StateReconnecting is set when the SDK has signalled a reconnect (via
	// log line parse or future hook). Cleared on next event received.
	StateReconnecting State = "reconnecting"
	// StateDegraded is set when reconnects keep happening but no events flow.
	// Useful to surface "looks alive, actually broken" cases via status.
	StateDegraded State = "degraded"
	// StateStopped is set after Close() / context cancellation.
	StateStopped State = "stopped"
)

Jump to

Keyboard shortcuts

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