Files
upstream-corteza/server/system/automation/templates_handler.go
upstream-mirror 7e385fc126
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
2026-09-18 01:22:23 +05:00

342 lines
8.3 KiB
Go

package automation
import (
"context"
"fmt"
"io"
. "github.com/cortezaproject/corteza/server/pkg/expr"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/pkg/wfexec"
"github.com/cortezaproject/corteza/server/system/types"
"github.com/spf13/cast"
)
type (
templateService interface {
FindByID(ctx context.Context, ID uint64) (*types.Template, error)
FindByHandle(ct context.Context, handle string) (*types.Template, error)
FindByAny(ctx context.Context, identifier interface{}) (*types.Template, error)
Search(context.Context, types.TemplateFilter) (types.TemplateSet, types.TemplateFilter, error)
Create(ctx context.Context, tpl *types.Template) (*types.Template, error)
Update(ctx context.Context, tpl *types.Template) (*types.Template, error)
DeleteByID(ctx context.Context, ID uint64) error
UndeleteByID(ctx context.Context, ID uint64) error
Render(ctx context.Context, templateID uint64, dstType string, variables map[string]interface{}, options map[string]string, aux types.TemplateRenderAux) (io.ReadSeeker, error)
}
templatesHandler struct {
reg templatesHandlerRegistry
tSvc templateService
}
templateSetIterator struct {
// Item buffer, current item pointer, and total items traversed
ptr uint
buffer types.TemplateSet
total uint
// When filter limit is set, this constraints it
iterLimit uint
useIterLimit bool
// Item loader for additional chunks
filter types.TemplateFilter
loader func() error
// Total reported by the first page
resTotal uint
}
templateLookup interface {
GetLookup() (bool, uint64, string, *types.Template)
}
)
func TemplatesHandler(reg templatesHandlerRegistry, tSvc templateService) *templatesHandler {
h := &templatesHandler{
reg: reg,
tSvc: tSvc,
}
h.register()
return h
}
func (h templatesHandler) lookup(ctx context.Context, args *templatesLookupArgs) (results *templatesLookupResults, err error) {
results = &templatesLookupResults{}
results.Template, err = lookupTemplate(ctx, h.tSvc, args)
return
}
func (h templatesHandler) search(ctx context.Context, args *templatesSearchArgs) (results *templatesSearchResults, err error) {
results = &templatesSearchResults{}
var (
f = types.TemplateFilter{
Handle: args.Handle,
Type: args.Type,
OwnerID: args.OwnerID,
Partial: args.Partial,
Labels: args.Labels,
}
)
if args.hasSort {
if err = f.Sort.Set(args.Sort); err != nil {
return
}
}
if args.hasPageCursor && args.PageCursor != "" {
f.PageCursor = &filter.PagingCursor{}
if err = f.PageCursor.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
return
}
}
if args.hasLabels {
f.Labels = args.Labels
}
if args.hasLimit {
f.Limit = uint(args.Limit)
}
// Total cannot be fetched together with a page cursor
f.IncTotal = args.IncTotal && f.PageCursor == nil
var auxf types.TemplateFilter
results.Templates, auxf, err = h.tSvc.Search(ctx, f)
results.Total = uint64(auxf.Total)
return
}
func (h templatesHandler) each(ctx context.Context, args *templatesEachArgs) (out wfexec.IteratorHandler, err error) {
var (
i = &templateSetIterator{}
f = types.TemplateFilter{
Handle: args.Handle,
Type: args.Type,
OwnerID: args.OwnerID,
Partial: args.Partial,
Labels: args.Labels,
}
)
if args.hasSort {
if err = f.Sort.Set(args.Sort); err != nil {
return
}
}
if args.hasPageCursor && args.PageCursor != "" {
f.NextPage = &filter.PagingCursor{}
if err = f.NextPage.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
return
}
}
if args.hasLabels {
f.Labels = args.Labels
}
if args.hasLimit {
i.useIterLimit = true
i.iterLimit = uint(args.Limit)
f.Limit = uint(args.Limit)
if args.Limit > uint64(wfexec.MaxIteratorBufferSize) {
f.Limit = wfexec.MaxIteratorBufferSize
}
i.iterLimit = uint(args.Limit)
} else {
f.Limit = wfexec.MaxIteratorBufferSize
}
f.IncTotal = args.IncTotal
i.filter = f
i.loader = func() (err error) {
// Edgecase
if i.filter.PageCursor != nil && i.filter.NextPage == nil {
return
}
i.total += i.ptr
i.ptr = 0
i.filter.PageCursor = i.filter.NextPage
i.filter.NextPage = nil
// Total is fetched with the first page only; a paged query cannot carry it
i.filter.IncTotal = i.filter.IncTotal && i.filter.PageCursor == nil
i.buffer, i.filter, err = h.tSvc.Search(ctx, i.filter)
if i.filter.IncTotal {
i.resTotal = i.filter.Total
}
return
}
// Initial load
return i, i.loader()
}
func (h templatesHandler) create(ctx context.Context, args *templatesCreateArgs) (results *templatesCreateResults, err error) {
results = &templatesCreateResults{}
results.Template, err = h.tSvc.Create(ctx, args.Template)
return
}
func (h templatesHandler) update(ctx context.Context, args *templatesUpdateArgs) (results *templatesUpdateResults, err error) {
results = &templatesUpdateResults{}
results.Template, err = h.tSvc.Update(ctx, args.Template)
return
}
func (h templatesHandler) delete(ctx context.Context, args *templatesDeleteArgs) error {
if id, err := getTemplateID(ctx, h.tSvc, args); err != nil {
return err
} else {
return h.tSvc.DeleteByID(ctx, id)
}
}
func (h templatesHandler) recover(ctx context.Context, args *templatesRecoverArgs) error {
if id, err := getTemplateID(ctx, h.tSvc, args); err != nil {
return err
} else {
return h.tSvc.UndeleteByID(ctx, id)
}
}
func (h templatesHandler) render(ctx context.Context, args *templatesRenderArgs) (*templatesRenderResults, error) {
var err error
opts := make(map[string]string)
if args.hasOptions {
opts, err = cast.ToStringMapStringE(args.Options)
if err != nil {
return nil, err
}
}
tplID, err := getTemplateID(ctx, h.tSvc, args)
if err != nil {
return nil, err
}
var vars map[string]interface{}
if args.hasVariables {
vars = args.Variables.Dict()
}
aux := types.TemplateRenderAux{}
aux.HeaderTemplateID, err = resolveTemplateID(ctx, h.tSvc, args.hasHeaderTemplate, args.headerTemplateID, args.headerTemplateHandle, args.headerTemplateRes)
if err != nil {
return nil, err
}
aux.FooterTemplateID, err = resolveTemplateID(ctx, h.tSvc, args.hasFooterTemplate, args.footerTemplateID, args.footerTemplateHandle, args.footerTemplateRes)
if err != nil {
return nil, err
}
doc, err := h.tSvc.Render(ctx, tplID, args.DocumentType, vars, opts, aux)
if err != nil {
return nil, err
}
rr := &templatesRenderResults{
Document: &renderedDocument{
Document: doc,
Name: args.DocumentName,
Type: args.DocumentType,
},
}
return rr, nil
}
func (i *templateSetIterator) More(context.Context, *Vars) (bool, error) {
return wfexec.GenericResourceNextCheck(i.useIterLimit, i.ptr, uint(len(i.buffer)), i.total, i.iterLimit, i.filter.NextPage != nil), nil
}
func (i *templateSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
func (i *templateSetIterator) Next(context.Context, *Vars) (out *Vars, err error) {
if len(i.buffer)-int(i.ptr) <= 0 {
if err = i.loader(); err != nil {
panic(err)
}
}
out = &Vars{}
out.Set("template", Must(NewTemplate(i.buffer[i.ptr])))
out.Set("index", Must(NewInteger(i.total+i.ptr)))
out.Set("total", Must(NewInteger(i.resTotal)))
i.ptr++
return out, nil
}
func lookupTemplate(ctx context.Context, svc templateService, args templateLookup) (*types.Template, error) {
_, ID, handle, template := args.GetLookup()
switch {
case template != nil:
return template, nil
case ID > 0:
return svc.FindByID(ctx, ID)
case len(handle) > 0:
return svc.FindByHandle(ctx, handle)
}
return nil, fmt.Errorf("empty lookup params")
}
func getTemplateID(ctx context.Context, svc templateService, args templateLookup) (uint64, error) {
_, ID, _, _ := args.GetLookup()
if ID > 0 {
return ID, nil
}
tpl, err := lookupTemplate(ctx, svc, args)
if err != nil {
return 0, err
}
return tpl.ID, nil
}
// resolveTemplateID resolves ID, handle or template argument into a template ID
//
// Unset argument resolves into zero ID
func resolveTemplateID(ctx context.Context, svc templateService, has bool, ID uint64, handle string, tpl *types.Template) (uint64, error) {
switch {
case !has:
return 0, nil
case tpl != nil:
return tpl.ID, nil
case ID > 0:
return ID, nil
case len(handle) > 0:
tpl, err := svc.FindByHandle(ctx, handle)
if err != nil {
return 0, err
}
return tpl.ID, nil
}
return 0, nil
}