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

337 lines
9.1 KiB
Go

package rest
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/cortezaproject/corteza/extra/server-discovery/searcher"
"github.com/cortezaproject/corteza/extra/server-discovery/searcher/rest/request"
types2 "github.com/cortezaproject/corteza/server/compose/types"
)
type (
search struct{}
cResponse struct {
Response struct {
Set []struct {
NamespaceID uint64 `json:",string"`
Slug string `json:"slug"`
Name string `json:"name"`
ModuleID uint64 `json:",string"`
Handle string `json:"handle"`
Config types2.ModuleConfig `json:"config"`
} `json:"set,omitempty"`
} `json:"response,omitempty"`
}
moduleMeta struct {
Discovery ModuleMeta `json:"discovery"`
}
ModuleMeta struct {
Public struct {
Result []Result `json:"result"`
} `json:"public"`
Private struct {
Result []Result `json:"result"`
} `json:"private"`
Protected struct {
Result []Result `json:"result"`
} `json:"protected"`
}
Result struct {
Lang string `json:"lang"`
Fields []string `json:"fields"`
// @todo? TBD? excludeModuleFields, includeModuleFields <- if passed filter module field accordingly.
}
nsMeta struct {
Name string
Handle string
}
mMeta struct {
Name string
Handle string
}
)
func Search() *search {
return &search{}
}
func (s search) SearchResources(ctx context.Context, r *request.SearchResources) (out interface{}, err error) {
var (
log = searcher.DefaultLogger
allowedRoles = searcher.DefaultConfig.Searcher.AllowedRole
searchString = r.GetQuery()
size = r.GetSize()
from = r.GetFrom()
namespaceAggs = r.GetNamespaceAggs()
moduleAggs = r.GetModuleAggs()
resourceTypes = r.GetResourceTypes()
validDumpRaw = r.GetDumpRaw() != ""
page pagination
results *esSearchResponse
aggregation *esSearchResponse
nsAggregation *esSearchResponse
mAggregation *esSearchResponse
nsReq *http.Request
nsRes *http.Response
mReq *http.Request
mRes *http.Response
nsResponse cResponse
mResponse cResponse
moduleMap = make(map[string][]string)
nsHandleMap = make(map[string]nsMeta)
mHandleMap = make(map[string]mMeta)
)
esc := searcher.DefaultEsClient
results, page, err = esSearch(ctx, log, esc, searchParams{
title: "results",
query: searchString,
resourceType: resourceTypes,
from: from,
size: size,
moduleAggs: moduleAggs,
namespaceAggs: namespaceAggs,
dumpRaw: validDumpRaw,
allowedRoles: allowedRoles,
})
if err != nil {
return nil, fmt.Errorf("could not execute search: %w", err)
}
if len(searchString) == 0 {
aggregation, _, err = esSearch(ctx, log, esc, searchParams{
title: "aggregation",
// size: size,
dumpRaw: validDumpRaw,
namespaceAggs: namespaceAggs,
aggOnly: true,
allowedRoles: allowedRoles,
})
if err != nil {
return nil, fmt.Errorf("could not execute aggregation search: %w", err)
}
}
// append all namespace agg with counts no matter what
nsAggregation, _, err = esSearch(ctx, log, esc, searchParams{
title: "nsAggregation",
// size: size,
dumpRaw: validDumpRaw,
aggOnly: true,
allowedRoles: allowedRoles,
})
if err != nil {
return nil, fmt.Errorf("could not execute namespace aggregation search: %w", err)
}
if len(searchString) == 0 {
if aggregation != nil && nsAggregation != nil {
aggregation.Aggregations.Namespace = nsAggregation.Aggregations.Namespace
}
} else {
if results != nil && nsAggregation != nil {
nsMap := make(map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
})
for _, bucket := range results.Aggregations.Namespace.Namespace.Buckets {
nsMap[bucket.Key] = bucket
}
var buckets []struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}
for _, bucket := range nsAggregation.Aggregations.Namespace.Namespace.Buckets {
val, ok := nsMap[bucket.Key]
if ok {
val.DocCount = nsMap[bucket.Key].DocCount
} else {
val.Key = bucket.Key
val.DocCount = 0
}
buckets = append(buckets, val)
}
results.Aggregations.Namespace.Namespace.Buckets = buckets
}
}
// append namespace agg response which are not in es response
if results != nil && len(namespaceAggs) > 0 {
nsMap := make(map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
})
var bb []struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}
for _, b := range results.Aggregations.Namespace.Namespace.Buckets {
nsMap = map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}{
b.Key: b,
}
bb = append(bb, b)
}
for _, agg := range namespaceAggs {
if _, ok := nsMap[agg]; !ok {
nsMap = map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}{
agg: {Key: agg, DocCount: 0},
}
bb = append(bb, struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}{Key: agg, DocCount: 0})
}
}
if len(bb) > 0 {
results.Aggregations.Namespace.Namespace.Buckets = bb
}
}
mAggregation, _, err = esSearch(ctx, log, esc, searchParams{
title: "mAggregation",
// size: size,
dumpRaw: validDumpRaw,
query: searchString,
namespaceAggs: namespaceAggs,
aggOnly: true,
mAggOnly: true,
allowedRoles: allowedRoles,
})
if err != nil {
return nil, fmt.Errorf("could not execute module aggregation search: %w", err)
}
if len(searchString) > 0 {
if results != nil && mAggregation != nil {
results.Aggregations.Module = mAggregation.Aggregations.Module
}
}
// append module agg response which are not in es response
if results != nil && len(moduleAggs) > 0 {
mMap := make(map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
})
var bb []struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}
for _, b := range results.Aggregations.Module.Module.Buckets {
mMap = map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}{
b.Key: b,
}
bb = append(bb, b)
}
for _, agg := range moduleAggs {
if _, ok := mMap[agg]; !ok {
mMap = map[string]struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}{
agg: {Key: agg, DocCount: 0},
}
bb = append(bb, struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
}{Key: agg, DocCount: 0})
}
}
if len(bb) > 0 {
results.Aggregations.Module.Module.Buckets = bb
}
}
noHits := len(searchString) == 0 && len(moduleAggs) == 0 && len(namespaceAggs) == 0
// if !noHits {
// @todo only fetch module from result but that requires another loop to fetch module Id from es response
// TEMP fix, I have solution use elastic for the same but different index
nsReq, err = searcher.DefaultApiClient.Namespaces()
if err != nil {
return nil, fmt.Errorf("failed to prepare namespace request: %w", err)
} else {
if nsRes, err = searcher.DefaultApiClient.HttpClient().Do(nsReq.WithContext(ctx)); err != nil {
return nil, fmt.Errorf("failed to send namespace request: %w", err)
}
if nsRes.StatusCode != http.StatusOK {
fmt.Println("err: ", err)
return nil, fmt.Errorf("request resulted in an unexpected status: %s: %w", err)
}
if err = json.NewDecoder(nsRes.Body).Decode(&nsResponse); err != nil {
return nil, fmt.Errorf("failed to decode namespace response: %w", err)
}
if err = nsRes.Body.Close(); err != nil {
return nil, fmt.Errorf("failed to close namespace response body: %w", err)
}
for _, s := range nsResponse.Response.Set {
// Get the module handles for aggs response
nsHandleMap[s.Slug] = nsMeta{
Name: s.Name,
Handle: s.Slug,
}
if mReq, err = searcher.DefaultApiClient.Modules(s.NamespaceID); err != nil {
return nil, fmt.Errorf("failed to prepare module meta request: %w", err)
}
if mRes, err = searcher.DefaultApiClient.HttpClient().Do(mReq.WithContext(ctx)); err != nil {
return nil, fmt.Errorf("failed to send module request: %w", err)
}
if mRes.StatusCode != http.StatusOK {
return nil, fmt.Errorf("request resulted in an unexpected status: %s: %w", err)
}
if err = json.NewDecoder(mRes.Body).Decode(&mResponse); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
if err = mRes.Body.Close(); err != nil {
return nil, fmt.Errorf("failed to close response body: %w", err)
}
for _, m := range mResponse.Response.Set {
// Get the module handles for aggs response
mHandleMap[m.Handle] = mMeta{
Name: m.Name,
Handle: m.Slug,
}
var (
key = fmt.Sprintf("%d-%d", s.NamespaceID, m.ModuleID)
)
if len(m.Config.Discovery.Private.Result) > 0 && len(m.Config.Discovery.Private.Result[0].Fields) > 0 {
moduleMap[key] = m.Config.Discovery.Private.Result[0].Fields
}
}
}
}
// }
return conv(results, aggregation, noHits, moduleMap, nsHandleMap, mHandleMap, page)
}