snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
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

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
package rest
import (
"context"
"github.com/cortezaproject/corteza/server/discovery/rest/internal/feed"
"github.com/cortezaproject/corteza/server/discovery/rest/request"
"time"
)
type (
feeder struct {
resActivityLog interface {
ResourceActivities(ctx context.Context, limit uint, cur string, from *time.Time, to *time.Time) (rsp *feed.Response, err error)
}
}
)
func Feed() *feeder {
return &feeder{
resActivityLog: feed.ResourceActivity(),
}
}
func (ctrl feeder) Changes(ctx context.Context, r *request.FeedChanges) (interface{}, error) {
return ctrl.resActivityLog.ResourceActivities(ctx, r.Limit, r.PageCursor, r.From, r.To)
}

View File

@@ -0,0 +1,57 @@
package handlers
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
//
import (
"context"
"github.com/cortezaproject/corteza/server/discovery/rest/request"
"github.com/cortezaproject/corteza/server/pkg/api"
"github.com/go-chi/chi/v5"
"net/http"
)
type (
// Internal API interface
FeedAPI interface {
Changes(context.Context, *request.FeedChanges) (interface{}, error)
}
// HTTP API interface
Feed struct {
Changes func(http.ResponseWriter, *http.Request)
}
)
func NewFeed(h FeedAPI) *Feed {
return &Feed{
Changes: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewFeedChanges()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.Changes(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
}
}
func (h Feed) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
r.Group(func(r chi.Router) {
r.Use(middlewares...)
r.Get("/feed/", h.Changes)
})
}

View File

@@ -0,0 +1,57 @@
package handlers
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
//
import (
"context"
"github.com/cortezaproject/corteza/server/discovery/rest/request"
"github.com/cortezaproject/corteza/server/pkg/api"
"github.com/go-chi/chi/v5"
"net/http"
)
type (
// Internal API interface
MappingsAPI interface {
List(context.Context, *request.MappingsList) (interface{}, error)
}
// HTTP API interface
Mappings struct {
List func(http.ResponseWriter, *http.Request)
}
)
func NewMappings(h MappingsAPI) *Mappings {
return &Mappings{
List: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewMappingsList()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.List(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
}
}
func (h Mappings) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
r.Group(func(r chi.Router) {
r.Use(middlewares...)
r.Get("/mappings/", h.List)
})
}

View File

@@ -0,0 +1,114 @@
package handlers
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
//
import (
"context"
"github.com/cortezaproject/corteza/server/discovery/rest/request"
"github.com/cortezaproject/corteza/server/pkg/api"
"github.com/go-chi/chi/v5"
"net/http"
)
type (
// Internal API interface
ResourcesAPI interface {
SystemUsers(context.Context, *request.ResourcesSystemUsers) (interface{}, error)
ComposeNamespaces(context.Context, *request.ResourcesComposeNamespaces) (interface{}, error)
ComposeModules(context.Context, *request.ResourcesComposeModules) (interface{}, error)
ComposeRecords(context.Context, *request.ResourcesComposeRecords) (interface{}, error)
}
// HTTP API interface
Resources struct {
SystemUsers func(http.ResponseWriter, *http.Request)
ComposeNamespaces func(http.ResponseWriter, *http.Request)
ComposeModules func(http.ResponseWriter, *http.Request)
ComposeRecords func(http.ResponseWriter, *http.Request)
}
)
func NewResources(h ResourcesAPI) *Resources {
return &Resources{
SystemUsers: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewResourcesSystemUsers()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.SystemUsers(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
ComposeNamespaces: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewResourcesComposeNamespaces()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.ComposeNamespaces(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
ComposeModules: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewResourcesComposeModules()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.ComposeModules(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
ComposeRecords: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewResourcesComposeRecords()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.ComposeRecords(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
}
}
func (h Resources) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
r.Group(func(r chi.Router) {
r.Use(middlewares...)
r.Get("/resources/system/users", h.SystemUsers)
r.Get("/resources/compose/namespaces", h.ComposeNamespaces)
r.Get("/resources/compose/namespaces/{namespaceID}/modules", h.ComposeModules)
r.Get("/resources/compose/namespaces/{namespaceID}/modules/{moduleID}/records", h.ComposeRecords)
})
}

View File

@@ -0,0 +1,551 @@
package documents
import (
"context"
"fmt"
"strconv"
"strings"
cmpService "github.com/cortezaproject/corteza/server/compose/service"
cmpTypes "github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/discovery/service"
"github.com/cortezaproject/corteza/server/pkg/errors"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/pkg/id"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/cortezaproject/corteza/server/pkg/rbac"
sysService "github.com/cortezaproject/corteza/server/system/service"
sysTypes "github.com/cortezaproject/corteza/server/system/types"
)
type (
composeResources struct {
opt options.DiscoveryOpt
settings *sysTypes.AppSettings
rbac interface {
SignificantRoles(res rbac.Resource, op string) (aRR, dRR []uint64)
}
ac interface {
CanReadModule(ctx context.Context, r *cmpTypes.Module) bool
CanReadNamespace(ctx context.Context, r *cmpTypes.Namespace) bool
CanReadRecord(ctx context.Context, r *cmpTypes.Record) bool
CanReadRecordValueOnModuleField(ctx context.Context, r *cmpTypes.ModuleField) bool
CanReadChart(ctx context.Context, r *cmpTypes.Chart) bool
CanReadPage(ctx context.Context, r *cmpTypes.Page) bool
}
ns interface {
FindByID(context.Context, uint64) (*cmpTypes.Namespace, error)
Find(context.Context, cmpTypes.NamespaceFilter) (cmpTypes.NamespaceSet, cmpTypes.NamespaceFilter, error)
}
mod interface {
FindByID(context.Context, uint64, uint64) (*cmpTypes.Module, error)
Find(ctx context.Context, filter cmpTypes.ModuleFilter) (set cmpTypes.ModuleSet, f cmpTypes.ModuleFilter, err error)
}
rec interface {
Find(ctx context.Context, filter cmpTypes.RecordFilter) (set cmpTypes.RecordSet, f cmpTypes.RecordFilter, err error)
}
page interface {
Find(ctx context.Context, filter cmpTypes.PageFilter) (set cmpTypes.PageSet, f cmpTypes.PageFilter, err error)
}
usr interface {
FindByID(ctx context.Context, id uint64) (*sysTypes.User, error)
}
}
pageDetail struct {
namespaceSlug string
moduleID uint64
pageID uint64
recordID uint64
}
)
var (
modulePages map[uint64]pageDetail
)
const (
maxValueLength = 512
)
func ComposeResources() *composeResources {
return &composeResources{
opt: service.DefaultOption,
settings: sysService.CurrentSettings,
rbac: rbac.Global(),
ac: cmpService.DefaultAccessControl,
ns: cmpService.DefaultNamespace,
mod: cmpService.DefaultModule,
rec: cmpService.DefaultRecord,
page: cmpService.DefaultPage,
usr: sysService.DefaultUser,
}
}
func (d composeResources) Namespaces(ctx context.Context, limit uint, cur string, namespaceID uint64, deleted uint) (rsp *Response, err error) {
return rsp, func() (err error) {
if !d.settings.Discovery.ComposeNamespaces.Enabled {
return errors.Internal("compose namespace indexing disabled")
}
var (
resourceType = "compose:namespace" // @todo use RBAC resourceType
ps cmpTypes.PageSet
pf cmpTypes.PageFilter
nss cmpTypes.NamespaceSet
f = cmpTypes.NamespaceFilter{
Deleted: filter.State(deleted),
}
)
if namespaceID > 0 {
f.NamespaceID = append(f.NamespaceID, id.String(namespaceID))
}
if f.Paging, err = filter.NewPaging(limit, cur); err != nil {
return err
}
if nss, f, err = d.ns.Find(ctx, f); err != nil {
return err
}
rsp = &Response{
Documents: make([]Document, len(nss)),
Filter: Filter{
Limit: limit,
NextPage: f.NextPage,
},
}
for i, ns := range nss {
nsID := ns.ID
nsSlug := ns.Slug
rsp.Documents[i].ID = nsID
pf.NamespaceID = nsID
// namespace pages
if ps, pf, err = d.page.Find(ctx, pf); err != nil {
return err
}
if modulePages == nil {
modulePages = make(map[uint64]pageDetail)
}
for _, p := range ps {
moduleID := p.ModuleID
if len(nsSlug) > 0 && moduleID > 0 {
modulePages[moduleID] = pageDetail{
namespaceSlug: nsSlug,
moduleID: moduleID,
pageID: p.ID,
}
}
}
// where should this link to?
// namespace root page on the compose?
// rsp.Documents[i].URL = "" // added to source
doc := &docComposeNamespace{
ResourceType: resourceType,
NamespaceID: nsID,
Name: ns.Name,
Handle: nsSlug,
Url: d.getUrlToResource(pageDetail{namespaceSlug: nsSlug}),
Meta: docPartialComposeNamespaceMeta{
Subtitle: ns.Meta.Subtitle,
Description: ns.Meta.Description,
},
Created: makePartialChange(&ns.CreatedAt),
Updated: makePartialChange(ns.UpdatedAt),
Deleted: makePartialChange(ns.DeletedAt),
Namespace: docPartialComposeNamespace{
NamespaceID: nsID,
Name: ns.Name,
Handle: nsSlug,
},
CatchAll: []any{nsID, ns.Name, nsSlug},
}
allowedRoles, deniedRoles := d.rbac.SignificantRoles(ns, "read")
doc.Security = append(doc.Security, docSecurity{
AllowedRoles: stringifyUints64(allowedRoles),
DeniedRoles: stringifyUints64(deniedRoles),
})
rsp.Documents[i].Source = doc
}
return nil
}()
}
func (d composeResources) Modules(ctx context.Context, namespaceID uint64, limit uint, cur string, moduleID uint64, deleted uint) (rsp *Response, err error) {
return rsp, func() (err error) {
if !d.settings.Discovery.ComposeModules.Enabled {
return errors.Internal("compose module indexing disabled")
}
var (
ns *cmpTypes.Namespace
mm cmpTypes.ModuleSet
f = cmpTypes.ModuleFilter{
NamespaceID: namespaceID,
Deleted: filter.State(deleted),
}
)
if moduleID > 0 {
f.ModuleID = append(f.ModuleID, id.String(moduleID))
}
if f.Paging, err = filter.NewPaging(limit, cur); err != nil {
return
}
if mm, f, err = d.mod.Find(ctx, f); err != nil {
return
}
rsp = &Response{
Documents: make([]Document, len(mm)),
Filter: Filter{
Limit: limit,
NextPage: f.NextPage,
},
}
if ns, err = d.ns.FindByID(ctx, namespaceID); err != nil {
return
}
nsPartial := docPartialComposeNamespace{
NamespaceID: namespaceID,
Name: ns.Name,
Handle: ns.Slug,
}
for i, mod := range mm {
rsp.Documents[i].ID = mod.ID
// Where should this link to?
// module edit screen in the administration? does this make sense?
//rsp.Documents[i].URL = "@todo"
doc := &docComposeModule{
ResourceType: "compose:module", // @todo use RBAC resourceType
ModuleID: mod.ID,
Name: mod.Name,
Handle: mod.Handle,
Fields: func() []*docPartialComposeModuleField {
out := make([]*docPartialComposeModuleField, len(mod.Fields))
for i, f := range mod.Fields {
out[i] = &docPartialComposeModuleField{
Name: f.Name,
Label: f.Label,
}
}
return out
}(),
Labels: func() map[string]string {
labels := make(map[string]string, len(mod.Labels))
for k, v := range mod.Labels {
if v.Val != "" {
labels[k] = v.Val
} else if len(v.Values) > 0 {
labels[k] = strings.Join(v.Values, ",")
}
}
return labels
}(),
Created: makePartialChange(&mod.CreatedAt),
Updated: makePartialChange(mod.UpdatedAt),
Deleted: makePartialChange(mod.DeletedAt),
CatchAll: []any{mod.ID, mod.Name, mod.Handle},
Namespace: nsPartial,
Module: docPartialComposeModule{
ModuleID: mod.ID,
Name: mod.Name,
Handle: mod.Handle,
},
}
for _, f := range mod.Fields {
if f.Name != "" {
doc.CatchAll = append(doc.CatchAll, f.Name)
}
if f.Label != "" {
doc.CatchAll = append(doc.CatchAll, f.Label)
}
}
allowedRoles, deniedRoles := d.rbac.SignificantRoles(mod, "read")
doc.Security = append(doc.Security, docSecurity{
AllowedRoles: stringifyUints64(allowedRoles),
DeniedRoles: stringifyUints64(deniedRoles),
})
rsp.Documents[i].Source = doc
}
return nil
}()
}
func (d composeResources) Records(ctx context.Context, namespaceID, moduleID uint64, limit uint, cur string, recordID uint64, deleted uint) (rsp *Response, err error) {
return rsp, func() (err error) {
if !d.settings.Discovery.ComposeRecords.Enabled {
return errors.Internal("compose record indexing disabled")
}
var (
ns *cmpTypes.Namespace
mod *cmpTypes.Module
rr cmpTypes.RecordSet
f = cmpTypes.RecordFilter{
NamespaceID: namespaceID,
ModuleID: moduleID,
Deleted: filter.State(deleted),
}
)
if recordID > 0 {
f.Query = fmt.Sprintf("recordID=%d", recordID)
}
if f.Paging, err = filter.NewPaging(limit, cur); err != nil {
return err
}
if rr, f, err = d.rec.Find(ctx, f); err != nil {
return err
}
rsp = &Response{
Documents: make([]Document, len(rr)),
Filter: Filter{
Limit: limit,
NextPage: f.NextPage,
},
}
// @todo handle unreadable (access-control) namespaces
if ns, err = d.ns.FindByID(ctx, namespaceID); err != nil {
return
}
nsPartial := docPartialComposeNamespace{
NamespaceID: namespaceID,
Name: ns.Name,
Handle: ns.Slug,
}
// @todo handle unreadable (access-control) modules
if mod, err = d.mod.FindByID(ctx, namespaceID, moduleID); err != nil {
return
}
modPartial := docPartialComposeModule{
ModuleID: f.ModuleID,
Name: mod.Name,
Handle: mod.Handle,
}
users := make(map[uint64]sysTypes.User)
for i, rec := range rr {
recordID := rec.ID
rsp.Documents[i].ID = recordID
// where should this link to? record page in the compose?
// rsp.Documents[i].URL = "" // added to source
// fixme refactor me
var (
createdBy *sysTypes.User
updatedBy *sysTypes.User
deletedBy *sysTypes.User
)
if rec.CreatedBy > 0 {
createdBy, users, err = d.getUserInfo(ctx, rec.CreatedBy, users)
if err != nil {
return
}
}
if rec.UpdatedBy > 0 {
updatedBy, users, err = d.getUserInfo(ctx, rec.UpdatedBy, users)
if err != nil {
return
}
}
if rec.DeletedBy > 0 {
deletedBy, users, err = d.getUserInfo(ctx, rec.DeletedBy, users)
if err != nil {
return
}
}
// fixme refactor me END
doc := &docComposeRecord{
ResourceType: "compose:record", // @todo use RBAC resourceType
RecordID: recordID,
Namespace: nsPartial,
Module: modPartial,
Url: d.getUrlToResource(pageDetail{moduleID: moduleID, recordID: recordID}),
Meta: rec.Meta,
//Values: d.recordValues(ctx, rec, nil),
//Created: makePartialChange(&rec.CreatedAt),
//Updated: makePartialChange(rec.UpdatedAt),
//Deleted: makePartialChange(rec.DeletedAt),
Created: makeChange(&rec.CreatedAt, createdBy),
Updated: makeChange(rec.UpdatedAt, updatedBy),
Deleted: makeChange(rec.DeletedAt, deletedBy),
}
// Values and value labels
doc.Values, doc.ValueLabels, doc.CatchAll = d.recordValues(ctx, rec, mod.Fields)
allowedRoles, deniedRoles := d.rbac.SignificantRoles(rec, "read")
doc.Security = append(doc.Security, docSecurity{
AllowedRoles: stringifyUints64(allowedRoles),
DeniedRoles: stringifyUints64(deniedRoles),
})
rsp.Documents[i].Source = doc
}
return nil
}()
}
func (d composeResources) recordValues(ctx context.Context, rec *cmpTypes.Record, ff cmpTypes.ModuleFieldSet) (map[string][]interface{}, map[string]string, []any) {
var (
rval = make(map[string][]interface{})
fields = make(map[string]string)
catchAll = make([]any, 0, len(rec.Values))
)
if rec.GetModule() == nil {
return nil, nil, nil
}
_ = rec.GetModule().Fields.Walk(func(f *cmpTypes.ModuleField) error {
if !d.ac.CanReadRecordValueOnModuleField(ctx, f) {
return nil
}
var (
rv = rec.Values.FilterByName(f.Name)
vv = make([]interface{}, 0, len(rv))
field = ff.FindByName(f.Name)
)
if len(rv) == 0 {
return nil
}
for _, val := range rv {
// refs needs to be casted to string (json & unsigned 64-bit integers)!
if f.IsRef() {
aux := fmt.Sprintf("%d", val.Ref)
vv = append(vv, aux)
catchAll = append(catchAll, aux)
continue
}
if tmp, err := val.Cast(f); err == nil {
vv = append(vv, tmp)
}
catchAll = append(catchAll, clampString(val.Value, maxValueLength))
}
if len(vv) == 0 {
return nil
}
rval[f.Name] = vv
if field != nil {
fields[f.Name] = field.Label
}
return nil
})
return rval, fields, catchAll
}
// getUrlToResource construct page url for compose resources
func (d composeResources) getUrlToResource(page pageDetail) (url string) {
var (
host = d.opt.CortezaDomain
validNsSlung = len(page.namespaceSlug) > 0
validModuleID = page.moduleID > 0
validRecord = page.recordID > 0
validPageID = page.pageID > 0
)
if len(host) == 0 {
return
}
if validModuleID && validRecord {
// For record, take moduleID & recordID from params(page) then construct url from modulePages map
if p, is := modulePages[page.moduleID]; is {
url = fmt.Sprintf("%s/compose/ns/%s/pages/%d/record/%d", host, p.namespaceSlug, p.pageID, page.recordID)
}
} else if validNsSlung && validPageID {
// for module? @todo, we need moduleID ref for module home page
// we can check page.self_id is 0 that is modules home pages, but ref_module is also 0
url = fmt.Sprintf("%s/compose/ns/%s/pages/%d", host, page.namespaceSlug, page.pageID)
} else if validNsSlung {
// For namespace, just send to /pages FE handles fallback to home(default) page
url = fmt.Sprintf("%s/compose/ns/%s/pages", host, page.namespaceSlug)
}
return
}
func (d composeResources) getUserInfo(ctx context.Context, ID uint64, users map[uint64]sysTypes.User) (*sysTypes.User, map[uint64]sysTypes.User, error) {
if users == nil {
users = make(map[uint64]sysTypes.User)
}
user, ok := users[ID]
if ok {
return &user, users, nil
} else {
u, err := d.usr.FindByID(ctx, ID)
if err != nil && u != nil {
users[ID] = *u
}
return u, users, err
}
}
func stringifyUints64(uints []uint64) []string {
strings := make([]string, len(uints))
for i, num := range uints {
strings[i] = strconv.FormatUint(num, 10)
}
return strings
}
func clampString(input string, maxLength int) string {
if len(input) > maxLength {
return input[:maxLength]
}
return input
}

View File

@@ -0,0 +1,172 @@
package documents
import (
"time"
"github.com/cortezaproject/corteza/server/pkg/filter"
sysTypes "github.com/cortezaproject/corteza/server/system/types"
)
type (
Response struct {
Filter Filter `json:"filter"`
Documents []Document `json:"documents"`
}
Document struct {
ID uint64 `json:"id,string"`
URL string `json:"url,omitempty"`
Source interface{} `json:"source"`
}
Filter struct {
Limit uint `json:"limit"`
NextPage *filter.PagingCursor `json:"nextPage"`
}
docPartialUser struct {
UserID uint64 `json:"userID,string"`
Email string `json:"email,omitempty"`
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
Handle string `json:"handle,omitempty"`
}
docPartialChange struct {
At *time.Time `json:"at,omitempty"`
By *docPartialUser `json:"by,omitempty"`
}
docUser struct {
ResourceType string `json:"resourceType"`
UserID uint64 `json:"userID,string"`
Email string `json:"email"`
Name string `json:"name,omitempty"`
Handle string `json:"handle,omitempty"`
Suspended *time.Time `json:"suspendedAt,omitempty"`
Url string `json:"url,omitempty"`
CatchAll []any `json:"catch_all"`
Updated *docPartialChange `json:"updated,omitempty"`
Created *docPartialChange `json:"created,omitempty"`
Deleted *docPartialChange `json:"deleted,omitempty"`
Security []docSecurity `json:"security"`
}
docComposeNamespace struct {
ResourceType string `json:"resourceType"`
NamespaceID uint64 `json:"namespaceID,string"`
Name string `json:"name,omitempty"`
Handle string `json:"handle,omitempty"`
Url string `json:"url,omitempty"`
Enabled bool `json:"enabled"`
Meta docPartialComposeNamespaceMeta `json:"meta"`
CatchAll []interface{} `json:"catch_all"`
Updated *docPartialChange `json:"updated,omitempty"`
Created *docPartialChange `json:"created,omitempty"`
Deleted *docPartialChange `json:"deleted,omitempty"`
Security []docSecurity `json:"security"`
// Aggregation update
Namespace docPartialComposeNamespace `json:"namespace"`
}
docPartialComposeNamespaceMeta struct {
Subtitle string `json:"subtitle,omitempty"`
Description string `json:"description,omitempty"`
}
docPartialComposeNamespace struct {
NamespaceID uint64 `json:"namespaceID,string"`
Name string `json:"name,omitempty"`
Handle string `json:"handle,omitempty"`
}
docComposeModule struct {
ResourceType string `json:"resourceType"`
ModuleID uint64 `json:"moduleID,string"`
Name string `json:"name,omitempty"`
Handle string `json:"handle,omitempty"`
Url string `json:"url,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Fields []*docPartialComposeModuleField `json:"fields"`
Updated *docPartialChange `json:"updated,omitempty"`
Created *docPartialChange `json:"created,omitempty"`
Deleted *docPartialChange `json:"deleted,omitempty"`
Security []docSecurity `json:"security"`
// Aggregation update
Namespace docPartialComposeNamespace `json:"namespace"`
Module docPartialComposeModule `json:"module"`
CatchAll []any `json:"catch_all"`
}
docPartialComposeModuleField struct {
Name string `json:"name,omitempty"`
Label string `json:"label,omitempty"`
}
docPartialComposeModule struct {
ModuleID uint64 `json:"moduleID,string"`
Name string `json:"name,omitempty"`
Handle string `json:"handle,omitempty"`
}
docComposeRecord struct {
ResourceType string `json:"resourceType"`
RecordID uint64 `json:"recordID,string"`
Url string `json:"url,omitempty"`
Meta map[string]any `json:"labels,omitempty"`
ValueLabels map[string]string `json:"valueLabels,omitempty"`
Values map[string][]interface{} `json:"values"`
CatchAll []any `json:"catch_all"`
Updated *docPartialChange `json:"updated,omitempty"`
Created *docPartialChange `json:"created,omitempty"`
Deleted *docPartialChange `json:"deleted,omitempty"`
Security []docSecurity `json:"security"`
// Aggregation update
Namespace docPartialComposeNamespace `json:"namespace"`
Module docPartialComposeModule `json:"module"`
// Vectors content field
VectorsValue []float32 `json:"vectorsValue,omitempty"`
}
docSecurity struct {
// list of roles that are allowed to read the resource
AllowedRoles []string `json:"allowedRoles"`
// list of roles that are disallowed to read the resource
DeniedRoles []string `json:"deniedRoles"`
}
)
func makePartialChange(at *time.Time) *docPartialChange {
if at == nil {
return nil
}
// @todo handle unreadable (access-control) modules
// @todo attach security info (allow/deny roles)
return &docPartialChange{At: at}
}
func makeChange(at *time.Time, user *sysTypes.User) (out *docPartialChange) {
out = &docPartialChange{}
if at != nil {
out.At = at
}
if user != nil {
out.By = &docPartialUser{
UserID: user.ID,
Email: user.Email,
Name: user.Name,
Username: user.Username,
Handle: user.Handle,
}
}
return
}

View File

@@ -0,0 +1,111 @@
package documents
import (
"context"
"fmt"
"github.com/cortezaproject/corteza/server/discovery/service"
"github.com/cortezaproject/corteza/server/pkg/errors"
"github.com/cortezaproject/corteza/server/pkg/id"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/pkg/rbac"
sysService "github.com/cortezaproject/corteza/server/system/service"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
systemResources struct {
opt options.DiscoveryOpt
settings *types.AppSettings
rbac interface {
SignificantRoles(res rbac.Resource, op string) (aRR, dRR []uint64)
}
ac interface {
CanReadUser(ctx context.Context, r *types.User) bool
}
usr interface {
Find(context.Context, types.UserFilter) (types.UserSet, types.UserFilter, error)
}
}
)
func SystemResources() *systemResources {
return &systemResources{
opt: service.DefaultOption,
settings: sysService.CurrentSettings,
rbac: rbac.Global(),
ac: sysService.DefaultAccessControl,
usr: sysService.DefaultUser,
}
}
func (d systemResources) Users(ctx context.Context, limit uint, cur string, userID uint64, deleted uint) (rsp *Response, err error) {
return rsp, func() (err error) {
if !d.settings.Discovery.SystemUsers.Enabled {
return errors.Internal("system user indexing disabled")
}
var (
uu types.UserSet
f = types.UserFilter{
Deleted: filter.State(deleted),
}
)
if userID > 0 {
f.UserID = append(f.UserID, id.String(userID))
}
if f.Paging, err = filter.NewPaging(limit, cur); err != nil {
return err
}
if uu, f, err = d.usr.Find(ctx, f); err != nil {
return err
}
rsp = &Response{
Documents: make([]Document, len(uu)),
Filter: Filter{
Limit: limit,
NextPage: f.NextPage,
},
}
for i, u := range uu {
doc := &docUser{
ResourceType: "system:user",
UserID: u.ID,
Email: u.Email,
Name: u.Name,
Handle: u.Handle,
Suspended: u.SuspendedAt,
CatchAll: []any{u.ID, u.Email, u.Name, u.Handle},
Created: makePartialChange(&u.CreatedAt),
Updated: makePartialChange(u.UpdatedAt),
Deleted: makePartialChange(u.DeletedAt),
}
if len(d.opt.CortezaDomain) > 0 && u.ID > 0 {
doc.Url = fmt.Sprintf("%s/admin/system/user/edit/%d", d.opt.CortezaDomain, u.ID)
}
allowedRoles, deniedRoles := d.rbac.SignificantRoles(u, "read")
doc.Security = append(doc.Security, docSecurity{
AllowedRoles: stringifyUints64(allowedRoles),
DeniedRoles: stringifyUints64(deniedRoles),
})
rsp.Documents[i].ID = u.ID
rsp.Documents[i].Source = doc
}
return nil
}()
}

View File

@@ -0,0 +1,28 @@
package feed
import (
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/jmoiron/sqlx/types"
"time"
)
type (
Response struct {
Filter Filter `json:"filter"`
ActivityLogs []ActivityLog `json:"activityLogs"`
}
ActivityLog struct {
ID uint64 `json:"activityID,string"`
ResourceID uint64 `json:"resourceID,string"`
ResourceType string `json:"resourceType"`
ResourceAction string `json:"resourceAction"`
Timestamp time.Time `json:"timestamp"`
Meta types.JSONText `json:"meta"`
}
Filter struct {
Limit uint `json:"limit"`
NextPage *filter.PagingCursor `json:"nextPage"`
}
)

View File

@@ -0,0 +1,105 @@
package feed
import (
"context"
"time"
"github.com/cortezaproject/corteza/server/discovery/service"
"github.com/cortezaproject/corteza/server/discovery/types"
"github.com/cortezaproject/corteza/server/pkg/errors"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/cortezaproject/corteza/server/pkg/rbac"
)
type (
resourceActivity struct {
opt options.DiscoveryOpt
rbac interface {
SignificantRoles(res rbac.Resource, op string) (aRR, dRR []uint64)
}
ac interface {
}
resActivity service.ResourceActivityService
}
)
func ResourceActivity() *resourceActivity {
return &resourceActivity{
opt: service.DefaultOption,
rbac: rbac.Global(),
resActivity: service.DefaultResourceActivity,
}
}
func (a resourceActivity) ResourceActivities(ctx context.Context, limit uint, cur string, from *time.Time, to *time.Time) (rsp *Response, err error) {
return rsp, func() (err error) {
var (
aa types.ResourceActivitySet
f = types.ResourceActivityFilter{}
)
if from == nil || from.IsZero() {
return errors.Internal("invalid or missing from timestamp")
}
if to == nil || to.IsZero() {
now := time.Now()
to = &now
}
if from.After(*to) {
return errors.Internal("invalid from timestamp, it must be before to timestamp")
}
// Ensure both 'from' and 'to' are in UTC
utcFrom := from.UTC()
utcTo := to.UTC()
from = &utcFrom
to = &utcTo
f.FromTimestamp = from
f.ToTimestamp = to
if f.Paging, err = filter.NewPaging(limit, cur); err != nil {
return err
}
if aa, f, err = a.resActivity.Find(ctx, f); err != nil {
return err
}
rsp = &Response{
ActivityLogs: make([]ActivityLog, 0),
Filter: Filter{
Limit: limit,
NextPage: f.NextPage,
},
}
err = aa.Walk(func(a *types.ResourceActivity) error {
rsp.ActivityLogs = append(rsp.ActivityLogs, ActivityLog{
ID: a.ID,
ResourceID: a.ResourceID,
ResourceType: a.ResourceType,
ResourceAction: a.ResourceAction,
Timestamp: a.Timestamp,
Meta: a.Meta,
})
return nil
})
if err != nil {
return err
}
return nil
}()
}

View File

@@ -0,0 +1,324 @@
package mapping
import (
"context"
"fmt"
"strings"
"github.com/cortezaproject/corteza/server/compose/service"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/options"
)
type (
namespaceSearcher interface {
Find(ctx context.Context, filter types.NamespaceFilter) (types.NamespaceSet, types.NamespaceFilter, error)
}
moduleSearcher interface {
Find(ctx context.Context, filter types.ModuleFilter) (types.ModuleSet, types.ModuleFilter, error)
}
composeAccessControl interface {
CanReadRecordValueOnModuleField(ctx context.Context, r *types.ModuleField) bool
CanSearchRecordsOnModule(ctx context.Context, r *types.Module) bool
}
composeMapping struct {
ac composeAccessControl
ns namespaceSearcher
mod moduleSearcher
opts options.DiscoveryOpt
}
)
func ComposeMapping(discoveryOpts options.DiscoveryOpt) *composeMapping {
return &composeMapping{
ac: service.DefaultAccessControl,
ns: service.DefaultNamespace,
mod: service.DefaultModule,
opts: discoveryOpts,
}
}
func (m composeMapping) Namespaces(ctx context.Context) ([]*Mapping, error) {
return []*Mapping{&Mapping{
Index: "compose-namespaces",
Mapping: map[string]*property{
"resourceType": {Type: "keyword"},
"namespaceID": {Type: "long"},
"name": {Type: "keyword", Boost: 2},
"handle": {Type: "keyword", Boost: 2},
"labels": {Type: "keyword"},
"enabled": {Type: "boolean"},
"url": {Type: "text"},
"meta": {Type: "object", Properties: map[string]*property{
"subtitle": {Type: "keyword"},
"description": {Type: "text"},
}},
"created": change(),
"updated": change(),
"deleted": change(),
"security": security(),
"namespace": {
Type: "nested",
Properties: map[string]*property{
"namespaceID": {Type: "long"},
"name": {Type: "text"},
"handle": {Type: "keyword"},
},
},
"catch_all": {
Type: "text",
Analyzer: "standard",
},
},
}}, nil
}
func (m composeMapping) Modules(ctx context.Context) ([]*Mapping, error) {
return []*Mapping{&Mapping{
Index: fmt.Sprintf("compose-modules"),
Mapping: map[string]*property{
"resourceType": {Type: "keyword"},
"moduleID": {Type: "long"},
"created": change(),
"updated": change(),
"deleted": change(),
"security": security(),
"name": {Type: "text", Boost: 2},
"handle": {Type: "keyword", Boost: 2},
"url": {Type: "text"},
"labels": {
Type: "object",
},
"fields": {
Type: "object",
Properties: map[string]*property{
"name": {Type: "keyword"},
"label": {Type: "text"},
},
},
"namespace": {
Type: "nested",
Properties: map[string]*property{
"namespaceID": {Type: "long"},
"name": {Type: "text"},
"handle": {Type: "keyword"},
},
},
"module": {
Type: "nested",
Properties: map[string]*property{
"moduleID": {Type: "long"},
"name": {Type: "text"},
"handle": {Type: "keyword"},
},
},
"catch_all": {
Type: "text",
Analyzer: "standard",
},
},
}}, nil
}
func (m composeMapping) Records(ctx context.Context) ([]*Mapping, error) {
var (
nn, _, err = m.ns.Find(ctx, types.NamespaceFilter{})
out = make([]*Mapping, 0, len(nn)*50)
)
if err != nil {
return nil, err
}
return out, nn.Walk(func(ns *types.Namespace) (err error) {
// @todo do we index records in this namespace?
var (
mm types.ModuleSet
)
mm, _, err = m.mod.Find(ctx, types.ModuleFilter{NamespaceID: ns.ID})
if err != nil {
return err
}
// collect record mappings for all modules
return mm.Walk(func(mod *types.Module) error {
// @todo do we index records of module?
if m := m.records(ctx, mod, mm); m != nil {
out = append(out, m)
}
return nil
})
})
}
// Module mapping is always returned, even if it contains no properties
//
// If there are no properties indexer can assume
func (m composeMapping) records(ctx context.Context, mod *types.Module, mm types.ModuleSet) (mapping *Mapping) {
// @todo check if module can be Mappinged
mapping = &Mapping{
Index: fmt.Sprintf("compose-records-%d-%d", mod.NamespaceID, mod.ID),
Mapping: make(map[string]*property),
}
props := m.moduleFieldsMappingProperties(ctx, mod, mm)
if len(props) == 0 {
// no mapping
return
}
mapping.Mapping["resourceType"] = &property{Type: "keyword"}
mapping.Mapping["recordID"] = &property{Type: "long"}
mapping.Mapping["url"] = &property{Type: "text"}
mapping.Mapping["created"] = change()
mapping.Mapping["updated"] = change()
mapping.Mapping["deleted"] = change()
mapping.Mapping["security"] = security()
mapping.Mapping["meta"] = &property{
Type: "object",
}
mapping.Mapping["values"] = &property{
Type: "nested",
Properties: props,
}
mapping.Mapping["catch_all"] = &property{
Type: "text",
Analyzer: "standard",
}
// mapping for record is there, add fields for namespace & module
mapping.Mapping["module"] = &property{
Type: "nested",
Properties: map[string]*property{
"moduleID": {Type: "long"},
"name": {Type: "text"},
"handle": {Type: "keyword"},
},
}
mapping.Mapping["namespace"] = &property{
Type: "nested",
Properties: map[string]*property{
"namespaceID": {Type: "long"},
"name": {Type: "text"},
"handle": {Type: "keyword"},
},
}
if m.opts.EmbeddingsEnabled {
mapping.Mapping["vectorsValue"] = vector(m.opts)
}
return
}
func (m composeMapping) moduleFieldsMappingProperties(ctx context.Context, mod *types.Module, mm types.ModuleSet) map[string]*property {
mf := make(map[string]*property)
for _, f := range mod.Fields {
for k, m := range m.moduleFieldMappingProperties(ctx, f, mm) {
mf[k] = m
}
}
return mf
}
func (m composeMapping) moduleFieldMappingProperties(ctx context.Context, f *types.ModuleField, mm types.ModuleSet) map[string]*property {
if !m.ac.CanReadRecordValueOnModuleField(ctx, f) {
return nil
}
var (
p = &property{Type: "text"}
name = f.Name
)
// @todo get indexing props from f.Options!
if p.Type == "" {
// Type not explicitly set, get it by converting
switch strings.ToLower(f.Kind) {
case "bool":
p.Type = "boolean"
case "datetime":
p.Type = "date"
// https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html
// @todo format param!
case "email":
p.Type = "keyword"
case "file":
// @todo ref
case "number":
// @todo what number type to use?
return nil
case "record":
//iopt := f.Options.Index()
// @todo make sure we do not fall into recursive loop
// if ifc.parents[]
// @todo if single-value then object
// https://www.elastic.co/guide/en/elasticsearch/reference/current/object.html
// @todo if multi-value then nested
// https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html
//var deepDive = 1
//
//if deepDive == 0 {
// // no
// return nil
//}
//
//m := mm.FindByID(f.Options.Uint64("moduleID"))
//if m == nil {
// return nil
//}
//
//if opt.nestingLevel < 2 {
// return append(composeModuleFieldsMappingProperties(m, mm, Context{prefix: f.Name, nestingLevel: opt.nestingLevel + 1}), m)
//}
return nil
case "select":
p.Type = "keyword"
case "string":
p.Type = "text"
case "url":
p.Type = "keyword"
case "user":
// @todo ref
return nil
}
}
return map[string]*property{name: p}
}

View File

@@ -0,0 +1,70 @@
package mapping
import "github.com/cortezaproject/corteza/server/pkg/options"
type (
Mapping struct {
Index string `json:"index"`
Mapping map[string]*property `json:"mapping"`
}
property struct {
// https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
Type string `json:"type,omitempty"`
// Boost factor
// https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-boost.html
Boost float32 `json:"boost,omitempty"`
Analyzer string `json:"analyzer,omitempty"`
Properties map[string]*property `json:"properties,omitempty"`
// for vector indexing
Dimension int `json:"dimension,omitempty"`
Method map[string]any `json:"method,omitempty"`
}
Context struct {
AccessRestriction string
}
)
func change() *property {
return &property{
Type: "nested",
Properties: map[string]*property{
"at": {Type: "date"},
"by": {Type: "nested", Properties: map[string]*property{
"id": {Type: "long"},
"email": {Type: "keyword"},
"name": {Type: "keyword"},
"handle": {Type: "keyword"},
}},
},
}
}
func security() *property {
return &property{
Properties: map[string]*property{
"allowedRoles": {Type: "long"},
"deniedRoles": {Type: "long"},
},
}
}
func vector(opts options.DiscoveryOpt) *property {
return &property{
Type: "knn_vector",
Dimension: opts.EmbeddingsDimension,
Method: map[string]any{
"name": "hnsw",
"engine": "lucene",
"parameters": map[string]any{
"ef_construction": opts.HnswEfConstruction,
"m": opts.HnswM,
},
},
}
}

View File

@@ -0,0 +1,54 @@
package mapping
import (
"context"
"github.com/cortezaproject/corteza/server/system/service"
)
type (
systemAccessControl interface {
CanSearchUsers(ctx context.Context) bool
CanSearchRoles(ctx context.Context) bool
CanSearchApplications(ctx context.Context) bool
CanSearchAuthClients(ctx context.Context) bool
}
systemMapping struct {
ac systemAccessControl
}
)
func SystemMapping() *systemMapping {
return &systemMapping{
ac: service.DefaultAccessControl,
}
}
func (svc systemMapping) Users(_ context.Context) ([]*Mapping, error) {
return []*Mapping{{
Index: "system-users",
Mapping: map[string]*property{
"resourceType": {Type: "keyword"},
"userID": {Type: "long"},
"email": {Type: "keyword", Boost: 2},
"name": {Type: "keyword", Boost: 10},
"handle": {Type: "keyword", Boost: 2},
"created": change(),
"updated": change(),
"deleted": change(),
"suspendedAt": {Type: "date"},
"security": security(),
"catch_all": {
Type: "text",
Analyzer: "standard",
},
},
}}, nil
}

View File

@@ -0,0 +1,75 @@
package rest
import (
"context"
"github.com/cortezaproject/corteza/server/discovery/rest/internal/mapping"
"github.com/cortezaproject/corteza/server/discovery/rest/request"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/cortezaproject/corteza/server/system/service"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
mappings struct {
settings *types.AppSettings
sys interface {
Users(context.Context) ([]*mapping.Mapping, error)
}
cmp interface {
Namespaces(context.Context) ([]*mapping.Mapping, error)
Modules(context.Context) ([]*mapping.Mapping, error)
Records(context.Context) ([]*mapping.Mapping, error)
}
}
)
func Mappings(discoveryOpts options.DiscoveryOpt) *mappings {
return &mappings{
settings: service.CurrentSettings,
sys: mapping.SystemMapping(),
cmp: mapping.ComposeMapping(discoveryOpts),
}
}
func (ctrl mappings) List(ctx context.Context, r *request.MappingsList) (interface{}, error) {
var (
out = make([]*mapping.Mapping, 0, 100)
// Collection of all mapping functions we support
//
// Each function is responsible to
mappingFn = make([]func(ctx context.Context) ([]*mapping.Mapping, error), 4)
)
if ctrl.settings.Discovery.SystemUsers.Enabled {
mappingFn = append(mappingFn, ctrl.sys.Users)
}
if ctrl.settings.Discovery.ComposeNamespaces.Enabled {
mappingFn = append(mappingFn, ctrl.cmp.Namespaces)
}
if ctrl.settings.Discovery.ComposeModules.Enabled {
mappingFn = append(mappingFn, ctrl.cmp.Modules)
}
if ctrl.settings.Discovery.ComposeRecords.Enabled {
mappingFn = append(mappingFn, ctrl.cmp.Records)
}
return out, func() error {
for _, fn := range mappingFn {
if fn == nil {
continue
}
mm, err := fn(ctx)
if err != nil {
return err
}
out = append(out, mm...)
}
return nil
}()
}

View File

@@ -0,0 +1,129 @@
package request
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
//
import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza/server/pkg/payload"
"github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
"strings"
"time"
)
// dummy vars to prevent
// unused imports complain
var (
_ = chi.URLParam
_ = multipart.ErrMessageTooLarge
_ = payload.ParseUint64s
_ = strings.ToLower
_ = io.EOF
_ = fmt.Errorf
_ = json.NewEncoder
)
type (
// Internal API interface
FeedChanges struct {
// Limit GET parameter
//
// Limit
Limit uint
// PageCursor GET parameter
//
// Page cursor
PageCursor string
// From GET parameter
//
// From timestamp
From *time.Time
// To GET parameter
//
// To timestamp
To *time.Time
}
)
// NewFeedChanges request
func NewFeedChanges() *FeedChanges {
return &FeedChanges{}
}
// Auditable returns all auditable/loggable parameters
func (r FeedChanges) Auditable() map[string]interface{} {
return map[string]interface{}{
"limit": r.Limit,
"pageCursor": r.PageCursor,
"from": r.From,
"to": r.To,
}
}
// Auditable returns all auditable/loggable parameters
func (r FeedChanges) GetLimit() uint {
return r.Limit
}
// Auditable returns all auditable/loggable parameters
func (r FeedChanges) GetPageCursor() string {
return r.PageCursor
}
// Auditable returns all auditable/loggable parameters
func (r FeedChanges) GetFrom() *time.Time {
return r.From
}
// Auditable returns all auditable/loggable parameters
func (r FeedChanges) GetTo() *time.Time {
return r.To
}
// Fill processes request and fills internal variables
func (r *FeedChanges) Fill(req *http.Request) (err error) {
{
// GET params
tmp := req.URL.Query()
if val, ok := tmp["limit"]; ok && len(val) > 0 {
r.Limit, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}
}
if val, ok := tmp["from"]; ok && len(val) > 0 {
r.From, err = payload.ParseISODatePtrWithErr(val[0])
if err != nil {
return err
}
}
if val, ok := tmp["to"]; ok && len(val) > 0 {
r.To, err = payload.ParseISODatePtrWithErr(val[0])
if err != nil {
return err
}
}
}
return err
}

View File

@@ -0,0 +1,54 @@
package request
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
//
import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza/server/pkg/payload"
"github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
"strings"
)
// dummy vars to prevent
// unused imports complain
var (
_ = chi.URLParam
_ = multipart.ErrMessageTooLarge
_ = payload.ParseUint64s
_ = strings.ToLower
_ = io.EOF
_ = fmt.Errorf
_ = json.NewEncoder
)
type (
// Internal API interface
MappingsList struct {
}
)
// NewMappingsList request
func NewMappingsList() *MappingsList {
return &MappingsList{}
}
// Auditable returns all auditable/loggable parameters
func (r MappingsList) Auditable() map[string]interface{} {
return map[string]interface{}{}
}
// Fill processes request and fills internal variables
func (r *MappingsList) Fill(req *http.Request) (err error) {
return err
}

View File

@@ -0,0 +1,470 @@
package request
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
//
import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza/server/pkg/payload"
"github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
"strings"
)
// dummy vars to prevent
// unused imports complain
var (
_ = chi.URLParam
_ = multipart.ErrMessageTooLarge
_ = payload.ParseUint64s
_ = strings.ToLower
_ = io.EOF
_ = fmt.Errorf
_ = json.NewEncoder
)
type (
// Internal API interface
ResourcesSystemUsers struct {
// Limit GET parameter
//
// Limit
Limit uint
// PageCursor GET parameter
//
// Page cursor
PageCursor string
// UserID GET parameter
//
// User ID
UserID uint64 `json:",string"`
// Deleted GET parameter
//
// Exclude (0
Deleted uint
}
ResourcesComposeNamespaces struct {
// Limit GET parameter
//
// Limit
Limit uint
// PageCursor GET parameter
//
// Page cursor
PageCursor string
// NamespaceID GET parameter
//
// Namespace ID
NamespaceID uint64 `json:",string"`
// Deleted GET parameter
//
// Exclude (0
Deleted uint
}
ResourcesComposeModules struct {
// NamespaceID PATH parameter
//
// Namespace ID
NamespaceID uint64 `json:",string"`
// Limit GET parameter
//
// Limit
Limit uint
// PageCursor GET parameter
//
// Page cursor
PageCursor string
// ModuleID GET parameter
//
// Module ID
ModuleID uint64 `json:",string"`
// Deleted GET parameter
//
// Exclude (0
Deleted uint
}
ResourcesComposeRecords struct {
// NamespaceID PATH parameter
//
// Namespace ID
NamespaceID uint64 `json:",string"`
// ModuleID PATH parameter
//
// Module ID
ModuleID uint64 `json:",string"`
// Limit GET parameter
//
// Limit
Limit uint
// PageCursor GET parameter
//
// Page cursor
PageCursor string
// RecordID GET parameter
//
// Record ID
RecordID uint64 `json:",string"`
// Deleted GET parameter
//
// Exclude (0
Deleted uint
}
)
// NewResourcesSystemUsers request
func NewResourcesSystemUsers() *ResourcesSystemUsers {
return &ResourcesSystemUsers{}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesSystemUsers) Auditable() map[string]interface{} {
return map[string]interface{}{
"limit": r.Limit,
"pageCursor": r.PageCursor,
"userID": r.UserID,
"deleted": r.Deleted,
}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesSystemUsers) GetLimit() uint {
return r.Limit
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesSystemUsers) GetPageCursor() string {
return r.PageCursor
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesSystemUsers) GetUserID() uint64 {
return r.UserID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesSystemUsers) GetDeleted() uint {
return r.Deleted
}
// Fill processes request and fills internal variables
func (r *ResourcesSystemUsers) Fill(req *http.Request) (err error) {
{
// GET params
tmp := req.URL.Query()
if val, ok := tmp["limit"]; ok && len(val) > 0 {
r.Limit, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}
}
if val, ok := tmp["userID"]; ok && len(val) > 0 {
r.UserID, err = payload.ParseUint64(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["deleted"]; ok && len(val) > 0 {
r.Deleted, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
}
return err
}
// NewResourcesComposeNamespaces request
func NewResourcesComposeNamespaces() *ResourcesComposeNamespaces {
return &ResourcesComposeNamespaces{}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeNamespaces) Auditable() map[string]interface{} {
return map[string]interface{}{
"limit": r.Limit,
"pageCursor": r.PageCursor,
"namespaceID": r.NamespaceID,
"deleted": r.Deleted,
}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeNamespaces) GetLimit() uint {
return r.Limit
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeNamespaces) GetPageCursor() string {
return r.PageCursor
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeNamespaces) GetNamespaceID() uint64 {
return r.NamespaceID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeNamespaces) GetDeleted() uint {
return r.Deleted
}
// Fill processes request and fills internal variables
func (r *ResourcesComposeNamespaces) Fill(req *http.Request) (err error) {
{
// GET params
tmp := req.URL.Query()
if val, ok := tmp["limit"]; ok && len(val) > 0 {
r.Limit, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}
}
if val, ok := tmp["namespaceID"]; ok && len(val) > 0 {
r.NamespaceID, err = payload.ParseUint64(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["deleted"]; ok && len(val) > 0 {
r.Deleted, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
}
return err
}
// NewResourcesComposeModules request
func NewResourcesComposeModules() *ResourcesComposeModules {
return &ResourcesComposeModules{}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeModules) Auditable() map[string]interface{} {
return map[string]interface{}{
"namespaceID": r.NamespaceID,
"limit": r.Limit,
"pageCursor": r.PageCursor,
"moduleID": r.ModuleID,
"deleted": r.Deleted,
}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeModules) GetNamespaceID() uint64 {
return r.NamespaceID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeModules) GetLimit() uint {
return r.Limit
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeModules) GetPageCursor() string {
return r.PageCursor
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeModules) GetModuleID() uint64 {
return r.ModuleID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeModules) GetDeleted() uint {
return r.Deleted
}
// Fill processes request and fills internal variables
func (r *ResourcesComposeModules) Fill(req *http.Request) (err error) {
{
// GET params
tmp := req.URL.Query()
if val, ok := tmp["limit"]; ok && len(val) > 0 {
r.Limit, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}
}
if val, ok := tmp["moduleID"]; ok && len(val) > 0 {
r.ModuleID, err = payload.ParseUint64(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["deleted"]; ok && len(val) > 0 {
r.Deleted, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
}
{
var val string
// path params
val = chi.URLParam(req, "namespaceID")
r.NamespaceID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
}
return err
}
// NewResourcesComposeRecords request
func NewResourcesComposeRecords() *ResourcesComposeRecords {
return &ResourcesComposeRecords{}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) Auditable() map[string]interface{} {
return map[string]interface{}{
"namespaceID": r.NamespaceID,
"moduleID": r.ModuleID,
"limit": r.Limit,
"pageCursor": r.PageCursor,
"recordID": r.RecordID,
"deleted": r.Deleted,
}
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) GetNamespaceID() uint64 {
return r.NamespaceID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) GetModuleID() uint64 {
return r.ModuleID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) GetLimit() uint {
return r.Limit
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) GetPageCursor() string {
return r.PageCursor
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) GetRecordID() uint64 {
return r.RecordID
}
// Auditable returns all auditable/loggable parameters
func (r ResourcesComposeRecords) GetDeleted() uint {
return r.Deleted
}
// Fill processes request and fills internal variables
func (r *ResourcesComposeRecords) Fill(req *http.Request) (err error) {
{
// GET params
tmp := req.URL.Query()
if val, ok := tmp["limit"]; ok && len(val) > 0 {
r.Limit, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}
}
if val, ok := tmp["recordID"]; ok && len(val) > 0 {
r.RecordID, err = payload.ParseUint64(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["deleted"]; ok && len(val) > 0 {
r.Deleted, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
}
{
var val string
// path params
val = chi.URLParam(req, "namespaceID")
r.NamespaceID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
val = chi.URLParam(req, "moduleID")
r.ModuleID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
}
return err
}

View File

@@ -0,0 +1,44 @@
package rest
import (
"context"
"github.com/cortezaproject/corteza/server/discovery/rest/internal/documents"
"github.com/cortezaproject/corteza/server/discovery/rest/request"
)
type (
resources struct {
sys interface {
Users(ctx context.Context, limit uint, cur string, userID uint64, deleted uint) (*documents.Response, error)
}
cmp interface {
Namespaces(ctx context.Context, limit uint, cur string, namespaceID uint64, deleted uint) (*documents.Response, error)
Modules(ctx context.Context, namespaceID uint64, limit uint, cur string, moduleID uint64, deleted uint) (*documents.Response, error)
Records(ctx context.Context, namespaceID, moduleID uint64, limit uint, cur string, recordID uint64, deleted uint) (*documents.Response, error)
}
}
)
func Resources() *resources {
return &resources{
sys: documents.SystemResources(),
cmp: documents.ComposeResources(),
}
}
func (ctrl resources) SystemUsers(ctx context.Context, r *request.ResourcesSystemUsers) (interface{}, error) {
return ctrl.sys.Users(ctx, r.Limit, r.PageCursor, r.UserID, r.Deleted)
}
func (ctrl resources) ComposeNamespaces(ctx context.Context, r *request.ResourcesComposeNamespaces) (interface{}, error) {
return ctrl.cmp.Namespaces(ctx, r.Limit, r.PageCursor, r.NamespaceID, r.Deleted)
}
func (ctrl resources) ComposeModules(ctx context.Context, r *request.ResourcesComposeModules) (interface{}, error) {
return ctrl.cmp.Modules(ctx, r.NamespaceID, r.Limit, r.PageCursor, r.ModuleID, r.Deleted)
}
func (ctrl resources) ComposeRecords(ctx context.Context, r *request.ResourcesComposeRecords) (interface{}, error) {
return ctrl.cmp.Records(ctx, r.NamespaceID, r.ModuleID, r.Limit, r.PageCursor, r.RecordID, r.Deleted)
}

View File

@@ -0,0 +1,21 @@
package rest
import (
"github.com/go-chi/chi/v5"
"github.com/cortezaproject/corteza/server/discovery/rest/handlers"
"github.com/cortezaproject/corteza/server/pkg/auth"
"github.com/cortezaproject/corteza/server/pkg/options"
)
func MountRoutes(discoveryOpts options.DiscoveryOpt) func(r chi.Router) {
return func(r chi.Router) {
r.Group(func(r chi.Router) {
r.Use(auth.HttpTokenValidator("discovery"))
handlers.NewResources(Resources()).MountRoutes(r)
handlers.NewFeed(Feed()).MountRoutes(r)
handlers.NewMappings(Mappings(discoveryOpts)).MountRoutes(r)
})
}
}