Merge pull request 'operator-channel.asks answers open asks within a bound when asked with no argument (issue 1974)' (#621) from fix/1974-asks-default-bounded into main
This commit was merged in pull request #621.
This commit is contained in:
@@ -1418,27 +1418,35 @@ func (h *Holder) AsksNow() []AskView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AsksQuery narrows what the tool `operator-channel.asks` answers (issue 595): Key one ask, State open (open and held) or
|
// AsksQuery narrows what the tool `operator-channel.asks` answers (issue 595): Key one ask, State open (open and held) or
|
||||||
// ended, Since the asks opened at or after that time, Bound the most bytes of the answer's asks. The zero query keeps the
|
// ended (all takes both), Since the asks opened at or after that time, Bound the most bytes of the answer's asks. With no
|
||||||
// whole answer, as before.
|
// argument at all the query is the open asks within DefaultAsksBound (issue 1974), and the answer says so.
|
||||||
type AsksQuery struct {
|
type AsksQuery struct {
|
||||||
Key, State string
|
Key, State string
|
||||||
Since time.Time
|
Since time.Time
|
||||||
Bound int
|
Bound int
|
||||||
|
// Defaulted is set by AsksQueryOf when no key, state or since was given: the open asks only.
|
||||||
|
Defaulted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultAsksBound is the most bytes of asks an answer holds when the caller gives no bound (issue 1974: the whole of a
|
||||||
|
// month's asks was 737,934 characters, more than a session reads).
|
||||||
|
const DefaultAsksBound = 32768
|
||||||
|
|
||||||
// AsksAnswer is the answer of a narrowed query: the asks kept, how many the filters matched, and, when the bound cut the
|
// AsksAnswer is the answer of a narrowed query: the asks kept, how many the filters matched, and, when the bound cut the
|
||||||
// answer short, that it did and how.
|
// answer short, that it did and how.
|
||||||
type AsksAnswer struct {
|
type AsksAnswer struct {
|
||||||
Asks []AskView `json:"asks"`
|
Asks []AskView `json:"asks"`
|
||||||
Matched int `json:"matched"`
|
Matched int `json:"matched"`
|
||||||
Cut string `json:"cut,omitempty"`
|
Cut string `json:"cut,omitempty"`
|
||||||
|
// Default says that no key, state or since was given, so only the open asks are answered, and how to ask for more.
|
||||||
|
Default string `json:"default,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsksQueryOf reads the arguments of the tool; an empty set is the zero query.
|
// AsksQueryOf reads the arguments of the tool; an empty set is the zero query.
|
||||||
func AsksQueryOf(a map[string]any) (AsksQuery, error) {
|
func AsksQueryOf(a map[string]any) (AsksQuery, error) {
|
||||||
q := AsksQuery{Key: strArg(a, "key"), State: strArg(a, "state")}
|
q := AsksQuery{Key: strArg(a, "key"), State: strArg(a, "state")}
|
||||||
if q.State != "" && q.State != "open" && q.State != "ended" {
|
if q.State != "" && q.State != "open" && q.State != "ended" && q.State != "all" {
|
||||||
return q, errorf("state is open or ended, not %q", q.State)
|
return q, errorf("state is open, ended or all, not %q", q.State)
|
||||||
}
|
}
|
||||||
if since := strArg(a, "since"); since != "" {
|
if since := strArg(a, "since"); since != "" {
|
||||||
t, err := time.Parse(time.RFC3339, since)
|
t, err := time.Parse(time.RFC3339, since)
|
||||||
@@ -1453,6 +1461,12 @@ func AsksQueryOf(a map[string]any) (AsksQuery, error) {
|
|||||||
}
|
}
|
||||||
q.Bound = int(v)
|
q.Bound = int(v)
|
||||||
}
|
}
|
||||||
|
if q.Key == "" && q.State == "" && q.Since.IsZero() {
|
||||||
|
q.State, q.Defaulted = "open", true
|
||||||
|
}
|
||||||
|
if q.Bound == 0 {
|
||||||
|
q.Bound = DefaultAsksBound
|
||||||
|
}
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1461,6 +1475,10 @@ func AsksQueryOf(a map[string]any) (AsksQuery, error) {
|
|||||||
func (q AsksQuery) Narrow(all []AskView) AsksAnswer {
|
func (q AsksQuery) Narrow(all []AskView) AsksAnswer {
|
||||||
var out AsksAnswer
|
var out AsksAnswer
|
||||||
out.Asks = []AskView{}
|
out.Asks = []AskView{}
|
||||||
|
if q.Defaulted {
|
||||||
|
out.Default = "no key, state or since was given, so only the open asks are shown: state ended or all for the ended ones, " +
|
||||||
|
"key for one ask, since for the asks opened from a time"
|
||||||
|
}
|
||||||
var kept []AskView
|
var kept []AskView
|
||||||
for _, v := range all {
|
for _, v := range all {
|
||||||
open := v.State == StateOpen || v.State == StateHeld
|
open := v.State == StateOpen || v.State == StateHeld
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ func TestAHeldAskNamesEveryKindAndWhyWhenNoChannelIsReady(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Issue 595: operator-channel.asks answers one ask, the open or the ended, those opened from a time, and no more bytes than a
|
// Issue 595: operator-channel.asks answers one ask, the open or the ended, those opened from a time, and no more bytes than a
|
||||||
// bound, said when it cut the answer short; with no argument it keeps the whole answer.
|
// bound, said when it cut the answer short; with no argument it answers the open asks only, within a default bound, and says so (issue 1974).
|
||||||
func TestTheAsksVerbNarrowsItsAnswer(t *testing.T) {
|
func TestTheAsksVerbNarrowsItsAnswer(t *testing.T) {
|
||||||
view := func(key, state, opened string) AskView {
|
view := func(key, state, opened string) AskView {
|
||||||
return AskView{Key: key, Asker: "mesh-delivery", Headline: "Approve " + key, State: state, Opened: opened}
|
return AskView{Key: key, Asker: "mesh-delivery", Headline: "Approve " + key, State: state, Opened: opened}
|
||||||
@@ -159,13 +159,15 @@ func TestTheAsksVerbNarrowsItsAnswer(t *testing.T) {
|
|||||||
cut bool
|
cut bool
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{"no argument is the zero query", map[string]any{}, nil, 0, false, false},
|
{"no argument is the open asks, said", map[string]any{}, []string{"a.3", "a.2"}, 2, false, false},
|
||||||
|
{"state all is every ask", map[string]any{"state": "all"}, []string{"a.3", "a.2", "a.1", "a.0"}, 4, false, false},
|
||||||
|
{"a key finds an ended ask whatever its state", map[string]any{"key": "a.0"}, []string{"a.0"}, 1, false, false},
|
||||||
{"key", map[string]any{"key": "a.1"}, []string{"a.1"}, 1, false, false},
|
{"key", map[string]any{"key": "a.1"}, []string{"a.1"}, 1, false, false},
|
||||||
{"state open takes open and held", map[string]any{"state": "open"}, []string{"a.3", "a.2"}, 2, false, false},
|
{"state open takes open and held", map[string]any{"state": "open"}, []string{"a.3", "a.2"}, 2, false, false},
|
||||||
{"state ended", map[string]any{"state": "ended"}, []string{"a.1", "a.0"}, 2, false, false},
|
{"state ended", map[string]any{"state": "ended"}, []string{"a.1", "a.0"}, 2, false, false},
|
||||||
{"since", map[string]any{"since": "2026-11-02T12:00:00Z"}, []string{"a.3", "a.2"}, 2, false, false},
|
{"since", map[string]any{"since": "2026-11-02T12:00:00Z"}, []string{"a.3", "a.2"}, 2, false, false},
|
||||||
{"bound cuts and says so", map[string]any{"bound": float64(2*len(one) + 10), "state": "open"}, []string{"a.3", "a.2"}, 2, false, false},
|
{"bound cuts and says so", map[string]any{"bound": float64(2*len(one) + 10), "state": "open"}, []string{"a.3", "a.2"}, 2, false, false},
|
||||||
{"a bound under the asks cuts", map[string]any{"bound": float64(len(one) + 5)}, []string{"a.3"}, 4, true, false},
|
{"a bound under the asks cuts", map[string]any{"bound": float64(len(one) + 5)}, []string{"a.3"}, 2, true, false},
|
||||||
{"a state that is neither", map[string]any{"state": "done"}, nil, 0, false, true},
|
{"a state that is neither", map[string]any{"state": "done"}, nil, 0, false, true},
|
||||||
{"a time that is not one", map[string]any{"since": "yesterday"}, nil, 0, false, true},
|
{"a time that is not one", map[string]any{"since": "yesterday"}, nil, 0, false, true},
|
||||||
} {
|
} {
|
||||||
@@ -177,13 +179,10 @@ func TestTheAsksVerbNarrowsItsAnswer(t *testing.T) {
|
|||||||
if tc.err {
|
if tc.err {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tc.want == nil {
|
|
||||||
if q != (AsksQuery{}) {
|
|
||||||
t.Errorf("%s: %+v is not the zero query, which keeps today's answer", tc.name, q)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
got := q.Narrow(all)
|
got := q.Narrow(all)
|
||||||
|
if (got.Default != "") != (tc.args["key"] == nil && tc.args["state"] == nil && tc.args["since"] == nil) || q.Bound == 0 {
|
||||||
|
t.Errorf("%s: default %q, bound %d: only no argument is defaulted, and every query is bounded", tc.name, got.Default, q.Bound)
|
||||||
|
}
|
||||||
var keys []string
|
var keys []string
|
||||||
for _, v := range got.Asks {
|
for _, v := range got.Asks {
|
||||||
keys = append(keys, v.Key)
|
keys = append(keys, v.Key)
|
||||||
|
|||||||
@@ -734,12 +734,13 @@ func tools(h *Holder, l *listening) []stdio.Tool {
|
|||||||
{Name: "operator-channel.asks",
|
{Name: "operator-channel.asks",
|
||||||
Description: "The asks this router holds (novox/hq ADR 0259): open (or held, with since when and why no channel can carry it yet) first, then the last month's — each " +
|
Description: "The asks this router holds (novox/hq ADR 0259): open (or held, with since when and why no channel can carry it yet) first, then the last month's — each " +
|
||||||
"one's asker, headline, state, when it opened and expires, the channels it was offered on, how it " +
|
"one's asker, headline, state, when it opened and expires, the channels it was offered on, how it " +
|
||||||
"ended (the warrant: who chose what, through which channel), and whether its asker was told. With no argument every " +
|
"ended (the warrant: who chose what, through which channel), and whether its asker was told. With no argument " +
|
||||||
"ask, which is large; filter it: key for one ask, state open or ended, since (a time, RFC 3339) for the asks opened " +
|
"only the open asks, within 32768 bytes; the answer says what it cut and how to ask for more: key for one ask " +
|
||||||
"from then, and bound, the most bytes of the answer, which says when it cut it short.",
|
"(any state), state open, ended or all, since (a time, RFC 3339) for the asks opened from then, and bound, the " +
|
||||||
|
"most bytes of the answer.",
|
||||||
Input: map[string]any{
|
Input: map[string]any{
|
||||||
"key": str("one ask, by its key (asker.id); none for every ask"),
|
"key": str("one ask, by its key (asker.id); none for the open asks"),
|
||||||
"state": str("open (open and held) or ended; none for both"),
|
"state": str("open (open and held), ended or all; none is open when no key or since is given, else both"),
|
||||||
"since": str("only the asks opened at or after this time, RFC 3339"),
|
"since": str("only the asks opened at or after this time, RFC 3339"),
|
||||||
"bound": map[string]any{"type": "integer", "description": "the most bytes of the asks in the answer; said when it cuts the answer short"}},
|
"bound": map[string]any{"type": "integer", "description": "the most bytes of the asks in the answer; said when it cuts the answer short"}},
|
||||||
Run: func(a map[string]any) (any, error) {
|
Run: func(a map[string]any) (any, error) {
|
||||||
@@ -747,9 +748,6 @@ func tools(h *Holder, l *listening) []stdio.Tool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if q == (AsksQuery{}) {
|
|
||||||
return h.AsksNow(), nil
|
|
||||||
}
|
|
||||||
return q.Narrow(h.AsksNow()), nil
|
return q.Narrow(h.AsksNow()), nil
|
||||||
}},
|
}},
|
||||||
{Name: "messenger_identities",
|
{Name: "messenger_identities",
|
||||||
|
|||||||
Reference in New Issue
Block a user