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
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:
208
server/compose/envoy/csv_encode.go
Normal file
208
server/compose/envoy/csv_encode.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/envoyx"
|
||||
"github.com/cortezaproject/corteza/server/pkg/envoyx/datasource"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
CsvEncoder struct{}
|
||||
)
|
||||
|
||||
func (e CsvEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt string, nodes envoyx.NodeSet, tt envoyx.Traverser) (err error) {
|
||||
w, err := e.getWriter(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cw := csv.NewWriter(w)
|
||||
|
||||
switch rt {
|
||||
case ComposeRecordDatasourceAuxType:
|
||||
_, err = e.encodeRecordDatasources(ctx, cw, p, nodes, tt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cw.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
func (e CsvEncoder) encodeRecordDatasources(ctx context.Context, writer *csv.Writer, p envoyx.EncodeParams, nodes envoyx.NodeSet, tt envoyx.Traverser) (out any, err error) {
|
||||
for _, n := range nodes {
|
||||
_, err = e.encodeRecordDatasource(ctx, writer, p, n, tt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e CsvEncoder) encodeRecordDatasource(ctx context.Context, writer *csv.Writer, p envoyx.EncodeParams, node *envoyx.Node, tt envoyx.Traverser) (_ any, err error) {
|
||||
rds := node.Datasource.(*RecordDatasource)
|
||||
resolved := map[string]string{}
|
||||
|
||||
var loc *time.Location
|
||||
loc, err = resolveTimezone(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
header := make([]string, 0, 4)
|
||||
|
||||
for _, m := range p.FieldMapping {
|
||||
if strings.ToLower(m.Field) != "id" {
|
||||
header = append(header, m.Field)
|
||||
}
|
||||
}
|
||||
header = append([]string{"ID"}, header...)
|
||||
|
||||
mvDelimiter := ";"
|
||||
wrapBrackets := false
|
||||
|
||||
if p.Params["multiValueDelimiter"] == nil {
|
||||
wrapBrackets = false
|
||||
mvDelimiter = ";"
|
||||
}
|
||||
|
||||
switch cast.ToString(p.Params["multiValueDelimiter"]) {
|
||||
case ",":
|
||||
mvDelimiter = ","
|
||||
wrapBrackets = false
|
||||
case ";":
|
||||
mvDelimiter = ";"
|
||||
wrapBrackets = false
|
||||
case "|":
|
||||
mvDelimiter = "|"
|
||||
wrapBrackets = false
|
||||
case "[,]":
|
||||
mvDelimiter = ","
|
||||
wrapBrackets = true
|
||||
case "[;]":
|
||||
mvDelimiter = ";"
|
||||
wrapBrackets = true
|
||||
case "[|]":
|
||||
mvDelimiter = "|"
|
||||
wrapBrackets = true
|
||||
}
|
||||
|
||||
getValue := func(cache datasource.RawRecord, h string) string {
|
||||
v := cache[h]
|
||||
if len(v.Values) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Encode as mf if the OG field is multi value OR we're on the resolved record column
|
||||
src, ok := resolved[h]
|
||||
isDt := rds.datetimeFields[h] || (ok && rds.datetimeFields[src])
|
||||
if !rds.multivalues[h] && !(ok && rds.multivalues[src]) {
|
||||
vv := v.Values[0]
|
||||
if isDt {
|
||||
vv = formatInTimezone(vv, loc)
|
||||
}
|
||||
|
||||
return vv
|
||||
}
|
||||
|
||||
auxv := make([]string, 0, len(v.Values))
|
||||
for _, vv := range v.Values {
|
||||
if isDt {
|
||||
vv = formatInTimezone(vv, loc)
|
||||
}
|
||||
|
||||
if strings.Contains(vv, mvDelimiter) {
|
||||
auxv = append(auxv, fmt.Sprintf("\"%s\"", vv))
|
||||
} else {
|
||||
auxv = append(auxv, vv)
|
||||
}
|
||||
}
|
||||
|
||||
out := strings.Join(auxv, mvDelimiter)
|
||||
if wrapBrackets {
|
||||
out = fmt.Sprintf("[%s]", out)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Pre-build resolved field set from datasource so header is correct even
|
||||
// when the first row has no value for a resolved field.
|
||||
resolvedFieldSet := make(map[string]bool)
|
||||
for _, f := range rds.ResolvedFields() {
|
||||
resolvedFieldSet[f] = true
|
||||
}
|
||||
|
||||
row := make([]string, 0, 4)
|
||||
var (
|
||||
more bool
|
||||
hWritten bool
|
||||
)
|
||||
for {
|
||||
cache := make(datasource.RawRecord)
|
||||
_, more, err = rds.Next(ctx, cache)
|
||||
if err != nil || !more {
|
||||
return
|
||||
}
|
||||
|
||||
if len(header) == 0 {
|
||||
for k := range cache {
|
||||
header = append(header, k)
|
||||
}
|
||||
}
|
||||
|
||||
if !hWritten {
|
||||
hWritten = true
|
||||
|
||||
// Splice in "X ID" columns after each resolved field
|
||||
newHeader := make([]string, 0, len(header)+len(resolvedFieldSet))
|
||||
for _, h := range header {
|
||||
newHeader = append(newHeader, h)
|
||||
if resolvedFieldSet[h] {
|
||||
idCol := fmt.Sprintf("%s ID", h)
|
||||
newHeader = append(newHeader, idCol)
|
||||
resolved[idCol] = h
|
||||
}
|
||||
}
|
||||
header = newHeader
|
||||
|
||||
err = writer.Write(header)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range header {
|
||||
row = append(row, getValue(cache, h))
|
||||
}
|
||||
|
||||
err = writer.Write(row)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
row = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e CsvEncoder) getWriter(p envoyx.EncodeParams) (out io.Writer, err error) {
|
||||
aux, ok := p.Params[paramsKeyWriter]
|
||||
if ok {
|
||||
out, ok = aux.(io.Writer)
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = fmt.Errorf("csv encoder expects a writer conforming to io.Writer interface")
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user