Remove/replace "github.com/pkg/errors" and "errors" with "fmt" or "corteza/server/pkg/errors" Closes #528
240 lines
5.9 KiB
Go
240 lines
5.9 KiB
Go
package rest
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cortezaproject/corteza/server/pkg/api"
|
|
"github.com/cortezaproject/corteza/server/pkg/filter"
|
|
"github.com/cortezaproject/corteza/server/system/renderer"
|
|
"github.com/cortezaproject/corteza/server/system/rest/request"
|
|
"github.com/cortezaproject/corteza/server/system/service"
|
|
"github.com/cortezaproject/corteza/server/system/types"
|
|
)
|
|
|
|
type (
|
|
Template struct {
|
|
renderer service.TemplateService
|
|
ac templateAccessController
|
|
}
|
|
|
|
templateSetPayload struct {
|
|
Filter types.TemplateFilter `json:"filter"`
|
|
Set []*templatePayload `json:"set"`
|
|
}
|
|
|
|
templatePayload struct {
|
|
*types.Template
|
|
|
|
CanGrant bool `json:"canGrant"`
|
|
CanUpdateTemplate bool `json:"canUpdateTemplate"`
|
|
CanDeleteTemplate bool `json:"canDeleteTemplate"`
|
|
}
|
|
|
|
driverSetPayload struct {
|
|
Set []*driverPayload `json:"set"`
|
|
}
|
|
|
|
driverPayload struct {
|
|
renderer.DriverDefinition
|
|
}
|
|
|
|
templateAccessController interface {
|
|
CanGrant(context.Context) bool
|
|
CanCreateTemplate(context.Context) bool
|
|
CanReadTemplate(context.Context, *types.Template) bool
|
|
CanUpdateTemplate(context.Context, *types.Template) bool
|
|
CanDeleteTemplate(context.Context, *types.Template) bool
|
|
}
|
|
)
|
|
|
|
func (Template) New() *Template {
|
|
return &Template{
|
|
renderer: service.DefaultRenderer,
|
|
ac: service.DefaultAccessControl,
|
|
}
|
|
}
|
|
|
|
func (ctrl *Template) Read(ctx context.Context, r *request.TemplateRead) (interface{}, error) {
|
|
tpl, err := ctrl.renderer.FindByID(ctx, r.TemplateID)
|
|
return ctrl.makeTemplatePayload(ctx, tpl, err)
|
|
}
|
|
|
|
func (ctrl *Template) List(ctx context.Context, r *request.TemplateList) (interface{}, error) {
|
|
var (
|
|
err error
|
|
f = types.TemplateFilter{
|
|
Query: r.Query,
|
|
Handle: r.Handle,
|
|
Type: r.Type,
|
|
OwnerID: r.OwnerID,
|
|
Partial: r.Partial,
|
|
Deleted: filter.State(r.Deleted),
|
|
}
|
|
)
|
|
|
|
if f.Paging, err = filter.NewPaging(r.Limit, r.PageCursor); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f.IncTotal = r.IncTotal
|
|
|
|
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
set, filter, err := ctrl.renderer.Search(ctx, f)
|
|
return ctrl.makeFilterTemplatePayload(ctx, set, filter, err)
|
|
}
|
|
|
|
func (ctrl *Template) Create(ctx context.Context, r *request.TemplateCreate) (interface{}, error) {
|
|
var (
|
|
err error
|
|
app = &types.Template{
|
|
Handle: r.Handle,
|
|
Language: r.Language,
|
|
Type: types.DocumentType(r.Type),
|
|
Partial: r.Partial,
|
|
Meta: r.Meta,
|
|
Template: r.Template,
|
|
OwnerID: r.OwnerID,
|
|
}
|
|
)
|
|
|
|
app, err = ctrl.renderer.Create(ctx, app)
|
|
return ctrl.makeTemplatePayload(ctx, app, err)
|
|
}
|
|
|
|
func (ctrl *Template) Update(ctx context.Context, r *request.TemplateUpdate) (interface{}, error) {
|
|
var (
|
|
err error
|
|
app = &types.Template{
|
|
ID: r.TemplateID,
|
|
Handle: r.Handle,
|
|
Language: r.Language,
|
|
Type: types.DocumentType(r.Type),
|
|
Partial: r.Partial,
|
|
Meta: r.Meta,
|
|
Template: r.Template,
|
|
OwnerID: r.OwnerID,
|
|
}
|
|
)
|
|
|
|
app, err = ctrl.renderer.Update(ctx, app)
|
|
return ctrl.makeTemplatePayload(ctx, app, err)
|
|
}
|
|
|
|
func (ctrl *Template) Delete(ctx context.Context, r *request.TemplateDelete) (interface{}, error) {
|
|
return api.OK(), ctrl.renderer.DeleteByID(ctx, r.TemplateID)
|
|
}
|
|
|
|
func (ctrl *Template) Undelete(ctx context.Context, r *request.TemplateUndelete) (interface{}, error) {
|
|
return api.OK(), ctrl.renderer.UndeleteByID(ctx, r.TemplateID)
|
|
}
|
|
|
|
func (ctrl *Template) RenderDrivers(ctx context.Context, r *request.TemplateRenderDrivers) (interface{}, error) {
|
|
return ctrl.makeSetRenderDriverPayload(ctx, ctrl.renderer.Drivers()), nil
|
|
}
|
|
|
|
func (ctrl *Template) Render(ctx context.Context, r *request.TemplateRender) (interface{}, error) {
|
|
vars := make(map[string]interface{})
|
|
err := json.Unmarshal(r.Variables, &vars)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
opts := make(map[string]string)
|
|
if r.Options != nil {
|
|
err = json.Unmarshal(r.Options, &opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
ct := ctrl.getDestinationType(r.Ext)
|
|
|
|
doc, err := ctrl.renderer.Render(ctx, r.TemplateID, ct, vars, opts)
|
|
return ctrl.serve(doc, ct, r, err)
|
|
}
|
|
|
|
// Utilities
|
|
|
|
func (ctrl Template) makeFilterTemplatePayload(ctx context.Context, nn types.TemplateSet, f types.TemplateFilter, err error) (*templateSetPayload, error) {
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msp := &templateSetPayload{Filter: f, Set: make([]*templatePayload, len(nn))}
|
|
|
|
for i := range nn {
|
|
msp.Set[i], _ = ctrl.makeTemplatePayload(ctx, nn[i], nil)
|
|
}
|
|
|
|
return msp, nil
|
|
}
|
|
|
|
func (ctrl Template) makeTemplatePayload(ctx context.Context, tpl *types.Template, err error) (*templatePayload, error) {
|
|
if err != nil || tpl == nil {
|
|
return nil, err
|
|
}
|
|
|
|
pl := &templatePayload{
|
|
Template: tpl,
|
|
|
|
CanGrant: ctrl.ac.CanGrant(ctx),
|
|
CanUpdateTemplate: ctrl.ac.CanUpdateTemplate(ctx, tpl),
|
|
CanDeleteTemplate: ctrl.ac.CanDeleteTemplate(ctx, tpl),
|
|
}
|
|
|
|
return pl, nil
|
|
}
|
|
|
|
func (ctrl Template) makeSetRenderDriverPayload(ctx context.Context, nn []renderer.DriverDefinition) *driverSetPayload {
|
|
msp := &driverSetPayload{Set: make([]*driverPayload, len(nn))}
|
|
|
|
for i := range nn {
|
|
msp.Set[i] = &driverPayload{
|
|
DriverDefinition: nn[i],
|
|
}
|
|
}
|
|
|
|
return msp
|
|
}
|
|
|
|
func (ctrl *Template) serve(doc io.ReadSeeker, ct string, r *request.TemplateRender, err error) (interface{}, error) {
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return func(w http.ResponseWriter, req *http.Request) {
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
name := url.QueryEscape(strings.TrimSpace(r.Filename) + "." + strings.TrimSpace(r.Ext))
|
|
w.Header().Add("Content-Disposition", "attachment; filename="+name)
|
|
w.Header().Add("Content-Type", ct+"; charset=utf-8")
|
|
|
|
http.ServeContent(w, req, name, time.Now(), doc)
|
|
}, nil
|
|
}
|
|
|
|
func (ctrl *Template) getDestinationType(ext string) string {
|
|
switch ext {
|
|
case "txt":
|
|
return "text/plain"
|
|
case "html":
|
|
return "text/html"
|
|
case "pdf":
|
|
return "application/pdf"
|
|
}
|
|
|
|
return "text/plain"
|
|
}
|