Add a report description endpoint to query shape
This commit is contained in:
@@ -21,6 +21,7 @@ type (
|
||||
Datasource interface {
|
||||
Name() string
|
||||
Load(context.Context, ...*FrameDefinition) (Loader, Closer, error)
|
||||
Describe() FrameDescriptionSet
|
||||
}
|
||||
|
||||
// GroupableDatasource is able to provide groupped data
|
||||
|
||||
@@ -54,6 +54,15 @@ type (
|
||||
Paging *filter.Paging `json:"paging"`
|
||||
Sorting filter.SortExprSet `json:"sorting"`
|
||||
}
|
||||
|
||||
FrameDescriptionSet []*FrameDescription
|
||||
FrameDescription struct {
|
||||
Source string `json:"source"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Columns FrameColumnSet `json:"columns"`
|
||||
|
||||
// @todo size and other shape related bits
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -19,6 +19,7 @@ type (
|
||||
Add(...Step) M
|
||||
Run(context.Context) error
|
||||
Load(context.Context, ...*FrameDefinition) ([]*Frame, error)
|
||||
Describe(source string) (FrameDescriptionSet, error)
|
||||
}
|
||||
|
||||
StepSet []Step
|
||||
@@ -130,6 +131,15 @@ func (m *model) Run(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) Describe(source string) (out FrameDescriptionSet, err error) {
|
||||
ds := m.datasources.Find(source)
|
||||
if ds == nil {
|
||||
return nil, fmt.Errorf("model does not contain the datasource: %s", source)
|
||||
}
|
||||
|
||||
return ds.Describe(), nil
|
||||
}
|
||||
|
||||
func (m *model) Load(ctx context.Context, dd ...*FrameDefinition) ([]*Frame, error) {
|
||||
var err error
|
||||
|
||||
|
||||
@@ -93,6 +93,34 @@ func (d *joinedDataset) Name() string {
|
||||
return d.def.Name
|
||||
}
|
||||
|
||||
func (d *joinedDataset) Describe() FrameDescriptionSet {
|
||||
out := make(FrameDescriptionSet, 0, 2)
|
||||
|
||||
local := d.base.Describe()
|
||||
for _, l := range local {
|
||||
out = append(out,
|
||||
&FrameDescription{
|
||||
Source: d.Name(),
|
||||
Ref: l.Source,
|
||||
Columns: l.Columns,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
foreign := d.foreign.Describe()
|
||||
for _, f := range foreign {
|
||||
out = append(out,
|
||||
&FrameDescription{
|
||||
Source: d.Name(),
|
||||
Ref: f.Source,
|
||||
Columns: f.Columns,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// @todo allow x-join filtering
|
||||
func (d *joinedDataset) Load(ctx context.Context, dd ...*FrameDefinition) (Loader, Closer, error) {
|
||||
// to hold closer references for all underlying datasources
|
||||
|
||||
@@ -174,6 +174,15 @@ func (r *recordDatasource) Group(d report.GroupDefinition, name string) (bool, e
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *recordDatasource) Describe() report.FrameDescriptionSet {
|
||||
return report.FrameDescriptionSet{
|
||||
&report.FrameDescription{
|
||||
Source: r.Name(),
|
||||
Columns: r.cols,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *recordDatasource) Load(ctx context.Context, dd ...*report.FrameDefinition) (l report.Loader, c report.Closer, err error) {
|
||||
def := dd[0]
|
||||
|
||||
|
||||
@@ -1435,6 +1435,16 @@ endpoints:
|
||||
name: reportID
|
||||
required: true
|
||||
title: Report ID
|
||||
- name: describe
|
||||
method: POST
|
||||
title: Describe report (fresh)
|
||||
path: "/describe"
|
||||
parameters:
|
||||
post:
|
||||
- { name: sources, type: "types.ReportDataSourceSet", title: Report steps definition }
|
||||
- { name: steps, type: "report.StepDefinitionSet", title: Report steps definition }
|
||||
- { name: describe, type: "[]string", title: The source descriptions to generate }
|
||||
|
||||
# @todo better name
|
||||
- name: runFresh
|
||||
method: POST
|
||||
|
||||
@@ -25,6 +25,7 @@ type (
|
||||
Read(context.Context, *request.ReportRead) (interface{}, error)
|
||||
Delete(context.Context, *request.ReportDelete) (interface{}, error)
|
||||
Undelete(context.Context, *request.ReportUndelete) (interface{}, error)
|
||||
Describe(context.Context, *request.ReportDescribe) (interface{}, error)
|
||||
RunFresh(context.Context, *request.ReportRunFresh) (interface{}, error)
|
||||
Run(context.Context, *request.ReportRun) (interface{}, error)
|
||||
}
|
||||
@@ -37,6 +38,7 @@ type (
|
||||
Read func(http.ResponseWriter, *http.Request)
|
||||
Delete func(http.ResponseWriter, *http.Request)
|
||||
Undelete func(http.ResponseWriter, *http.Request)
|
||||
Describe func(http.ResponseWriter, *http.Request)
|
||||
RunFresh func(http.ResponseWriter, *http.Request)
|
||||
Run func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
@@ -140,6 +142,22 @@ func NewReport(h ReportAPI) *Report {
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Describe: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewReportDescribe()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Describe(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
RunFresh: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewReportRunFresh()
|
||||
@@ -184,6 +202,7 @@ func (h Report) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http
|
||||
r.Get("/reports/{reportID}", h.Read)
|
||||
r.Delete("/reports/{reportID}", h.Delete)
|
||||
r.Post("/reports/{reportID}/undelete", h.Undelete)
|
||||
r.Post("/reports/describe", h.Describe)
|
||||
r.Post("/reports/run", h.RunFresh)
|
||||
r.Post("/reports/{reportID}/run", h.Run)
|
||||
})
|
||||
|
||||
@@ -30,6 +30,7 @@ type (
|
||||
// @todo
|
||||
// Run(ctx context.Context, ID uint64, dd report.DatasetDefinitionSet) (rr *report.Matrix, err error)
|
||||
RunFresh(ctx context.Context, src types.ReportDataSourceSet, st report.StepDefinitionSet, dd report.FrameDefinitionSet) (rr []*report.Frame, err error)
|
||||
DescribeFresh(ctx context.Context, src types.ReportDataSourceSet, st report.StepDefinitionSet, sources ...string) (out report.FrameDescriptionSet, err error)
|
||||
}
|
||||
|
||||
reportAccessController interface {
|
||||
@@ -139,6 +140,10 @@ func (ctrl *Report) Run(ctx context.Context, r *request.ReportRun) (interface{},
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ctrl *Report) Describe(ctx context.Context, r *request.ReportDescribe) (interface{}, error) {
|
||||
return ctrl.report.DescribeFresh(ctx, r.Sources, r.Steps, r.Describe...)
|
||||
}
|
||||
|
||||
func (ctrl *Report) RunFresh(ctx context.Context, r *request.ReportRunFresh) (interface{}, error) {
|
||||
rr, err := ctrl.report.RunFresh(ctx, r.Sources, r.Steps, r.Frames)
|
||||
return ctrl.makeReportFramePayload(ctx, rr, err)
|
||||
|
||||
@@ -148,6 +148,23 @@ type (
|
||||
ReportID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
ReportDescribe struct {
|
||||
// Sources POST parameter
|
||||
//
|
||||
// Report steps definition
|
||||
Sources types.ReportDataSourceSet
|
||||
|
||||
// Steps POST parameter
|
||||
//
|
||||
// Report steps definition
|
||||
Steps report.StepDefinitionSet
|
||||
|
||||
// Describe POST parameter
|
||||
//
|
||||
// The source descriptions to generate
|
||||
Describe []string
|
||||
}
|
||||
|
||||
ReportRunFresh struct {
|
||||
// Sources POST parameter
|
||||
//
|
||||
@@ -623,6 +640,81 @@ func (r *ReportUndelete) Fill(req *http.Request) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// NewReportDescribe request
|
||||
func NewReportDescribe() *ReportDescribe {
|
||||
return &ReportDescribe{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ReportDescribe) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"sources": r.Sources,
|
||||
"steps": r.Steps,
|
||||
"describe": r.Describe,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ReportDescribe) GetSources() types.ReportDataSourceSet {
|
||||
return r.Sources
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ReportDescribe) GetSteps() report.StepDefinitionSet {
|
||||
return r.Steps
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ReportDescribe) GetDescribe() []string {
|
||||
return r.Describe
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ReportDescribe) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
//if val, ok := req.Form["sources[]"]; ok && len(val) > 0 {
|
||||
// r.Sources, err = types.ReportDataSourceSet(val), nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
|
||||
//if val, ok := req.Form["steps[]"]; ok && len(val) > 0 {
|
||||
// r.Steps, err = report.StepDefinitionSet(val), nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
|
||||
//if val, ok := req.Form["describe[]"]; ok && len(val) > 0 {
|
||||
// r.Describe, err = val, nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewReportRunFresh request
|
||||
func NewReportRunFresh() *ReportRunFresh {
|
||||
return &ReportRunFresh{}
|
||||
|
||||
@@ -288,6 +288,45 @@ func (svc *report) Run(ctx context.Context, ID uint64, dd rep.FrameDefinitionSet
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// actionlog?
|
||||
func (svc *report) DescribeFresh(ctx context.Context, src types.ReportDataSourceSet, st rep.StepDefinitionSet, sources ...string) (out rep.FrameDescriptionSet, err error) {
|
||||
// var (
|
||||
// aaProps = &reportActionProps{}
|
||||
// )
|
||||
|
||||
out = make(rep.FrameDescriptionSet, 0, len(sources)*2)
|
||||
|
||||
err = func() (err error) {
|
||||
ss := src.ModelSteps()
|
||||
ss = append(ss, st...)
|
||||
|
||||
// Model the report
|
||||
model, err := rep.Model(ctx, reporters, ss...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = model.Run(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var auxOut rep.FrameDescriptionSet
|
||||
for _, s := range sources {
|
||||
auxOut, err = model.Describe(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out = append(out, auxOut...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (svc *report) RunFresh(ctx context.Context, src types.ReportDataSourceSet, st rep.StepDefinitionSet, dd rep.FrameDefinitionSet) (out []*rep.Frame, err error) {
|
||||
var (
|
||||
aaProps = &reportActionProps{}
|
||||
|
||||
Reference in New Issue
Block a user