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
337 lines
9.1 KiB
Go
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)
|
|
}
|