Files
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

325 lines
7.4 KiB
Go

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}
}