120 lines
4.1 KiB
Go
120 lines
4.1 KiB
Go
// Package flowdemo 仅为可运行流程示例提供初始化和有界等待,不属于公开SDK或业务恢复策略。
|
||
package flowdemo
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"os"
|
||
"time"
|
||
|
||
orbit "git.yuntuo.hk/k2safe/orbit/sdk/go"
|
||
)
|
||
|
||
func Client() (*orbit.Client, string, string, error) {
|
||
raw, err := os.ReadFile(os.Getenv("ORBIT_CREDENTIALS_FILE"))
|
||
if err != nil {
|
||
return nil, "", "", err
|
||
}
|
||
var credentials struct {
|
||
Token string `json:"token"`
|
||
QueueID string `json:"queue_id"`
|
||
}
|
||
if json.Unmarshal(raw, &credentials) != nil || credentials.Token == "" || credentials.QueueID == "" {
|
||
return nil, "", "", errors.New("invalid private credentials file")
|
||
}
|
||
client, err := orbit.NewClient(os.Getenv("ORBIT_URL"), orbit.Options{Token: credentials.Token})
|
||
if err != nil {
|
||
return nil, "", "", err
|
||
}
|
||
operation, err := orbit.NewWorkerMessageID()
|
||
return client, credentials.QueueID, operation, err
|
||
}
|
||
|
||
// PublishStart 每次示例运行创建独立身份;不将重新运行命令当作未知响应的原键恢复。
|
||
func PublishStart(ctx context.Context, api *orbit.Client, operation, name string, spec orbit.WorkflowSpecification, input json.RawMessage) (orbit.WorkflowInstance, error) {
|
||
var empty orbit.WorkflowInstance
|
||
fmt.Printf("scenario=%s operation=%s\n", name, operation)
|
||
created, err := api.CreateWorkflowDefinition(ctx, operation, orbit.CreateWorkflowDefinitionRequest{Name: name + "." + operation, Description: "controlled standalone workflow example"})
|
||
if err != nil {
|
||
return empty, err
|
||
}
|
||
if _, err = api.PublishWorkflowVersion(ctx, created.Definition.ID, operation, orbit.PublishWorkflowVersionRequest{Version: "v1", Specification: spec, Reason: "publish controlled SDK example"}); err != nil {
|
||
return empty, err
|
||
}
|
||
result, err := api.StartWorkflow(ctx, operation, orbit.StartWorkflowRequest{DefinitionID: created.Definition.ID, Version: "v1", BusinessKey: operation, Input: input})
|
||
if err != nil {
|
||
return empty, err
|
||
}
|
||
fmt.Printf("workflow=%s version=%s\n", result.Instance.ID, result.Instance.Version)
|
||
return result.Instance, nil
|
||
}
|
||
|
||
// Runner 持有一个真实Worker及其结束通知,等待时同时检测Worker异常退出。
|
||
type Runner struct {
|
||
Worker *orbit.Worker
|
||
stop context.CancelFunc
|
||
done chan struct{}
|
||
err error // err 仅在done关闭后读取,关闭通道建立同步关系。
|
||
}
|
||
|
||
func StartWorker(ctx context.Context, api *orbit.Client, queue string, handlers ...orbit.WorkerHandler) (*Runner, error) {
|
||
w, err := orbit.NewWorker(api, orbit.WorkerOptions{QueueID: queue, Capacity: 1, ShutdownTimeout: 5 * time.Second, Handlers: handlers})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
workerCtx, stop := context.WithCancel(ctx)
|
||
r := &Runner{Worker: w, stop: stop, done: make(chan struct{})}
|
||
go func() { r.err = w.Run(workerCtx); close(r.done) }()
|
||
return r, nil
|
||
}
|
||
|
||
func (r *Runner) Close() error {
|
||
r.stop()
|
||
<-r.done
|
||
return r.err
|
||
}
|
||
|
||
func (r *Runner) Wait(ctx context.Context, check func() (bool, error)) error {
|
||
for {
|
||
ready, err := check()
|
||
if err != nil || ready {
|
||
return err
|
||
}
|
||
select {
|
||
case <-ctx.Done():
|
||
return ctx.Err()
|
||
case <-r.done:
|
||
return fmt.Errorf("example Worker stopped before expected state: %v", r.err)
|
||
case <-time.After(100 * time.Millisecond):
|
||
}
|
||
}
|
||
}
|
||
|
||
func (r *Runner) Step(ctx context.Context, api *orbit.Client, workflow, key string, predicate func(orbit.WorkflowStepDetail) bool) (orbit.WorkflowStepDetail, error) {
|
||
var out orbit.WorkflowStepDetail
|
||
err := r.Wait(ctx, func() (bool, error) {
|
||
var err error
|
||
out, err = api.GetWorkflowStep(ctx, workflow, key)
|
||
return err == nil && predicate(out), err
|
||
})
|
||
return out, err
|
||
}
|
||
|
||
func (r *Runner) Workflow(ctx context.Context, api *orbit.Client, workflow, state string) (orbit.WorkflowInstance, error) {
|
||
var out orbit.WorkflowInstance
|
||
err := r.Wait(ctx, func() (bool, error) {
|
||
var err error
|
||
out, err = api.GetWorkflow(ctx, workflow)
|
||
if err != nil || out.State == state {
|
||
return err == nil, err
|
||
}
|
||
if out.State == "NEEDS_REVIEW" || out.State == "FAILED" || out.State == "CANCELLED" || out.State == "SUCCEEDED" {
|
||
return false, fmt.Errorf("unexpected workflow state %s; wanted %s", out.State, state)
|
||
}
|
||
return false, nil
|
||
})
|
||
return out, err
|
||
}
|