Skip to content

Commit 6a3e5e7

Browse files
authored
feat(api): support for dynamic dataset readme rendering
Merge pull request #1137 from qri-io/feat_render_api_input
2 parents b4ca6c7 + 7cd8b74 commit 6a3e5e7

File tree

4 files changed

+116
-18
lines changed

4 files changed

+116
-18
lines changed

api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ func NewServerRoutes(s Server) *http.ServeMux {
297297
m.Handle("/fsi/write/", s.middleware(fsih.WriteHandler("/fsi/write")))
298298

299299
renderh := NewRenderHandlers(node.Repo)
300+
m.Handle("/render", s.middleware(renderh.RenderHandler))
300301
m.Handle("/render/", s.middleware(renderh.RenderHandler))
301302

302303
lh := NewLogHandlers(node)

api/render.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package api
22

33
import (
4+
"encoding/json"
45
"net/http"
56

67
"github.com/qri-io/apiutil"
8+
"github.com/qri-io/dataset"
79
"github.com/qri-io/qri/lib"
810
"github.com/qri-io/qri/repo"
911
)
@@ -33,6 +35,16 @@ func (h *RenderHandlers) RenderHandler(w http.ResponseWriter, r *http.Request) {
3335
OutFormat: "html",
3436
}
3537

38+
// support rendering a passed-in JSON dataset document
39+
if r.Header.Get("Content-Type") == "application/json" {
40+
ds := &dataset.Dataset{}
41+
if err := json.NewDecoder(r.Body).Decode(ds); err != nil {
42+
apiutil.WriteErrResponse(w, http.StatusBadRequest, err)
43+
return
44+
}
45+
p.Dataset = ds
46+
}
47+
3648
// Old style viz component rendering
3749
if r.FormValue("viz") == "true" {
3850
data := []byte{}

lib/render.go

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,42 @@ func (RenderRequests) CoreRequestsName() string { return "render" }
3939

4040
// RenderParams defines parameters for the Render method
4141
type RenderParams struct {
42-
Ref string
43-
Template []byte
44-
UseFSI bool
42+
// Ref is a string reference to the dataset to render
43+
Ref string
44+
// Optionally pass an entire dataset in for rendering, if providing a dataset,
45+
// the Ref field must be empty
46+
Dataset *dataset.Dataset
47+
// Optional template override
48+
Template []byte
49+
// If true,
50+
UseFSI bool
51+
// Output format. defaults to "html"
4552
OutFormat string
4653
}
4754

55+
// Validate checks if render parameters are valid
56+
func (p *RenderParams) Validate() error {
57+
if p.Ref != "" && p.Dataset != nil {
58+
return fmt.Errorf("cannot provide both a reference and a dataset to render")
59+
}
60+
return nil
61+
}
62+
4863
// RenderViz renders a viz component as html
4964
func (r *RenderRequests) RenderViz(p *RenderParams, res *[]byte) (err error) {
5065
if r.cli != nil {
5166
return r.cli.Call("RenderRequests.RenderViz", p, res)
5267
}
5368
ctx := context.TODO()
5469

70+
if err = p.Validate(); err != nil {
71+
return err
72+
}
73+
74+
if p.Dataset != nil {
75+
return fmt.Errorf("rendering dynamic dataset viz component is not supported")
76+
}
77+
5578
var ref reporef.DatasetRef
5679
if ref, err = repo.ParseDatasetRef(p.Ref); err != nil {
5780
return
@@ -74,35 +97,41 @@ func (r *RenderRequests) RenderReadme(p *RenderParams, res *string) (err error)
7497
}
7598
ctx := context.TODO()
7699

77-
ref, err := base.ToDatasetRef(p.Ref, r.repo, p.UseFSI)
78-
if err != nil {
100+
if err = p.Validate(); err != nil {
79101
return err
80102
}
81103

82104
var ds *dataset.Dataset
83-
if p.UseFSI {
84-
if ref.FSIPath == "" {
85-
return fsi.ErrNoLink
86-
}
87-
if ds, err = fsi.ReadDir(ref.FSIPath); err != nil {
88-
return fmt.Errorf("loading linked dataset: %s", err)
89-
}
105+
if p.Dataset != nil {
106+
ds = p.Dataset
90107
} else {
91-
ds, err = dsfs.LoadDataset(ctx, r.repo.Store(), ref.Path)
108+
ref, err := base.ToDatasetRef(p.Ref, r.repo, p.UseFSI)
92109
if err != nil {
93-
return fmt.Errorf("loading dataset: %s", err)
110+
return err
111+
}
112+
113+
if p.UseFSI {
114+
if ref.FSIPath == "" {
115+
return fsi.ErrNoLink
116+
}
117+
if ds, err = fsi.ReadDir(ref.FSIPath); err != nil {
118+
return fmt.Errorf("loading linked dataset: %s", err)
119+
}
120+
} else {
121+
ds, err = dsfs.LoadDataset(ctx, r.repo.Store(), ref.Path)
122+
if err != nil {
123+
return fmt.Errorf("loading dataset: %s", err)
124+
}
94125
}
95126
}
96127

97128
if ds.Readme == nil {
98129
return fmt.Errorf("no readme to render")
99130
}
100131

101-
err = ds.Readme.OpenScriptFile(ctx, r.repo.Filesystem())
102-
if err != nil {
132+
if err = ds.Readme.OpenScriptFile(ctx, r.repo.Filesystem()); err != nil {
103133
return err
104134
}
105-
106135
if ds.Readme.ScriptFile() == nil {
107136
return fmt.Errorf("no readme to render")
108137
}

lib/render_test.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestNewRenderRequests(t *testing.T) {
4646
t.Errorf("invalid requests name. expected: '%s', got: '%s'", "render", reqs.CoreRequestsName())
4747
}
4848

49-
// this should panic:
49+
// this should panic, triggering the defer statement above
5050
NewRenderRequests(tr, rpc.NewClient(conn))
5151
}
5252

@@ -176,6 +176,35 @@ func (r *renderTestRunner) Save(ref string, ds *dataset.Dataset, bodyPath string
176176
}
177177
}
178178

179+
func TestRenderRequestsRenderViz(t *testing.T) {
180+
runner := newRenderTestRunner(t, "render_viz")
181+
defer runner.Delete()
182+
183+
params := RenderParams{
184+
Ref: "foo/bar",
185+
Dataset: &dataset.Dataset{
186+
Readme: &dataset.Readme{
187+
ScriptBytes: []byte("# hi\n\nhello"),
188+
},
189+
},
190+
}
191+
var data []byte
192+
if err := runner.RenderReqs.RenderViz(&params, &data); err == nil {
193+
t.Errorf("expected RenderReadme with both ref & dataset to error")
194+
}
195+
196+
params = RenderParams{
197+
Dataset: &dataset.Dataset{
198+
Readme: &dataset.Readme{
199+
ScriptBytes: []byte("# hi\n\nhello"),
200+
},
201+
},
202+
}
203+
if err := runner.RenderReqs.RenderViz(&params, &data); err == nil {
204+
t.Errorf("expected attempt to dynamic-render viz to fail")
205+
}
206+
}
207+
179208
// Test that render with a readme returns an html string
180209
func TestRenderReadme(t *testing.T) {
181210
runner := newRenderTestRunner(t, "render_readme")
@@ -204,4 +233,31 @@ func TestRenderReadme(t *testing.T) {
204233
if diff := cmp.Diff(expect, text); diff != "" {
205234
t.Errorf("response mismatch (-want +got):\n%s", diff)
206235
}
236+
237+
params = RenderParams{
238+
Dataset: &dataset.Dataset{
239+
Readme: &dataset.Readme{
240+
ScriptBytes: []byte("# hi\n\nhello"),
241+
},
242+
},
243+
}
244+
if err = runner.RenderReqs.RenderReadme(&params, &text); err != nil {
245+
t.Errorf("dynamic dataset render error: %s", err)
246+
}
247+
248+
if diff := cmp.Diff(expect, text); diff != "" {
249+
t.Errorf("dynamic dataset render response mismatch (-want +got):\n%s", diff)
250+
}
251+
252+
params = RenderParams{
253+
Ref: "foo/bar",
254+
Dataset: &dataset.Dataset{
255+
Readme: &dataset.Readme{
256+
ScriptBytes: []byte("# hi\n\nhello"),
257+
},
258+
},
259+
}
260+
if err = runner.RenderReqs.RenderReadme(&params, &text); err == nil {
261+
t.Errorf("expected RenderReadme with both ref & dataset to error")
262+
}
207263
}

0 commit comments

Comments
 (0)