feat: add scoped workflow instance queries and SDK pagination
This commit is contained in:
75
internal/transport/httpserver/workflow_query.go
Normal file
75
internal/transport/httpserver/workflow_query.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.yuntuo.hk/k2safe/orbit/internal/fault"
|
||||
core "git.yuntuo.hk/k2safe/orbit/model"
|
||||
"git.yuntuo.hk/k2safe/orbit/platform/auth"
|
||||
"git.yuntuo.hk/k2safe/orbit/weave/instance"
|
||||
weave "git.yuntuo.hk/k2safe/orbit/weave/model"
|
||||
)
|
||||
|
||||
func workflowListQuery(r *http.Request) (instance.ListInput, error) {
|
||||
var in instance.ListInput
|
||||
q, err := url.ParseQuery(r.URL.RawQuery)
|
||||
if err != nil {
|
||||
return in, fault.Invalid("invalid query encoding")
|
||||
}
|
||||
for k, v := range q {
|
||||
if len(v) != 1 {
|
||||
return in, fault.Invalid("duplicate query parameter")
|
||||
}
|
||||
switch k {
|
||||
case "definition_id", "version", "state", "paused", "business_key", "created_from", "created_before", "limit", "cursor":
|
||||
default:
|
||||
return in, fault.Invalid("unknown query parameter")
|
||||
}
|
||||
if v[0] == "" {
|
||||
return in, fault.Invalid("empty query parameter")
|
||||
}
|
||||
}
|
||||
in.DefinitionID = core.ID(q.Get("definition_id"))
|
||||
in.Version = q.Get("version")
|
||||
in.State = weave.InstanceState(q.Get("state"))
|
||||
in.BusinessKey = q.Get("business_key")
|
||||
in.Cursor = q.Get("cursor")
|
||||
if q.Has("limit") {
|
||||
in.Limit, err = strconv.Atoi(q.Get("limit"))
|
||||
if err != nil || in.Limit <= 0 {
|
||||
return in, fault.Invalid("invalid limit")
|
||||
}
|
||||
}
|
||||
if q.Has("paused") {
|
||||
v := q.Get("paused")
|
||||
if v != "true" && v != "false" {
|
||||
return in, fault.Invalid("paused must be true or false")
|
||||
}
|
||||
b := v == "true"
|
||||
in.Paused = &b
|
||||
}
|
||||
for name, field := range map[string]**time.Time{"created_from": &in.CreatedFrom, "created_before": &in.CreatedBefore} {
|
||||
if !q.Has(name) {
|
||||
continue
|
||||
}
|
||||
v, err := time.Parse(time.RFC3339Nano, q.Get(name))
|
||||
if err != nil {
|
||||
return in, fault.Invalid("invalid creation time bound")
|
||||
}
|
||||
*field = &v
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
func registerWorkflowQuery(mux *http.ServeMux, s *instance.Service) {
|
||||
mux.HandleFunc("GET /v1/workflows", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
in, err := workflowListQuery(r)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.List(r.Context(), t, in)
|
||||
return out, 200, err
|
||||
}))
|
||||
}
|
||||
21
internal/transport/httpserver/workflow_query_test.go
Normal file
21
internal/transport/httpserver/workflow_query_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWorkflowListQueryStrictFilters(t *testing.T) {
|
||||
for _, query := range []string{"limit=0", "limit=", "limit=x", "state=", "state=RUNNING&state=WAITING", "paused=0", "paused=True", "unknown=x", "business_key=", "created_from=tomorrow", "version=%zz", "state=WAITING;limit=2"} {
|
||||
r := httptest.NewRequest("GET", "/v1/workflows?"+query, nil)
|
||||
if _, err := workflowListQuery(r); err == nil {
|
||||
t.Fatal("accepted", query)
|
||||
}
|
||||
}
|
||||
q := url.Values{"business_key": {"关联 +/%\n "}, "paused": {"false"}, "created_from": {"2026-09-18T08:00:00.123456+08:00"}, "limit": {"2"}}
|
||||
in, err := workflowListQuery(httptest.NewRequest("GET", "/v1/workflows?"+q.Encode(), nil))
|
||||
if err != nil || in.BusinessKey != q.Get("business_key") || in.Paused == nil || *in.Paused || in.Limit != 2 || in.CreatedFrom.UTC().Hour() != 0 {
|
||||
t.Fatal(in, err)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
func RegisterWorkflows(server *http.Server, instances *instance.Service, controls *control.Service, signals *signal.Service, reviews *recovery.Service) {
|
||||
mux := server.Handler.(*http.ServeMux)
|
||||
registerWorkflowHistory(mux, instances, signals)
|
||||
registerWorkflowQuery(mux, instances)
|
||||
mux.HandleFunc("POST /v1/workflows", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
if _, err := historyQuery(r, false); err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
Reference in New Issue
Block a user