diff --git a/server/server.go b/server/server.go index c9efa58c49..004e926270 100644 --- a/server/server.go +++ b/server/server.go @@ -38,6 +38,7 @@ import ( "github.com/open-policy-agent/opa/server/writer" "github.com/open-policy-agent/opa/storage" "github.com/open-policy-agent/opa/topdown" + "github.com/open-policy-agent/opa/topdown/notes" "github.com/open-policy-agent/opa/util" "github.com/open-policy-agent/opa/version" "github.com/open-policy-agent/opa/watch" @@ -1857,6 +1858,12 @@ func (s *Server) evalDiagnosticPolicy(r *http.Request) (logger diagnosticsLogger func (s *Server) getExplainResponse(explainMode types.ExplainModeV1, trace []*topdown.Event, pretty bool) (explanation types.TraceV1) { switch explainMode { + case types.ExplainNotesV1: + var err error + explanation, err = types.NewTraceV1(notes.Filter(trace), pretty) + if err != nil { + break + } case types.ExplainFullV1: var err error explanation, err = types.NewTraceV1(trace, pretty) @@ -2105,6 +2112,8 @@ func getWatch(p []string) (watch bool) { func getExplain(p []string, zero types.ExplainModeV1) types.ExplainModeV1 { for _, x := range p { switch x { + case string(types.ExplainNotesV1): + return types.ExplainNotesV1 case string(types.ExplainFullV1): return types.ExplainFullV1 } diff --git a/server/server_test.go b/server/server_test.go index 5dcdb657da..0ab4e5a130 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1433,6 +1433,43 @@ p = [1, 2, 3, 4] { true }`, 200, "") } +func TestDataPostExplainNotes(t *testing.T) { + f := newFixture(t) + + f.v1(http.MethodPut, "/policies/test", ` + package test + p { + data.a[i] = x; x > 1 + trace(sprintf("found x = %d", [x])) + }`, 200, "") + + f.v1(http.MethodPut, "/data/a", `[1,2,3]`, 200, "") + f.reset() + + req := newReqV1(http.MethodPost, "/data/test/p?explain=notes", "") + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode err: %v", err) + } + + var trace types.TraceV1Raw + + if err := trace.UnmarshalJSON(result.Explanation); err != nil { + t.Fatal(err) + } + + if len(trace) != 6 || trace[2].Op != "note" || trace[5].Op != "note" { + t.Logf("Found %d events in trace", len(trace)) + for i := range trace { + t.Logf("Event #%d: %v\n", i, trace[i]) + } + t.Fatal("Unexpected trace") + } +} + func TestDataProvenance(t *testing.T) { f := newFixture(t) diff --git a/server/types/types.go b/server/types/types.go index d1207be330..b0e96869c2 100644 --- a/server/types/types.go +++ b/server/types/types.go @@ -191,8 +191,9 @@ type ExplainModeV1 string // Explanation mode enumeration. const ( - ExplainOffV1 ExplainModeV1 = "off" - ExplainFullV1 ExplainModeV1 = "full" + ExplainOffV1 ExplainModeV1 = "off" + ExplainFullV1 ExplainModeV1 = "full" + ExplainNotesV1 ExplainModeV1 = "notes" ) // TraceV1 models the trace result returned for queries that include the