Files
orbit/sdk/go/workflow_events.go

89 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package orbit
import (
"context"
"encoding/json"
"errors"
"time"
)
// WorkflowEventSummary 只表示已保存的事件,不把当前状态或当前激活反填到历史。
type WorkflowEventSummary struct {
ID string `json:"id"` // ID 原不可变状态事件标识。
WorkflowID string `json:"workflow_id"` // WorkflowID 已核验授权的流程归属。
AggregateKind string `json:"aggregate_kind"` // AggregateKind workflow、workflow_step或workflow_signal。
AggregateID string `json:"aggregate_id"` // AggregateID 产生事件的实例、步骤或信号身份。
AggregateRevision uint64 `json:"aggregate_revision,string"` // AggregateRevision 来源资源版本,各资源独立,不是全局序号。
Type string `json:"type"` // Type 原始事件类型,不按当前状态改写。
StateScope string `json:"state_scope"` // StateScope WORKFLOW、STEP或SIGNAL,说明State记录的是哪类对象。
State string `json:"state"` // State 该事件保存的状态,不代表对象当前状态。
StepKey string `json:"step_key,omitempty"` // StepKey 步骤事件的固定定义名称;其他事件省略。
OccurredAt time.Time `json:"occurred_at"` // OccurredAt 数据库事件记录时间,UTC微秒精度,不是外部业务发生时间或提交水位。
}
type WorkflowEventPage struct {
WorkflowID string `json:"workflow_id"` // WorkflowID 本页授权的实例身份。
Revision uint64 `json:"revision,string"` // Revision 本页读取时实例版本,不能作为信号或步骤事件的消费水位。
Items []WorkflowEventSummary `json:"items"` // Items 按记录时间和ID倒序的有界摘要,空列表为[]。
NextCursor string `json:"next_cursor,omitempty"` // NextCursor 绑定实例与事件查询种类,不承诺跨页快照。
}
type WorkflowEventDetail struct {
WorkflowEventSummary
Payload json.RawMessage `json:"payload"` // Payload 原不可变事件元数据,最多64KiB;嵌套数字保留原文本,输入/结果另走资源详情。
}
// ListWorkflowEvents 读取实例、步骤和信号的不可变状态事件,需要workflows:audit:read。
func (c *Client) ListWorkflowEvents(ctx context.Context, id string, in HistoryPageRequest) (WorkflowEventPage, error) {
var out WorkflowEventPage
if !validID(id) {
return out, errors.New("invalid workflow id")
}
query, err := historyPageQuery(in)
if err != nil {
return out, err
}
if err = c.call(ctx, "GET", "/v1/workflows/"+id+"/events"+query, "", nil, &out); err != nil {
return WorkflowEventPage{}, err
}
if out.WorkflowID != id || out.Revision == 0 || out.Items == nil || !historyResponseSize(len(out.Items), in) {
return WorkflowEventPage{}, errors.New("invalid workflow event page")
}
for i, e := range out.Items {
if !validWorkflowEvent(e, id) || i > 0 && (e.OccurredAt.After(out.Items[i-1].OccurredAt) || e.OccurredAt.Equal(out.Items[i-1].OccurredAt) && e.ID >= out.Items[i-1].ID) {
return WorkflowEventPage{}, errors.New("invalid workflow event summary or ordering")
}
}
return out, nil
}
func validWorkflowEvent(e WorkflowEventSummary, id string) bool {
if e.WorkflowID != id || !validID(e.ID) || !validID(e.AggregateID) || e.AggregateRevision == 0 || e.Type == "" || e.State == "" || e.OccurredAt.IsZero() {
return false
}
switch e.AggregateKind {
case "workflow":
if e.AggregateID != id {
return false
}
case "workflow_step":
if !workflowVersionName(e.StepKey) {
return false
}
case "workflow_signal":
default:
return false
}
return e.StateScope == "WORKFLOW" || e.StateScope == "STEP" || e.StateScope == "SIGNAL"
}
func (c *Client) GetWorkflowEvent(ctx context.Context, id, event string) (WorkflowEventDetail, error) {
var out WorkflowEventDetail
if !validID(id) || !validID(event) {
return out, errors.New("invalid workflow event identity")
}
if err := c.call(ctx, "GET", "/v1/workflows/"+id+"/events/"+event, "", nil, &out); err != nil {
return WorkflowEventDetail{}, err
}
if out.ID != event || !validWorkflowEvent(out.WorkflowEventSummary, id) || len(out.Payload) == 0 || len(out.Payload) > 65536 || !json.Valid(out.Payload) {
return WorkflowEventDetail{}, errors.New("invalid workflow event detail")
}
return out, nil
}