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:
30
extra/server-discovery/vendor/github.com/PaesslerAG/gval/.gitignore
generated
vendored
Normal file
30
extra/server-discovery/vendor/github.com/PaesslerAG/gval/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
coverage.out
|
||||
|
||||
manual_test.go
|
||||
*.out
|
||||
*.err
|
||||
|
||||
.vscode
|
||||
12
extra/server-discovery/vendor/github.com/PaesslerAG/gval/.travis.yml
generated
vendored
Normal file
12
extra/server-discovery/vendor/github.com/PaesslerAG/gval/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
language: go
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- go test -bench=. -benchmem -timeout 9m -coverprofile coverage.out
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
|
||||
- go test -bench=Random -benchtime 3m -timeout 9m -benchmem -coverprofile coverage.out
|
||||
|
||||
go: "1.15"
|
||||
12
extra/server-discovery/vendor/github.com/PaesslerAG/gval/LICENSE
generated
vendored
Normal file
12
extra/server-discovery/vendor/github.com/PaesslerAG/gval/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Copyright (c) 2017, Paessler AG <support@paessler.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
163
extra/server-discovery/vendor/github.com/PaesslerAG/gval/README.md
generated
vendored
Normal file
163
extra/server-discovery/vendor/github.com/PaesslerAG/gval/README.md
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# Gval
|
||||
|
||||
[](https://pkg.go.dev/github.com/PaesslerAG/gval)
|
||||
[](https://travis-ci.org/PaesslerAG/gval)
|
||||
[](https://coveralls.io/github/PaesslerAG/gval?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/PaesslerAG/gval)
|
||||
|
||||
Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions.
|
||||
|
||||

|
||||
|
||||
## Evaluate
|
||||
|
||||
Gval can evaluate expressions with parameters, arimethetic, logical, and string operations:
|
||||
|
||||
- basic expression: [10 > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Basic)
|
||||
- parameterized expression: [foo > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Parameter)
|
||||
- nested parameterized expression: [foo.bar > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedParameter)
|
||||
- arithmetic expression: [(requests_made * requests_succeeded / 100) >= 90](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Arithmetic)
|
||||
- string expression: [http_response_body == "service is ok"](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-String)
|
||||
- float64 expression: [(mem_used / total_mem) * 100](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Float64)
|
||||
|
||||
It can easily be extended with custom functions or operators:
|
||||
|
||||
- custom date comparator: [date(\`2014-01-02\`) > date(\`2014-01-01 23:59:59\`)](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-DateComparison)
|
||||
- string length: [strlen("someReallyLongInputString") <= 16](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Strlen)
|
||||
|
||||
You can parse gval.Expressions once and re-use them multiple times. Parsing is the compute-intensive phase of the process, so if you intend to use the same expression with different parameters, just parse it once:
|
||||
|
||||
- [Parsing and Evaluation](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluable)
|
||||
|
||||
The normal Go-standard order of operators is respected. When writing an expression, be sure that you either order the operators correctly, or use parentheses to clarify which portions of an expression should be run first.
|
||||
|
||||
Strings, numbers, and booleans can be used like in Go:
|
||||
|
||||
- [(7 < "47" == true ? "hello world!\n\u263a") + \` more text\`](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Encoding)
|
||||
|
||||
## Parameter
|
||||
|
||||
Variables can be accessed via string literals. They can be used for values with string keys if the parameter is a `map[string]interface{}` or `map[interface{}]interface{}` and for fields or methods if the parameter is a struct.
|
||||
|
||||
- [foo > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Parameter)
|
||||
|
||||
### Bracket Selector
|
||||
|
||||
Map and array elements and Struct Field can be accessed via `[]`.
|
||||
|
||||
- [foo[0]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Array)
|
||||
- [foo["b" + "a" + "r"]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-ExampleEvaluate_ComplexAccessor)
|
||||
|
||||
### Dot Selector
|
||||
|
||||
A nested variable with a name containing only letters and underscores can be accessed via a dot selector.
|
||||
|
||||
- [foo.bar > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedParameter)
|
||||
|
||||
### Custom Selector
|
||||
|
||||
Parameter names like `response-time` will be interpreted as `response` minus `time`. While gval doesn't support these parameter names directly, you can easily access them via a custom extension like [JSON Path](https://github.com/PaesslerAG/jsonpath):
|
||||
|
||||
- [$["response-time"]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Jsonpath)
|
||||
|
||||
Jsonpath is also suitable for accessing array elements.
|
||||
|
||||
### Fields and Methods
|
||||
|
||||
If you have structs in your parameters, you can access their fields and methods in the usual way:
|
||||
|
||||
- [foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-FlatAccessor)
|
||||
|
||||
It also works if the parameter is a struct directly
|
||||
[Hello + World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Accessor)
|
||||
or if the fields are nested
|
||||
[foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedAccessor)
|
||||
|
||||
This may be convenient but note that using accessors on strucs makes the expression about four times slower than just using a parameter (consult the benchmarks for more precise measurements on your system). If there are functions you want to use, it's faster (and probably cleaner) to define them as functions (see the Evaluate section). These approaches use no reflection, and are designed to be fast and clean.
|
||||
|
||||
## Default Language
|
||||
|
||||
The default language is in serveral sub languages like text, arithmetic or propositional logic defined. See [Godoc](https://pkg.go.dev/github.com/PaesslerAG/gval/#Gval) for details. All sub languages are merged into gval.Full which contains the following elements:
|
||||
|
||||
- Modifiers: `+` `-` `/` `*` `&` `|` `^` `**` `%` `>>` `<<`
|
||||
- Comparators: `>` `>=` `<` `<=` `==` `!=` `=~` `!~`
|
||||
- Logical ops: `||` `&&`
|
||||
- Numeric constants, as 64-bit floating point (`12345.678`)
|
||||
- String constants (double quotes: `"foobar"`)
|
||||
- Date function 'Date(x)', using any permutation of RFC3339, ISO8601, ruby date, or unix date
|
||||
- Boolean constants: `true` `false`
|
||||
- Parentheses to control order of evaluation `(` `)`
|
||||
- Json Arrays : `[1, 2, "foo"]`
|
||||
- Json Objects : `{"a":1, "b":2, "c":"foo"}`
|
||||
- Prefixes: `!` `-` `~`
|
||||
- Ternary conditional: `?` `:`
|
||||
- Null coalescence: `??`
|
||||
|
||||
## Customize
|
||||
|
||||
Gval is completly customizable. Every constant, function or operator can be defined separately and existing expression languages can be reused:
|
||||
|
||||
- [foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Language)
|
||||
|
||||
For details see [Godoc](https://pkg.go.dev/github.com/PaesslerAG/gval).
|
||||
|
||||
### Implementing custom selector
|
||||
|
||||
In a case you want to provide custom logic for selectors you can implement `SelectGVal(ctx context.Context, k string) (interface{}, error)` on your struct.
|
||||
Function receives next part of the path and can return any type of var that is again evaluated through standard gval procedures.
|
||||
|
||||
[Example Custom Selector](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-custom-selector)
|
||||
|
||||
### External gval Languages
|
||||
|
||||
A list of external libraries for gval. Feel free to add your own library.
|
||||
|
||||
- [gvalstrings](https://github.com/generikvault/gvalstrings) parse single quoted strings in gval.
|
||||
- [jsonpath](https://github.com/PaesslerAG/jsonpath) full support for jsonpath in gval.
|
||||
|
||||
## Performance
|
||||
|
||||
The library is built with the intention of being quick but has not been aggressively profiled and optimized. For most applications, though, it is completely fine.
|
||||
If performance is an issue, make sure to create your expression language with all functions, constants and operators only once. Evaluating an expression like gval.Evaluate("expression, const1, func1, func2, ...) creates a new gval.Language everytime it is called and slows execution.
|
||||
|
||||
The library comes with a bunch of benchmarks to measure the performance of parsing and evaluating expressions. You can run them with `go test -bench=.`.
|
||||
|
||||
For a very rough idea of performance, here are the results from a benchmark run on a Dell Latitude E7470 Win 10 i5-6300U.
|
||||
|
||||
``` text
|
||||
BenchmarkGval/const_evaluation-4 500000000 3.57 ns/op
|
||||
BenchmarkGval/const_parsing-4 1000000 1144 ns/op
|
||||
BenchmarkGval/single_parameter_evaluation-4 10000000 165 ns/op
|
||||
BenchmarkGval/single_parameter_parsing-4 1000000 1648 ns/op
|
||||
BenchmarkGval/parameter_evaluation-4 5000000 352 ns/op
|
||||
BenchmarkGval/parameter_parsing-4 500000 2773 ns/op
|
||||
BenchmarkGval/common_evaluation-4 3000000 434 ns/op
|
||||
BenchmarkGval/common_parsing-4 300000 4419 ns/op
|
||||
BenchmarkGval/complex_evaluation-4 100000000 11.6 ns/op
|
||||
BenchmarkGval/complex_parsing-4 100000 17936 ns/op
|
||||
BenchmarkGval/literal_evaluation-4 300000000 3.84 ns/op
|
||||
BenchmarkGval/literal_parsing-4 500000 2559 ns/op
|
||||
BenchmarkGval/modifier_evaluation-4 500000000 3.54 ns/op
|
||||
BenchmarkGval/modifier_parsing-4 500000 3755 ns/op
|
||||
BenchmarkGval/regex_evaluation-4 50000 21347 ns/op
|
||||
BenchmarkGval/regex_parsing-4 200000 6480 ns/op
|
||||
BenchmarkGval/constant_regex_evaluation-4 1000000 1000 ns/op
|
||||
BenchmarkGval/constant_regex_parsing-4 200000 9417 ns/op
|
||||
BenchmarkGval/accessors_evaluation-4 3000000 417 ns/op
|
||||
BenchmarkGval/accessors_parsing-4 1000000 1778 ns/op
|
||||
BenchmarkGval/accessors_method_evaluation-4 1000000 1931 ns/op
|
||||
BenchmarkGval/accessors_method_parsing-4 1000000 1729 ns/op
|
||||
BenchmarkGval/accessors_method_parameter_evaluation-4 1000000 2162 ns/op
|
||||
BenchmarkGval/accessors_method_parameter_parsing-4 500000 2618 ns/op
|
||||
BenchmarkGval/nested_accessors_evaluation-4 2000000 681 ns/op
|
||||
BenchmarkGval/nested_accessors_parsing-4 1000000 2115 ns/op
|
||||
BenchmarkRandom-4 500000 3631 ns/op
|
||||
ok
|
||||
```
|
||||
|
||||
## API Breaks
|
||||
|
||||
Gval is designed with easy expandability in mind and API breaks will be avoided if possible. If API breaks are unavoidable they wil be explicitly stated via an increased major version number.
|
||||
|
||||
-------------------------------------
|
||||
Credits to Reene French for the gophers.
|
||||
348
extra/server-discovery/vendor/github.com/PaesslerAG/gval/evaluable.go
generated
vendored
Normal file
348
extra/server-discovery/vendor/github.com/PaesslerAG/gval/evaluable.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Selector allows for custom variable selection from structs
|
||||
//
|
||||
// Return value is again handled with variable() until end of the given path
|
||||
type Selector interface {
|
||||
SelectGVal(c context.Context, key string) (interface{}, error)
|
||||
}
|
||||
|
||||
// Evaluable evaluates given parameter
|
||||
type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)
|
||||
|
||||
//EvalInt evaluates given parameter to an int
|
||||
func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error) {
|
||||
v, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
f, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
|
||||
}
|
||||
return int(f), nil
|
||||
}
|
||||
|
||||
//EvalFloat64 evaluates given parameter to a float64
|
||||
func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error) {
|
||||
v, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
f, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
//EvalBool evaluates given parameter to a bool
|
||||
func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error) {
|
||||
v, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
b, ok := convertToBool(v)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("expected bool but got %v (%T)", v, v)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
//EvalString evaluates given parameter to a string
|
||||
func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error) {
|
||||
o, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%v", o), nil
|
||||
}
|
||||
|
||||
//Const Evaluable represents given constant
|
||||
func (*Parser) Const(value interface{}) Evaluable {
|
||||
return constant(value)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func constant(value interface{}) Evaluable {
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
//Var Evaluable represents value at given path.
|
||||
//It supports with default language VariableSelector:
|
||||
// map[interface{}]interface{},
|
||||
// map[string]interface{} and
|
||||
// []interface{} and via reflect
|
||||
// struct fields,
|
||||
// struct methods,
|
||||
// slices and
|
||||
// map with int or string key.
|
||||
func (p *Parser) Var(path ...Evaluable) Evaluable {
|
||||
if p.selector == nil {
|
||||
return variable(path)
|
||||
}
|
||||
return p.selector(path)
|
||||
}
|
||||
|
||||
// Evaluables is a slice of Evaluable.
|
||||
type Evaluables []Evaluable
|
||||
|
||||
// EvalStrings evaluates given parameter to a string slice
|
||||
func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error) {
|
||||
strs := make([]string, len(evs))
|
||||
for i, p := range evs {
|
||||
k, err := p.EvalString(c, parameter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
strs[i] = k
|
||||
}
|
||||
return strs, nil
|
||||
}
|
||||
|
||||
func variable(path Evaluables) Evaluable {
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
keys, err := path.EvalStrings(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, k := range keys {
|
||||
switch o := v.(type) {
|
||||
case Selector:
|
||||
v, err = o.SelectGVal(c, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select '%s' on %T: %w", k, o, err)
|
||||
}
|
||||
continue
|
||||
case map[interface{}]interface{}:
|
||||
v = o[k]
|
||||
continue
|
||||
case map[string]interface{}:
|
||||
v = o[k]
|
||||
continue
|
||||
case []interface{}:
|
||||
if i, err := strconv.Atoi(k); err == nil && i >= 0 && len(o) > i {
|
||||
v = o[i]
|
||||
continue
|
||||
}
|
||||
default:
|
||||
var ok bool
|
||||
v, ok = reflectSelect(k, o)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
|
||||
vv := reflect.ValueOf(value)
|
||||
vvElem := resolvePotentialPointer(vv)
|
||||
|
||||
switch vvElem.Kind() {
|
||||
case reflect.Map:
|
||||
mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
|
||||
vvElem = resolvePotentialPointer(vvElem)
|
||||
|
||||
if vvElem.IsValid() {
|
||||
return vvElem.Interface(), true
|
||||
}
|
||||
case reflect.Slice:
|
||||
if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
|
||||
vvElem = resolvePotentialPointer(vv.Index(i))
|
||||
return vvElem.Interface(), true
|
||||
}
|
||||
case reflect.Struct:
|
||||
field := vvElem.FieldByName(key)
|
||||
if field.IsValid() {
|
||||
return field.Interface(), true
|
||||
}
|
||||
|
||||
method := vv.MethodByName(key)
|
||||
if method.IsValid() {
|
||||
return method.Interface(), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func resolvePotentialPointer(value reflect.Value) reflect.Value {
|
||||
if value.Kind() == reflect.Ptr {
|
||||
return value.Elem()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
|
||||
switch k {
|
||||
case reflect.String:
|
||||
return value, true
|
||||
case reflect.Int:
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
|
||||
return func(c context.Context, v interface{}) (ret interface{}, err error) {
|
||||
a := make([]interface{}, len(args))
|
||||
for i, arg := range args {
|
||||
ai, err := arg(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a[i] = ai
|
||||
}
|
||||
return fun(c, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable) Evaluable {
|
||||
return func(c context.Context, v interface{}) (ret interface{}, err error) {
|
||||
f, err := fun(c, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not call function: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("failed to execute function '%s': %s", fullname, r)
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
|
||||
ff := reflect.ValueOf(f)
|
||||
|
||||
if ff.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("could not call '%s' type %T", fullname, f)
|
||||
}
|
||||
|
||||
a := make([]reflect.Value, len(args))
|
||||
for i := range args {
|
||||
arg, err := args[i](c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a[i] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
rr := ff.Call(a)
|
||||
|
||||
r := make([]interface{}, len(rr))
|
||||
for i, e := range rr {
|
||||
r[i] = e.Interface()
|
||||
}
|
||||
|
||||
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
|
||||
if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
|
||||
if r[len(r)-1] != nil {
|
||||
err = r[len(r)-1].(error)
|
||||
}
|
||||
r = r[0 : len(r)-1]
|
||||
}
|
||||
|
||||
switch len(r) {
|
||||
case 0:
|
||||
return err, nil
|
||||
case 1:
|
||||
return r[0], err
|
||||
default:
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//IsConst returns if the Evaluable is a Parser.Const() value
|
||||
func (e Evaluable) IsConst() bool {
|
||||
pc := reflect.ValueOf(constant(nil)).Pointer()
|
||||
pe := reflect.ValueOf(e).Pointer()
|
||||
return pc == pe
|
||||
}
|
||||
|
||||
func regEx(a, b Evaluable) (Evaluable, error) {
|
||||
if !b.IsConst() {
|
||||
return func(c context.Context, o interface{}) (interface{}, error) {
|
||||
a, err := a.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := b.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matched, err := regexp.MatchString(b, a)
|
||||
return matched, err
|
||||
}, nil
|
||||
}
|
||||
s, err := b.EvalString(context.TODO(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
regex, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
s, err := a.EvalString(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return regex.MatchString(s), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func notRegEx(a, b Evaluable) (Evaluable, error) {
|
||||
if !b.IsConst() {
|
||||
return func(c context.Context, o interface{}) (interface{}, error) {
|
||||
a, err := a.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := b.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matched, err := regexp.MatchString(b, a)
|
||||
return !matched, err
|
||||
}, nil
|
||||
}
|
||||
s, err := b.EvalString(context.TODO(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
regex, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
s, err := a.EvalString(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return !regex.MatchString(s), nil
|
||||
}, nil
|
||||
}
|
||||
126
extra/server-discovery/vendor/github.com/PaesslerAG/gval/functions.go
generated
vendored
Normal file
126
extra/server-discovery/vendor/github.com/PaesslerAG/gval/functions.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type function func(ctx context.Context, arguments ...interface{}) (interface{}, error)
|
||||
|
||||
func toFunc(f interface{}) function {
|
||||
if f, ok := f.(func(arguments ...interface{}) (interface{}, error)); ok {
|
||||
return function(func(ctx context.Context, arguments ...interface{}) (interface{}, error) {
|
||||
var v interface{}
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
errCh <- fmt.Errorf("%v", recovered)
|
||||
}
|
||||
}()
|
||||
result, err := f(arguments...)
|
||||
v = result
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case err := <-errCh:
|
||||
return v, err
|
||||
}
|
||||
})
|
||||
}
|
||||
if f, ok := f.(func(ctx context.Context, arguments ...interface{}) (interface{}, error)); ok {
|
||||
return function(f)
|
||||
}
|
||||
|
||||
fun := reflect.ValueOf(f)
|
||||
t := fun.Type()
|
||||
return func(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
var v interface{}
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
errCh <- fmt.Errorf("%v", recovered)
|
||||
}
|
||||
}()
|
||||
in, err := createCallArguments(ctx, t, args)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
out := fun.Call(in)
|
||||
|
||||
r := make([]interface{}, len(out))
|
||||
for i, e := range out {
|
||||
r[i] = e.Interface()
|
||||
}
|
||||
|
||||
err = nil
|
||||
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
|
||||
if len(r) > 0 && t.Out(len(r)-1).Implements(errorInterface) {
|
||||
if r[len(r)-1] != nil {
|
||||
err = r[len(r)-1].(error)
|
||||
}
|
||||
r = r[0 : len(r)-1]
|
||||
}
|
||||
|
||||
switch len(r) {
|
||||
case 0:
|
||||
v = nil
|
||||
case 1:
|
||||
v = r[0]
|
||||
default:
|
||||
v = r
|
||||
}
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case err := <-errCh:
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createCallArguments(ctx context.Context, t reflect.Type, args []interface{}) ([]reflect.Value, error) {
|
||||
variadic := t.IsVariadic()
|
||||
numIn := t.NumIn()
|
||||
|
||||
// if first argument is a context, use the given execution context
|
||||
if numIn > 0 {
|
||||
thisFun := reflect.ValueOf(createCallArguments)
|
||||
thisT := thisFun.Type()
|
||||
if t.In(0) == thisT.In(0) {
|
||||
args = append([]interface{}{ctx}, args...)
|
||||
}
|
||||
}
|
||||
|
||||
if (!variadic && len(args) != numIn) || (variadic && len(args) < numIn-1) {
|
||||
return nil, fmt.Errorf("invalid number of parameters")
|
||||
}
|
||||
|
||||
in := make([]reflect.Value, len(args))
|
||||
var inType reflect.Type
|
||||
for i, arg := range args {
|
||||
if !variadic || i < numIn-1 {
|
||||
inType = t.In(i)
|
||||
} else if i == numIn-1 {
|
||||
inType = t.In(numIn - 1).Elem()
|
||||
}
|
||||
argVal := reflect.ValueOf(arg)
|
||||
if arg == nil {
|
||||
argVal = reflect.ValueOf(reflect.Interface)
|
||||
} else if !argVal.Type().AssignableTo(inType) {
|
||||
return nil, fmt.Errorf("expected type %s for parameter %d but got %T",
|
||||
inType.String(), i, arg)
|
||||
}
|
||||
in[i] = argVal
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
327
extra/server-discovery/vendor/github.com/PaesslerAG/gval/gval.go
generated
vendored
Normal file
327
extra/server-discovery/vendor/github.com/PaesslerAG/gval/gval.go
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
// Package gval provides a generic expression language.
|
||||
// All functions, infix and prefix operators can be replaced by composing languages into a new one.
|
||||
//
|
||||
// The package contains concrete expression languages for common application in text, arithmetic, decimal arithmetic, propositional logic and so on.
|
||||
// They can be used as basis for a custom expression language or to evaluate expressions directly.
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"text/scanner"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
//Evaluate given parameter with given expression in gval full language
|
||||
func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error) {
|
||||
return EvaluateWithContext(context.Background(), expression, parameter, opts...)
|
||||
}
|
||||
|
||||
//Evaluate given parameter with given expression in gval full language using a context
|
||||
func EvaluateWithContext(c context.Context, expression string, parameter interface{}, opts ...Language) (interface{}, error) {
|
||||
l := full
|
||||
if len(opts) > 0 {
|
||||
l = NewLanguage(append([]Language{l}, opts...)...)
|
||||
}
|
||||
return l.EvaluateWithContext(c, expression, parameter)
|
||||
}
|
||||
|
||||
// Full is the union of Arithmetic, Bitmask, Text, PropositionalLogic, and Json
|
||||
// Operator in: a in b is true iff value a is an element of array b
|
||||
// Operator ??: a ?? b returns a if a is not false or nil, otherwise n
|
||||
// Operator ?: a ? b : c returns b if bool a is true, otherwise b
|
||||
//
|
||||
// Function Date: Date(a) parses string a. a must match RFC3339, ISO8601, ruby date, or unix date
|
||||
func Full(extensions ...Language) Language {
|
||||
if len(extensions) == 0 {
|
||||
return full
|
||||
}
|
||||
return NewLanguage(append([]Language{full}, extensions...)...)
|
||||
}
|
||||
|
||||
// Arithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-)
|
||||
// and numerical order (<=,<,>,>=)
|
||||
//
|
||||
// Arithmetic operators expect float64 operands.
|
||||
// Called with unfitting input, they try to convert the input to float64.
|
||||
// They can parse strings and convert any type of int or float.
|
||||
func Arithmetic() Language {
|
||||
return arithmetic
|
||||
}
|
||||
|
||||
// DecimalArithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-)
|
||||
// and numerical order (<=,<,>,>=)
|
||||
//
|
||||
// DecimalArithmetic operators expect decimal.Decimal operands (github.com/shopspring/decimal)
|
||||
// and are used to calculate money/decimal rather than floating point calculations.
|
||||
// Called with unfitting input, they try to convert the input to decimal.Decimal.
|
||||
// They can parse strings and convert any type of int or float.
|
||||
func DecimalArithmetic() Language {
|
||||
return decimalArithmetic
|
||||
}
|
||||
|
||||
// Bitmask contains base, bitwise and(&), bitwise or(|) and bitwise not(^).
|
||||
//
|
||||
// Bitmask operators expect float64 operands.
|
||||
// Called with unfitting input they try to convert the input to float64.
|
||||
// They can parse strings and convert any type of int or float.
|
||||
func Bitmask() Language {
|
||||
return bitmask
|
||||
}
|
||||
|
||||
// Text contains base, lexical order on strings (<=,<,>,>=),
|
||||
// regex match (=~) and regex not match (!~)
|
||||
func Text() Language {
|
||||
return text
|
||||
}
|
||||
|
||||
// PropositionalLogic contains base, not(!), and (&&), or (||) and Base.
|
||||
//
|
||||
// Propositional operator expect bool operands.
|
||||
// Called with unfitting input they try to convert the input to bool.
|
||||
// Numbers other than 0 and the strings "TRUE" and "true" are interpreted as true.
|
||||
// 0 and the strings "FALSE" and "false" are interpreted as false.
|
||||
func PropositionalLogic() Language {
|
||||
return propositionalLogic
|
||||
}
|
||||
|
||||
// JSON contains json objects ({string:expression,...})
|
||||
// and json arrays ([expression, ...])
|
||||
func JSON() Language {
|
||||
return ljson
|
||||
}
|
||||
|
||||
// Parentheses contains support for parentheses.
|
||||
func Parentheses() Language {
|
||||
return parentheses
|
||||
}
|
||||
|
||||
// Ident contains support for variables and functions.
|
||||
func Ident() Language {
|
||||
return ident
|
||||
}
|
||||
|
||||
// Base contains equal (==) and not equal (!=), perentheses and general support for variables, constants and functions
|
||||
// It contains true, false, (floating point) number, string ("" or ``) and char ('') constants
|
||||
func Base() Language {
|
||||
return base
|
||||
}
|
||||
|
||||
var full = NewLanguage(arithmetic, bitmask, text, propositionalLogic, ljson,
|
||||
|
||||
InfixOperator("in", inArray),
|
||||
|
||||
InfixShortCircuit("??", func(a interface{}) (interface{}, bool) {
|
||||
v := reflect.ValueOf(a)
|
||||
return a, a != nil && !v.IsZero()
|
||||
}),
|
||||
InfixOperator("??", func(a, b interface{}) (interface{}, error) {
|
||||
if v := reflect.ValueOf(a); a == nil || v.IsZero() {
|
||||
return b, nil
|
||||
}
|
||||
return a, nil
|
||||
}),
|
||||
|
||||
PostfixOperator("?", parseIf),
|
||||
|
||||
Function("date", func(arguments ...interface{}) (interface{}, error) {
|
||||
if len(arguments) != 1 {
|
||||
return nil, fmt.Errorf("date() expects exactly one string argument")
|
||||
}
|
||||
s, ok := arguments[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("date() expects exactly one string argument")
|
||||
}
|
||||
for _, format := range [...]string{
|
||||
time.ANSIC,
|
||||
time.UnixDate,
|
||||
time.RubyDate,
|
||||
time.Kitchen,
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
"2006-01-02", // RFC 3339
|
||||
"2006-01-02 15:04", // RFC 3339 with minutes
|
||||
"2006-01-02 15:04:05", // RFC 3339 with seconds
|
||||
"2006-01-02 15:04:05-07:00", // RFC 3339 with seconds and timezone
|
||||
"2006-01-02T15Z0700", // ISO8601 with hour
|
||||
"2006-01-02T15:04Z0700", // ISO8601 with minutes
|
||||
"2006-01-02T15:04:05Z0700", // ISO8601 with seconds
|
||||
"2006-01-02T15:04:05.999999999Z0700", // ISO8601 with nanoseconds
|
||||
} {
|
||||
ret, err := time.ParseInLocation(format, s, time.Local)
|
||||
if err == nil {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("date() could not parse %s", s)
|
||||
}),
|
||||
)
|
||||
|
||||
var ljson = NewLanguage(
|
||||
PrefixExtension('[', parseJSONArray),
|
||||
PrefixExtension('{', parseJSONObject),
|
||||
)
|
||||
|
||||
var arithmetic = NewLanguage(
|
||||
InfixNumberOperator("+", func(a, b float64) (interface{}, error) { return a + b, nil }),
|
||||
InfixNumberOperator("-", func(a, b float64) (interface{}, error) { return a - b, nil }),
|
||||
InfixNumberOperator("*", func(a, b float64) (interface{}, error) { return a * b, nil }),
|
||||
InfixNumberOperator("/", func(a, b float64) (interface{}, error) { return a / b, nil }),
|
||||
InfixNumberOperator("%", func(a, b float64) (interface{}, error) { return math.Mod(a, b), nil }),
|
||||
InfixNumberOperator("**", func(a, b float64) (interface{}, error) { return math.Pow(a, b), nil }),
|
||||
|
||||
InfixNumberOperator(">", func(a, b float64) (interface{}, error) { return a > b, nil }),
|
||||
InfixNumberOperator(">=", func(a, b float64) (interface{}, error) { return a >= b, nil }),
|
||||
InfixNumberOperator("<", func(a, b float64) (interface{}, error) { return a < b, nil }),
|
||||
InfixNumberOperator("<=", func(a, b float64) (interface{}, error) { return a <= b, nil }),
|
||||
|
||||
InfixNumberOperator("==", func(a, b float64) (interface{}, error) { return a == b, nil }),
|
||||
InfixNumberOperator("!=", func(a, b float64) (interface{}, error) { return a != b, nil }),
|
||||
|
||||
base,
|
||||
)
|
||||
|
||||
var decimalArithmetic = NewLanguage(
|
||||
InfixDecimalOperator("+", func(a, b decimal.Decimal) (interface{}, error) { return a.Add(b), nil }),
|
||||
InfixDecimalOperator("-", func(a, b decimal.Decimal) (interface{}, error) { return a.Sub(b), nil }),
|
||||
InfixDecimalOperator("*", func(a, b decimal.Decimal) (interface{}, error) { return a.Mul(b), nil }),
|
||||
InfixDecimalOperator("/", func(a, b decimal.Decimal) (interface{}, error) { return a.Div(b), nil }),
|
||||
InfixDecimalOperator("%", func(a, b decimal.Decimal) (interface{}, error) { return a.Mod(b), nil }),
|
||||
InfixDecimalOperator("**", func(a, b decimal.Decimal) (interface{}, error) { return a.Pow(b), nil }),
|
||||
|
||||
InfixDecimalOperator(">", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThan(b), nil }),
|
||||
InfixDecimalOperator(">=", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThanOrEqual(b), nil }),
|
||||
InfixDecimalOperator("<", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThan(b), nil }),
|
||||
InfixDecimalOperator("<=", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThanOrEqual(b), nil }),
|
||||
|
||||
InfixDecimalOperator("==", func(a, b decimal.Decimal) (interface{}, error) { return a.Equal(b), nil }),
|
||||
InfixDecimalOperator("!=", func(a, b decimal.Decimal) (interface{}, error) { return !a.Equal(b), nil }),
|
||||
base,
|
||||
//Base is before these overrides so that the Base options are overridden
|
||||
PrefixExtension(scanner.Int, parseDecimal),
|
||||
PrefixExtension(scanner.Float, parseDecimal),
|
||||
PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
i, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
|
||||
}
|
||||
return decimal.NewFromFloat(i).Neg(), nil
|
||||
}),
|
||||
)
|
||||
|
||||
var bitmask = NewLanguage(
|
||||
InfixNumberOperator("^", func(a, b float64) (interface{}, error) { return float64(int64(a) ^ int64(b)), nil }),
|
||||
InfixNumberOperator("&", func(a, b float64) (interface{}, error) { return float64(int64(a) & int64(b)), nil }),
|
||||
InfixNumberOperator("|", func(a, b float64) (interface{}, error) { return float64(int64(a) | int64(b)), nil }),
|
||||
InfixNumberOperator("<<", func(a, b float64) (interface{}, error) { return float64(int64(a) << uint64(b)), nil }),
|
||||
InfixNumberOperator(">>", func(a, b float64) (interface{}, error) { return float64(int64(a) >> uint64(b)), nil }),
|
||||
|
||||
PrefixOperator("~", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
i, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %T expected number", v)
|
||||
}
|
||||
return float64(^int64(i)), nil
|
||||
}),
|
||||
)
|
||||
|
||||
var text = NewLanguage(
|
||||
InfixTextOperator("+", func(a, b string) (interface{}, error) { return fmt.Sprintf("%v%v", a, b), nil }),
|
||||
|
||||
InfixTextOperator("<", func(a, b string) (interface{}, error) { return a < b, nil }),
|
||||
InfixTextOperator("<=", func(a, b string) (interface{}, error) { return a <= b, nil }),
|
||||
InfixTextOperator(">", func(a, b string) (interface{}, error) { return a > b, nil }),
|
||||
InfixTextOperator(">=", func(a, b string) (interface{}, error) { return a >= b, nil }),
|
||||
|
||||
InfixEvalOperator("=~", regEx),
|
||||
InfixEvalOperator("!~", notRegEx),
|
||||
base,
|
||||
)
|
||||
|
||||
var propositionalLogic = NewLanguage(
|
||||
PrefixOperator("!", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
b, ok := convertToBool(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %T expected bool", v)
|
||||
}
|
||||
return !b, nil
|
||||
}),
|
||||
|
||||
InfixShortCircuit("&&", func(a interface{}) (interface{}, bool) { return false, a == false }),
|
||||
InfixBoolOperator("&&", func(a, b bool) (interface{}, error) { return a && b, nil }),
|
||||
InfixShortCircuit("||", func(a interface{}) (interface{}, bool) { return true, a == true }),
|
||||
InfixBoolOperator("||", func(a, b bool) (interface{}, error) { return a || b, nil }),
|
||||
|
||||
InfixBoolOperator("==", func(a, b bool) (interface{}, error) { return a == b, nil }),
|
||||
InfixBoolOperator("!=", func(a, b bool) (interface{}, error) { return a != b, nil }),
|
||||
|
||||
base,
|
||||
)
|
||||
|
||||
var parentheses = NewLanguage(
|
||||
PrefixExtension('(', parseParentheses),
|
||||
)
|
||||
|
||||
var ident = NewLanguage(
|
||||
PrefixMetaPrefix(scanner.Ident, parseIdent),
|
||||
)
|
||||
|
||||
var base = NewLanguage(
|
||||
PrefixExtension(scanner.Int, parseNumber),
|
||||
PrefixExtension(scanner.Float, parseNumber),
|
||||
PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
i, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
|
||||
}
|
||||
return -i, nil
|
||||
}),
|
||||
|
||||
PrefixExtension(scanner.String, parseString),
|
||||
PrefixExtension(scanner.Char, parseString),
|
||||
PrefixExtension(scanner.RawString, parseString),
|
||||
|
||||
Constant("true", true),
|
||||
Constant("false", false),
|
||||
|
||||
InfixOperator("==", func(a, b interface{}) (interface{}, error) { return reflect.DeepEqual(a, b), nil }),
|
||||
InfixOperator("!=", func(a, b interface{}) (interface{}, error) { return !reflect.DeepEqual(a, b), nil }),
|
||||
parentheses,
|
||||
|
||||
Precedence("??", 0),
|
||||
|
||||
Precedence("||", 20),
|
||||
Precedence("&&", 21),
|
||||
|
||||
Precedence("==", 40),
|
||||
Precedence("!=", 40),
|
||||
Precedence(">", 40),
|
||||
Precedence(">=", 40),
|
||||
Precedence("<", 40),
|
||||
Precedence("<=", 40),
|
||||
Precedence("=~", 40),
|
||||
Precedence("!~", 40),
|
||||
Precedence("in", 40),
|
||||
|
||||
Precedence("^", 60),
|
||||
Precedence("&", 60),
|
||||
Precedence("|", 60),
|
||||
|
||||
Precedence("<<", 90),
|
||||
Precedence(">>", 90),
|
||||
|
||||
Precedence("+", 120),
|
||||
Precedence("-", 120),
|
||||
|
||||
Precedence("*", 150),
|
||||
Precedence("/", 150),
|
||||
Precedence("%", 150),
|
||||
|
||||
Precedence("**", 200),
|
||||
|
||||
ident,
|
||||
)
|
||||
281
extra/server-discovery/vendor/github.com/PaesslerAG/gval/language.go
generated
vendored
Normal file
281
extra/server-discovery/vendor/github.com/PaesslerAG/gval/language.go
generated
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// Language is an expression language
|
||||
type Language struct {
|
||||
prefixes map[interface{}]extension
|
||||
operators map[string]operator
|
||||
operatorSymbols map[rune]struct{}
|
||||
init extension
|
||||
def extension
|
||||
selector func(Evaluables) Evaluable
|
||||
}
|
||||
|
||||
// NewLanguage returns the union of given Languages as new Language.
|
||||
func NewLanguage(bases ...Language) Language {
|
||||
l := newLanguage()
|
||||
for _, base := range bases {
|
||||
for i, e := range base.prefixes {
|
||||
l.prefixes[i] = e
|
||||
}
|
||||
for i, e := range base.operators {
|
||||
l.operators[i] = e.merge(l.operators[i])
|
||||
l.operators[i].initiate(i)
|
||||
}
|
||||
for i := range base.operatorSymbols {
|
||||
l.operatorSymbols[i] = struct{}{}
|
||||
}
|
||||
if base.init != nil {
|
||||
l.init = base.init
|
||||
}
|
||||
if base.def != nil {
|
||||
l.def = base.def
|
||||
}
|
||||
if base.selector != nil {
|
||||
l.selector = base.selector
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func newLanguage() Language {
|
||||
return Language{
|
||||
prefixes: map[interface{}]extension{},
|
||||
operators: map[string]operator{},
|
||||
operatorSymbols: map[rune]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewEvaluable returns an Evaluable for given expression in the specified language
|
||||
func (l Language) NewEvaluable(expression string) (Evaluable, error) {
|
||||
return l.NewEvaluableWithContext(context.Background(), expression)
|
||||
}
|
||||
|
||||
// NewEvaluableWithContext returns an Evaluable for given expression in the specified language using context
|
||||
func (l Language) NewEvaluableWithContext(c context.Context, expression string) (Evaluable, error) {
|
||||
p := newParser(expression, l)
|
||||
|
||||
eval, err := p.parse(c)
|
||||
if err == nil && p.isCamouflaged() && p.lastScan != scanner.EOF {
|
||||
err = p.camouflage
|
||||
}
|
||||
if err != nil {
|
||||
pos := p.scanner.Pos()
|
||||
return nil, fmt.Errorf("parsing error: %s - %d:%d %s", p.scanner.Position, pos.Line, pos.Column, err)
|
||||
}
|
||||
|
||||
return eval, nil
|
||||
}
|
||||
|
||||
// Evaluate given parameter with given expression
|
||||
func (l Language) Evaluate(expression string, parameter interface{}) (interface{}, error) {
|
||||
return l.EvaluateWithContext(context.Background(), expression, parameter)
|
||||
}
|
||||
|
||||
// Evaluate given parameter with given expression using context
|
||||
func (l Language) EvaluateWithContext(c context.Context, expression string, parameter interface{}) (interface{}, error) {
|
||||
eval, err := l.NewEvaluableWithContext(c, expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := eval(c, parameter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not evaluate %s: %v", expression, err)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Function returns a Language with given function.
|
||||
// Function has no conversion for input types.
|
||||
//
|
||||
// If the function returns an error it must be the last return parameter.
|
||||
//
|
||||
// If the function has (without the error) more then one return parameter,
|
||||
// it returns them as []interface{}.
|
||||
func Function(name string, function interface{}) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[name] = func(c context.Context, p *Parser) (eval Evaluable, err error) {
|
||||
args := []Evaluable{}
|
||||
scan := p.Scan()
|
||||
switch scan {
|
||||
case '(':
|
||||
args, err = p.parseArguments(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
p.Camouflage("function call", '(')
|
||||
}
|
||||
return p.callFunc(toFunc(function), args...), nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Constant returns a Language with given constant
|
||||
func Constant(name string, value interface{}) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[l.makePrefixKey(name)] = func(c context.Context, p *Parser) (eval Evaluable, err error) {
|
||||
return p.Const(value), nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// PrefixExtension extends a Language
|
||||
func PrefixExtension(r rune, ext func(context.Context, *Parser) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[r] = ext
|
||||
return l
|
||||
}
|
||||
|
||||
// Init is a language that does no parsing, but invokes the given function when
|
||||
// parsing starts. It is incumbent upon the function to call ParseExpression to
|
||||
// continue parsing.
|
||||
//
|
||||
// This function can be used to customize the parser settings, such as
|
||||
// whitespace or ident behavior.
|
||||
func Init(ext func(context.Context, *Parser) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.init = ext
|
||||
return l
|
||||
}
|
||||
|
||||
// DefaultExtension is a language that runs the given function if no other
|
||||
// prefix matches.
|
||||
func DefaultExtension(ext func(context.Context, *Parser) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.def = ext
|
||||
return l
|
||||
}
|
||||
|
||||
// PrefixMetaPrefix chooses a Prefix to be executed
|
||||
func PrefixMetaPrefix(r rune, ext func(context.Context, *Parser) (call string, alternative func() (Evaluable, error), err error)) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[r] = func(c context.Context, p *Parser) (Evaluable, error) {
|
||||
call, alternative, err := ext(c, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if prefix, ok := p.prefixes[l.makePrefixKey(call)]; ok {
|
||||
return prefix(c, p)
|
||||
}
|
||||
return alternative()
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
//PrefixOperator returns a Language with given prefix
|
||||
func PrefixOperator(name string, e Evaluable) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[l.makePrefixKey(name)] = func(c context.Context, p *Parser) (Evaluable, error) {
|
||||
eval, err := p.ParseNextExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefix := func(c context.Context, v interface{}) (interface{}, error) {
|
||||
a, err := eval(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e(c, a)
|
||||
}
|
||||
if eval.IsConst() {
|
||||
v, err := prefix(c, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefix = p.Const(v)
|
||||
}
|
||||
return prefix, nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// PostfixOperator extends a Language.
|
||||
func PostfixOperator(name string, ext func(context.Context, *Parser, Evaluable) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.operators[l.makeInfixKey(name)] = postfix{
|
||||
f: func(c context.Context, p *Parser, eval Evaluable, pre operatorPrecedence) (Evaluable, error) {
|
||||
return ext(c, p, eval)
|
||||
},
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// InfixOperator for two arbitrary values.
|
||||
func InfixOperator(name string, f func(a, b interface{}) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{arbitrary: f})
|
||||
}
|
||||
|
||||
// InfixShortCircuit operator is called after the left operand is evaluated.
|
||||
func InfixShortCircuit(name string, f func(a interface{}) (interface{}, bool)) Language {
|
||||
return newLanguageOperator(name, &infix{shortCircuit: f})
|
||||
}
|
||||
|
||||
// InfixTextOperator for two text values.
|
||||
func InfixTextOperator(name string, f func(a, b string) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{text: f})
|
||||
}
|
||||
|
||||
// InfixNumberOperator for two number values.
|
||||
func InfixNumberOperator(name string, f func(a, b float64) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{number: f})
|
||||
}
|
||||
|
||||
// InfixDecimalOperator for two decimal values.
|
||||
func InfixDecimalOperator(name string, f func(a, b decimal.Decimal) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{decimal: f})
|
||||
}
|
||||
|
||||
// InfixBoolOperator for two bool values.
|
||||
func InfixBoolOperator(name string, f func(a, b bool) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{boolean: f})
|
||||
}
|
||||
|
||||
// Precedence of operator. The Operator with higher operatorPrecedence is evaluated first.
|
||||
func Precedence(name string, operatorPrecendence uint8) Language {
|
||||
return newLanguageOperator(name, operatorPrecedence(operatorPrecendence))
|
||||
}
|
||||
|
||||
// InfixEvalOperator operates on the raw operands.
|
||||
// Therefore it cannot be combined with operators for other operand types.
|
||||
func InfixEvalOperator(name string, f func(a, b Evaluable) (Evaluable, error)) Language {
|
||||
return newLanguageOperator(name, directInfix{infixBuilder: f})
|
||||
}
|
||||
|
||||
func newLanguageOperator(name string, op operator) Language {
|
||||
op.initiate(name)
|
||||
l := newLanguage()
|
||||
l.operators[l.makeInfixKey(name)] = op
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Language) makePrefixKey(key string) interface{} {
|
||||
runes := []rune(key)
|
||||
if len(runes) == 1 && !unicode.IsLetter(runes[0]) {
|
||||
return runes[0]
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func (l *Language) makeInfixKey(key string) string {
|
||||
for _, r := range key {
|
||||
l.operatorSymbols[r] = struct{}{}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// VariableSelector returns a Language which uses given variable selector.
|
||||
// It must be combined with a Language that uses the vatiable selector. E.g. gval.Base().
|
||||
func VariableSelector(selector func(path Evaluables) Evaluable) Language {
|
||||
l := newLanguage()
|
||||
l.selector = selector
|
||||
return l
|
||||
}
|
||||
391
extra/server-discovery/vendor/github.com/PaesslerAG/gval/operator.go
generated
vendored
Normal file
391
extra/server-discovery/vendor/github.com/PaesslerAG/gval/operator.go
generated
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type stage struct {
|
||||
Evaluable
|
||||
infixBuilder
|
||||
operatorPrecedence
|
||||
}
|
||||
|
||||
type stageStack []stage //operatorPrecedence in stacktStage is continuously, monotone ascending
|
||||
|
||||
func (s *stageStack) push(b stage) error {
|
||||
for len(*s) > 0 && s.peek().operatorPrecedence >= b.operatorPrecedence {
|
||||
a := s.pop()
|
||||
eval, err := a.infixBuilder(a.Evaluable, b.Evaluable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.IsConst() && b.IsConst() {
|
||||
v, err := eval(nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Evaluable = constant(v)
|
||||
continue
|
||||
}
|
||||
b.Evaluable = eval
|
||||
}
|
||||
*s = append(*s, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stageStack) peek() stage {
|
||||
return (*s)[len(*s)-1]
|
||||
}
|
||||
|
||||
func (s *stageStack) pop() stage {
|
||||
a := s.peek()
|
||||
(*s) = (*s)[:len(*s)-1]
|
||||
return a
|
||||
}
|
||||
|
||||
type infixBuilder func(a, b Evaluable) (Evaluable, error)
|
||||
|
||||
func (l Language) isSymbolOperation(r rune) bool {
|
||||
_, in := l.operatorSymbols[r]
|
||||
return in
|
||||
}
|
||||
|
||||
func (l Language) isOperatorPrefix(op string) bool {
|
||||
for k := range l.operators {
|
||||
if strings.HasPrefix(k, op) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (op *infix) initiate(name string) {
|
||||
f := func(a, b interface{}) (interface{}, error) {
|
||||
return nil, fmt.Errorf("invalid operation (%T) %s (%T)", a, name, b)
|
||||
}
|
||||
if op.arbitrary != nil {
|
||||
f = op.arbitrary
|
||||
}
|
||||
for _, typeConvertion := range []bool{true, false} {
|
||||
if op.text != nil && (!typeConvertion || op.arbitrary == nil) {
|
||||
f = getStringOpFunc(op.text, f, typeConvertion)
|
||||
}
|
||||
if op.boolean != nil {
|
||||
f = getBoolOpFunc(op.boolean, f, typeConvertion)
|
||||
}
|
||||
if op.number != nil {
|
||||
f = getFloatOpFunc(op.number, f, typeConvertion)
|
||||
}
|
||||
if op.decimal != nil {
|
||||
f = getDecimalOpFunc(op.decimal, f, typeConvertion)
|
||||
}
|
||||
}
|
||||
if op.shortCircuit == nil {
|
||||
op.builder = func(a, b Evaluable) (Evaluable, error) {
|
||||
return func(c context.Context, x interface{}) (interface{}, error) {
|
||||
a, err := a(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := b(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f(a, b)
|
||||
}, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
shortF := op.shortCircuit
|
||||
op.builder = func(a, b Evaluable) (Evaluable, error) {
|
||||
return func(c context.Context, x interface{}) (interface{}, error) {
|
||||
a, err := a(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r, ok := shortF(a); ok {
|
||||
return r, nil
|
||||
}
|
||||
b, err := b(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f(a, b)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type opFunc func(a, b interface{}) (interface{}, error)
|
||||
|
||||
func getStringOpFunc(s func(a, b string) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
if a != nil && b != nil {
|
||||
return s(fmt.Sprintf("%v", a), fmt.Sprintf("%v", b))
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
s1, k := a.(string)
|
||||
s2, l := b.(string)
|
||||
if k && l {
|
||||
return s(s1, s2)
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
func convertToBool(o interface{}) (bool, bool) {
|
||||
if b, ok := o.(bool); ok {
|
||||
return b, true
|
||||
}
|
||||
v := reflect.ValueOf(o)
|
||||
for o != nil && v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
if !v.IsValid() {
|
||||
return false, false
|
||||
}
|
||||
o = v.Interface()
|
||||
}
|
||||
if o == false || o == nil || o == "false" || o == "FALSE" {
|
||||
return false, true
|
||||
}
|
||||
if o == true || o == "true" || o == "TRUE" {
|
||||
return true, true
|
||||
}
|
||||
if f, ok := convertToFloat(o); ok {
|
||||
return f != 0., true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
func getBoolOpFunc(o func(a, b bool) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := convertToBool(a)
|
||||
y, l := convertToBool(b)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := a.(bool)
|
||||
y, l := b.(bool)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
func convertToFloat(o interface{}) (float64, bool) {
|
||||
if i, ok := o.(float64); ok {
|
||||
return i, true
|
||||
}
|
||||
v := reflect.ValueOf(o)
|
||||
for o != nil && v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
if !v.IsValid() {
|
||||
return 0, false
|
||||
}
|
||||
o = v.Interface()
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(v.Int()), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float64(v.Uint()), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), true
|
||||
}
|
||||
if s, ok := o.(string); ok {
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err == nil {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
func getFloatOpFunc(o func(a, b float64) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := convertToFloat(a)
|
||||
y, l := convertToFloat(b)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := a.(float64)
|
||||
y, l := b.(float64)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
func convertToDecimal(o interface{}) (decimal.Decimal, bool) {
|
||||
if i, ok := o.(decimal.Decimal); ok {
|
||||
return i, true
|
||||
}
|
||||
if i, ok := o.(float64); ok {
|
||||
return decimal.NewFromFloat(i), true
|
||||
}
|
||||
v := reflect.ValueOf(o)
|
||||
for o != nil && v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
if !v.IsValid() {
|
||||
return decimal.Zero, false
|
||||
}
|
||||
o = v.Interface()
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return decimal.NewFromInt(v.Int()), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return decimal.NewFromFloat(float64(v.Uint())), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return decimal.NewFromFloat(v.Float()), true
|
||||
}
|
||||
if s, ok := o.(string); ok {
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err == nil {
|
||||
return decimal.NewFromFloat(f), true
|
||||
}
|
||||
}
|
||||
return decimal.Zero, false
|
||||
}
|
||||
func getDecimalOpFunc(o func(a, b decimal.Decimal) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := convertToDecimal(a)
|
||||
y, l := convertToDecimal(b)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := a.(decimal.Decimal)
|
||||
y, l := b.(decimal.Decimal)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
type operator interface {
|
||||
merge(operator) operator
|
||||
precedence() operatorPrecedence
|
||||
initiate(name string)
|
||||
}
|
||||
|
||||
type operatorPrecedence uint8
|
||||
|
||||
func (pre operatorPrecedence) merge(op operator) operator {
|
||||
if op, ok := op.(operatorPrecedence); ok {
|
||||
if op > pre {
|
||||
return op
|
||||
}
|
||||
return pre
|
||||
}
|
||||
if op == nil {
|
||||
return pre
|
||||
}
|
||||
return op.merge(pre)
|
||||
}
|
||||
|
||||
func (pre operatorPrecedence) precedence() operatorPrecedence {
|
||||
return pre
|
||||
}
|
||||
|
||||
func (pre operatorPrecedence) initiate(name string) {}
|
||||
|
||||
type infix struct {
|
||||
operatorPrecedence
|
||||
number func(a, b float64) (interface{}, error)
|
||||
decimal func(a, b decimal.Decimal) (interface{}, error)
|
||||
boolean func(a, b bool) (interface{}, error)
|
||||
text func(a, b string) (interface{}, error)
|
||||
arbitrary func(a, b interface{}) (interface{}, error)
|
||||
shortCircuit func(a interface{}) (interface{}, bool)
|
||||
builder infixBuilder
|
||||
}
|
||||
|
||||
func (op infix) merge(op2 operator) operator {
|
||||
switch op2 := op2.(type) {
|
||||
case *infix:
|
||||
if op.number == nil {
|
||||
op.number = op2.number
|
||||
}
|
||||
if op.decimal == nil {
|
||||
op.decimal = op2.decimal
|
||||
}
|
||||
if op.boolean == nil {
|
||||
op.boolean = op2.boolean
|
||||
}
|
||||
if op.text == nil {
|
||||
op.text = op2.text
|
||||
}
|
||||
if op.arbitrary == nil {
|
||||
op.arbitrary = op2.arbitrary
|
||||
}
|
||||
if op.shortCircuit == nil {
|
||||
op.shortCircuit = op2.shortCircuit
|
||||
}
|
||||
}
|
||||
if op2 != nil && op2.precedence() > op.operatorPrecedence {
|
||||
op.operatorPrecedence = op2.precedence()
|
||||
}
|
||||
return &op
|
||||
}
|
||||
|
||||
type directInfix struct {
|
||||
operatorPrecedence
|
||||
infixBuilder
|
||||
}
|
||||
|
||||
func (op directInfix) merge(op2 operator) operator {
|
||||
switch op2 := op2.(type) {
|
||||
case operatorPrecedence:
|
||||
op.operatorPrecedence = op2
|
||||
}
|
||||
if op2 != nil && op2.precedence() > op.operatorPrecedence {
|
||||
op.operatorPrecedence = op2.precedence()
|
||||
}
|
||||
return op
|
||||
}
|
||||
|
||||
type extension func(context.Context, *Parser) (Evaluable, error)
|
||||
|
||||
type postfix struct {
|
||||
operatorPrecedence
|
||||
f func(context.Context, *Parser, Evaluable, operatorPrecedence) (Evaluable, error)
|
||||
}
|
||||
|
||||
func (op postfix) merge(op2 operator) operator {
|
||||
switch op2 := op2.(type) {
|
||||
case postfix:
|
||||
if op2.f != nil {
|
||||
op.f = op2.f
|
||||
}
|
||||
}
|
||||
if op2 != nil && op2.precedence() > op.operatorPrecedence {
|
||||
op.operatorPrecedence = op2.precedence()
|
||||
}
|
||||
return op
|
||||
}
|
||||
347
extra/server-discovery/vendor/github.com/PaesslerAG/gval/parse.go
generated
vendored
Normal file
347
extra/server-discovery/vendor/github.com/PaesslerAG/gval/parse.go
generated
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/scanner"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
//ParseExpression scans an expression into an Evaluable.
|
||||
func (p *Parser) ParseExpression(c context.Context) (eval Evaluable, err error) {
|
||||
stack := stageStack{}
|
||||
for {
|
||||
eval, err = p.ParseNextExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stage, err := p.parseOperator(c, &stack, eval); err != nil {
|
||||
return nil, err
|
||||
} else if err = stack.push(stage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stack.peek().infixBuilder == nil {
|
||||
return stack.pop().Evaluable, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ParseNextExpression scans the expression ignoring following operators
|
||||
func (p *Parser) ParseNextExpression(c context.Context) (eval Evaluable, err error) {
|
||||
scan := p.Scan()
|
||||
ex, ok := p.prefixes[scan]
|
||||
if !ok {
|
||||
if scan != scanner.EOF && p.def != nil {
|
||||
return p.def(c, p)
|
||||
}
|
||||
return nil, p.Expected("extensions")
|
||||
}
|
||||
return ex(c, p)
|
||||
}
|
||||
|
||||
// ParseSublanguage sets the next language for this parser to parse and calls
|
||||
// its initialization function, usually ParseExpression.
|
||||
func (p *Parser) ParseSublanguage(c context.Context, l Language) (Evaluable, error) {
|
||||
if p.isCamouflaged() {
|
||||
panic("can not ParseSublanguage() on camouflaged Parser")
|
||||
}
|
||||
curLang := p.Language
|
||||
curWhitespace := p.scanner.Whitespace
|
||||
curMode := p.scanner.Mode
|
||||
curIsIdentRune := p.scanner.IsIdentRune
|
||||
|
||||
p.Language = l
|
||||
p.resetScannerProperties()
|
||||
|
||||
defer func() {
|
||||
p.Language = curLang
|
||||
p.scanner.Whitespace = curWhitespace
|
||||
p.scanner.Mode = curMode
|
||||
p.scanner.IsIdentRune = curIsIdentRune
|
||||
}()
|
||||
|
||||
return p.parse(c)
|
||||
}
|
||||
|
||||
func (p *Parser) parse(c context.Context) (Evaluable, error) {
|
||||
if p.init != nil {
|
||||
return p.init(c, p)
|
||||
}
|
||||
|
||||
return p.ParseExpression(c)
|
||||
}
|
||||
|
||||
func parseString(c context.Context, p *Parser) (Evaluable, error) {
|
||||
s, err := strconv.Unquote(p.TokenText())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse string: %s", err)
|
||||
}
|
||||
return p.Const(s), nil
|
||||
}
|
||||
|
||||
func parseNumber(c context.Context, p *Parser) (Evaluable, error) {
|
||||
n, err := strconv.ParseFloat(p.TokenText(), 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Const(n), nil
|
||||
}
|
||||
|
||||
func parseDecimal(c context.Context, p *Parser) (Evaluable, error) {
|
||||
n, err := strconv.ParseFloat(p.TokenText(), 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Const(decimal.NewFromFloat(n)), nil
|
||||
}
|
||||
|
||||
func parseParentheses(c context.Context, p *Parser) (Evaluable, error) {
|
||||
eval, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p.Scan() {
|
||||
case ')':
|
||||
return eval, nil
|
||||
default:
|
||||
return nil, p.Expected("parentheses", ')')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseOperator(c context.Context, stack *stageStack, eval Evaluable) (st stage, err error) {
|
||||
for {
|
||||
scan := p.Scan()
|
||||
op := p.TokenText()
|
||||
mustOp := false
|
||||
if p.isSymbolOperation(scan) {
|
||||
scan = p.Peek()
|
||||
for p.isSymbolOperation(scan) && p.isOperatorPrefix(op+string(scan)) {
|
||||
mustOp = true
|
||||
op += string(scan)
|
||||
p.Next()
|
||||
scan = p.Peek()
|
||||
}
|
||||
} else if scan != scanner.Ident {
|
||||
p.Camouflage("operator")
|
||||
return stage{Evaluable: eval}, nil
|
||||
}
|
||||
switch operator := p.operators[op].(type) {
|
||||
case *infix:
|
||||
return stage{
|
||||
Evaluable: eval,
|
||||
infixBuilder: operator.builder,
|
||||
operatorPrecedence: operator.operatorPrecedence,
|
||||
}, nil
|
||||
case directInfix:
|
||||
return stage{
|
||||
Evaluable: eval,
|
||||
infixBuilder: operator.infixBuilder,
|
||||
operatorPrecedence: operator.operatorPrecedence,
|
||||
}, nil
|
||||
case postfix:
|
||||
if err = stack.push(stage{
|
||||
operatorPrecedence: operator.operatorPrecedence,
|
||||
Evaluable: eval,
|
||||
}); err != nil {
|
||||
return stage{}, err
|
||||
}
|
||||
eval, err = operator.f(c, p, stack.pop().Evaluable, operator.operatorPrecedence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !mustOp {
|
||||
p.Camouflage("operator")
|
||||
return stage{Evaluable: eval}, nil
|
||||
}
|
||||
return stage{}, fmt.Errorf("unknown operator %s", op)
|
||||
}
|
||||
}
|
||||
|
||||
func parseIdent(c context.Context, p *Parser) (call string, alternative func() (Evaluable, error), err error) {
|
||||
token := p.TokenText()
|
||||
return token,
|
||||
func() (Evaluable, error) {
|
||||
fullname := token
|
||||
|
||||
keys := []Evaluable{p.Const(token)}
|
||||
for {
|
||||
scan := p.Scan()
|
||||
switch scan {
|
||||
case '.':
|
||||
scan = p.Scan()
|
||||
switch scan {
|
||||
case scanner.Ident:
|
||||
token = p.TokenText()
|
||||
keys = append(keys, p.Const(token))
|
||||
default:
|
||||
return nil, p.Expected("field", scanner.Ident)
|
||||
}
|
||||
case '(':
|
||||
args, err := p.parseArguments(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.callEvaluable(fullname, p.Var(keys...), args...), nil
|
||||
case '[':
|
||||
key, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p.Scan() {
|
||||
case ']':
|
||||
keys = append(keys, key)
|
||||
default:
|
||||
return nil, p.Expected("array key", ']')
|
||||
}
|
||||
default:
|
||||
p.Camouflage("variable", '.', '(', '[')
|
||||
return p.Var(keys...), nil
|
||||
}
|
||||
}
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (p *Parser) parseArguments(c context.Context) (args []Evaluable, err error) {
|
||||
if p.Scan() == ')' {
|
||||
return
|
||||
}
|
||||
p.Camouflage("scan arguments", ')')
|
||||
for {
|
||||
arg, err := p.ParseExpression(c)
|
||||
args = append(args, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p.Scan() {
|
||||
case ')':
|
||||
return args, nil
|
||||
case ',':
|
||||
default:
|
||||
return nil, p.Expected("arguments", ')', ',')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inArray(a, b interface{}) (interface{}, error) {
|
||||
col, ok := b.([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected type []interface{} for in operator but got %T", b)
|
||||
}
|
||||
for _, value := range col {
|
||||
if reflect.DeepEqual(a, value) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func parseIf(c context.Context, p *Parser, e Evaluable) (Evaluable, error) {
|
||||
a, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := p.Const(nil)
|
||||
switch p.Scan() {
|
||||
case ':':
|
||||
b, err = p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case scanner.EOF:
|
||||
default:
|
||||
return nil, p.Expected("<> ? <> : <>", ':', scanner.EOF)
|
||||
}
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
x, err := e(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if valX := reflect.ValueOf(x); x == nil || valX.IsZero() {
|
||||
return b(c, v)
|
||||
}
|
||||
return a(c, v)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseJSONArray(c context.Context, p *Parser) (Evaluable, error) {
|
||||
evals := []Evaluable{}
|
||||
for {
|
||||
switch p.Scan() {
|
||||
default:
|
||||
p.Camouflage("array", ',', ']')
|
||||
eval, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evals = append(evals, eval)
|
||||
case ',':
|
||||
case ']':
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
vs := make([]interface{}, len(evals))
|
||||
for i, e := range evals {
|
||||
eval, err := e(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs[i] = eval
|
||||
}
|
||||
|
||||
return vs, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseJSONObject(c context.Context, p *Parser) (Evaluable, error) {
|
||||
type kv struct {
|
||||
key Evaluable
|
||||
value Evaluable
|
||||
}
|
||||
evals := []kv{}
|
||||
for {
|
||||
switch p.Scan() {
|
||||
default:
|
||||
p.Camouflage("object", ',', '}')
|
||||
key, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.Scan() != ':' {
|
||||
if err != nil {
|
||||
return nil, p.Expected("object", ':')
|
||||
}
|
||||
}
|
||||
value, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evals = append(evals, kv{key, value})
|
||||
case ',':
|
||||
case '}':
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
vs := map[string]interface{}{}
|
||||
for _, e := range evals {
|
||||
value, err := e.value(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := e.key.EvalString(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs[key] = value
|
||||
}
|
||||
return vs, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
147
extra/server-discovery/vendor/github.com/PaesslerAG/gval/parser.go
generated
vendored
Normal file
147
extra/server-discovery/vendor/github.com/PaesslerAG/gval/parser.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
//Parser parses expressions in a Language into an Evaluable
|
||||
type Parser struct {
|
||||
scanner scanner.Scanner
|
||||
Language
|
||||
lastScan rune
|
||||
camouflage error
|
||||
}
|
||||
|
||||
func newParser(expression string, l Language) *Parser {
|
||||
sc := scanner.Scanner{}
|
||||
sc.Init(strings.NewReader(expression))
|
||||
sc.Error = func(*scanner.Scanner, string) {}
|
||||
sc.Filename = expression + "\t"
|
||||
p := &Parser{scanner: sc, Language: l}
|
||||
p.resetScannerProperties()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Parser) resetScannerProperties() {
|
||||
p.scanner.Whitespace = scanner.GoWhitespace
|
||||
p.scanner.Mode = scanner.GoTokens
|
||||
p.scanner.IsIdentRune = func(r rune, pos int) bool {
|
||||
return unicode.IsLetter(r) || r == '_' || (pos > 0 && unicode.IsDigit(r))
|
||||
}
|
||||
}
|
||||
|
||||
// SetWhitespace sets the behavior of the whitespace matcher. The given
|
||||
// characters must be less than or equal to 0x20 (' ').
|
||||
func (p *Parser) SetWhitespace(chars ...rune) {
|
||||
var mask uint64
|
||||
for _, char := range chars {
|
||||
mask |= 1 << char
|
||||
}
|
||||
|
||||
p.scanner.Whitespace = mask
|
||||
}
|
||||
|
||||
// SetMode sets the tokens that the underlying scanner will match.
|
||||
func (p *Parser) SetMode(mode uint) {
|
||||
p.scanner.Mode = mode
|
||||
}
|
||||
|
||||
// SetIsIdentRuneFunc sets the function that matches ident characters in the
|
||||
// underlying scanner.
|
||||
func (p *Parser) SetIsIdentRuneFunc(fn func(ch rune, i int) bool) {
|
||||
p.scanner.IsIdentRune = fn
|
||||
}
|
||||
|
||||
// Scan reads the next token or Unicode character from source and returns it.
|
||||
// It only recognizes tokens t for which the respective Mode bit (1<<-t) is set.
|
||||
// It returns scanner.EOF at the end of the source.
|
||||
func (p *Parser) Scan() rune {
|
||||
if p.isCamouflaged() {
|
||||
p.camouflage = nil
|
||||
return p.lastScan
|
||||
}
|
||||
p.camouflage = nil
|
||||
p.lastScan = p.scanner.Scan()
|
||||
return p.lastScan
|
||||
}
|
||||
|
||||
func (p *Parser) isCamouflaged() bool {
|
||||
return p.camouflage != nil && p.camouflage != errCamouflageAfterNext
|
||||
}
|
||||
|
||||
// Camouflage rewind the last Scan(). The Parser holds the camouflage error until
|
||||
// the next Scan()
|
||||
// Do not call Rewind() on a camouflaged Parser
|
||||
func (p *Parser) Camouflage(unit string, expected ...rune) {
|
||||
if p.isCamouflaged() {
|
||||
panic(fmt.Errorf("can only Camouflage() after Scan(): %v", p.camouflage))
|
||||
}
|
||||
p.camouflage = p.Expected(unit, expected...)
|
||||
}
|
||||
|
||||
// Peek returns the next Unicode character in the source without advancing
|
||||
// the scanner. It returns EOF if the scanner's position is at the last
|
||||
// character of the source.
|
||||
// Do not call Peek() on a camouflaged Parser
|
||||
func (p *Parser) Peek() rune {
|
||||
if p.isCamouflaged() {
|
||||
panic("can not Peek() on camouflaged Parser")
|
||||
}
|
||||
return p.scanner.Peek()
|
||||
}
|
||||
|
||||
var errCamouflageAfterNext = fmt.Errorf("Camouflage() after Next()")
|
||||
|
||||
// Next reads and returns the next Unicode character.
|
||||
// It returns EOF at the end of the source.
|
||||
// Do not call Next() on a camouflaged Parser
|
||||
func (p *Parser) Next() rune {
|
||||
if p.isCamouflaged() {
|
||||
panic("can not Next() on camouflaged Parser")
|
||||
}
|
||||
p.camouflage = errCamouflageAfterNext
|
||||
return p.scanner.Next()
|
||||
}
|
||||
|
||||
// TokenText returns the string corresponding to the most recently scanned token.
|
||||
// Valid after calling Scan().
|
||||
func (p *Parser) TokenText() string {
|
||||
return p.scanner.TokenText()
|
||||
}
|
||||
|
||||
//Expected returns an error signaling an unexpected Scan() result
|
||||
func (p *Parser) Expected(unit string, expected ...rune) error {
|
||||
return unexpectedRune{unit, expected, p.lastScan}
|
||||
}
|
||||
|
||||
type unexpectedRune struct {
|
||||
unit string
|
||||
expected []rune
|
||||
got rune
|
||||
}
|
||||
|
||||
func (err unexpectedRune) Error() string {
|
||||
exp := bytes.Buffer{}
|
||||
runes := err.expected
|
||||
switch len(runes) {
|
||||
default:
|
||||
for _, r := range runes[:len(runes)-2] {
|
||||
exp.WriteString(scanner.TokenString(r))
|
||||
exp.WriteString(", ")
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
exp.WriteString(scanner.TokenString(runes[len(runes)-2]))
|
||||
exp.WriteString(" or ")
|
||||
fallthrough
|
||||
case 1:
|
||||
exp.WriteString(scanner.TokenString(runes[len(runes)-1]))
|
||||
case 0:
|
||||
return fmt.Sprintf("unexpected %s while scanning %s", scanner.TokenString(err.got), err.unit)
|
||||
}
|
||||
return fmt.Sprintf("unexpected %s while scanning %s expected %s", scanner.TokenString(err.got), err.unit, exp.String())
|
||||
}
|
||||
BIN
extra/server-discovery/vendor/github.com/PaesslerAG/gval/prtg-batmin-gopher.png
generated
vendored
Normal file
BIN
extra/server-discovery/vendor/github.com/PaesslerAG/gval/prtg-batmin-gopher.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
14
extra/server-discovery/vendor/github.com/SentimensRG/ctx/.gitignore
generated
vendored
Normal file
14
extra/server-discovery/vendor/github.com/SentimensRG/ctx/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
21
extra/server-discovery/vendor/github.com/SentimensRG/ctx/LICENSE
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/SentimensRG/ctx/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Sentimens Research Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
63
extra/server-discovery/vendor/github.com/SentimensRG/ctx/README.md
generated
vendored
Normal file
63
extra/server-discovery/vendor/github.com/SentimensRG/ctx/README.md
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# ctx
|
||||
|
||||
Composable utilities for Go contexts.
|
||||
|
||||
[](https://godoc.org/github.com/SentimensRG/ctx)
|
||||
[](https://goreportcard.com/report/github.com/SentimensRG/ctx)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get -u github.com/SentimensRG/ctx
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
The `ctx` package provides utilites for working with data structures satisfying
|
||||
the `ctx.Doner` interface, most notably `context.Context`:
|
||||
|
||||
```go
|
||||
type Doner interface {
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
```
|
||||
|
||||
The functions in `ctx` are appropriate for operations that do not preserve the
|
||||
values in a context, e.g.: joining several contexts together.
|
||||
|
||||
## Subpackages
|
||||
|
||||
- [sigctx](https://github.com/SentimensRG/ctx/tree/master/sigctx): contexts for graceful shutdown
|
||||
- [refctx](https://github.com/SentimensRG/ctx/tree/master/refctx): contexts linked to a reference-counter
|
||||
- [mergectx](https://github.com/SentimensRG/ctx/tree/master/mergectx): utilities for merging `context.Context` instances while preserving values, errors and deadlines.
|
||||
|
||||
## RFC
|
||||
|
||||
If you find this useful please let me know: <l.thibault@sentimens.com>
|
||||
|
||||
Seriously, even if you just used it in your weekend project, I'd like to hear
|
||||
about it :)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017 Sentimens Research Group, LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
184
extra/server-discovery/vendor/github.com/SentimensRG/ctx/ctx.go
generated
vendored
Normal file
184
extra/server-discovery/vendor/github.com/SentimensRG/ctx/ctx.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Binder is the interface that wraps the basic Bind method.
|
||||
// Bind executes logic until the Doner completes. Implementations of Bind must
|
||||
// not return until the Doner has completed.
|
||||
type Binder interface {
|
||||
Bind(Doner)
|
||||
}
|
||||
|
||||
// BindFunc is an adapter to allow the use of ordinary functions as Binders.
|
||||
type BindFunc func(Doner)
|
||||
|
||||
// Bind executes logic until the Doner completes. It satisfies the Binder
|
||||
// interface.
|
||||
func (f BindFunc) Bind(d Doner) { f(d) }
|
||||
|
||||
// Doner can block until something is done
|
||||
type Doner interface {
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
// C is a basic implementation of Doner
|
||||
type C <-chan struct{}
|
||||
|
||||
// Background is the ctx analog to context.Background(). It never fires.
|
||||
func Background() C {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Done returns a channel that receives when an action is complete
|
||||
func (dc C) Done() <-chan struct{} { return dc }
|
||||
|
||||
type ctx struct {
|
||||
Doner
|
||||
}
|
||||
|
||||
// Deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. Deadline returns ok==false when no deadline is
|
||||
// set. Successive calls to Deadline return the same results.
|
||||
func (ctx) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c ctx) Err() error {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return context.Canceled
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c ctx) Value(interface{}) (v interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// AsContext creates a context that fires when the Doner fires
|
||||
func AsContext(d Doner) context.Context {
|
||||
return ctx{d}
|
||||
}
|
||||
|
||||
// After time time has elapsed, the Doner fires
|
||||
func After(d time.Duration) C {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
<-time.After(d)
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// WithCancel returns a new Doner that can be cancelled via the associated
|
||||
// function
|
||||
func WithCancel(d Doner) (C, func()) {
|
||||
var closer sync.Once
|
||||
cq := make(chan struct{})
|
||||
cancel := func() { closer.Do(func() { close(cq) }) }
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-cq:
|
||||
case <-d.Done():
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return cq, cancel
|
||||
}
|
||||
|
||||
// Tick returns a <-chan whose range ends when the underlying context cancels
|
||||
func Tick(d Doner) <-chan struct{} {
|
||||
c := make(chan struct{})
|
||||
cq := d.Done()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-cq:
|
||||
close(c)
|
||||
return
|
||||
default:
|
||||
select {
|
||||
case c <- struct{}{}:
|
||||
case <-cq:
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
// Defer guarantees that a function will be called after a context has cancelled
|
||||
func Defer(d Doner, cb func()) {
|
||||
go func() {
|
||||
<-d.Done()
|
||||
cb()
|
||||
}()
|
||||
}
|
||||
|
||||
// Link returns a channel that fires if ANY of the constituent Doners have fired
|
||||
func Link(doners ...Doner) C {
|
||||
c := make(chan struct{})
|
||||
cancel := func() { close(c) }
|
||||
|
||||
var once sync.Once
|
||||
for _, d := range doners {
|
||||
Defer(d, func() { once.Do(cancel) })
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Join returns a channel that receives when all constituent Doners have fired
|
||||
func Join(doners ...Doner) C {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(doners))
|
||||
for _, d := range doners {
|
||||
Defer(d, wg.Done)
|
||||
}
|
||||
|
||||
cq := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(cq)
|
||||
}()
|
||||
return cq
|
||||
}
|
||||
|
||||
// FTick calls a function in a loop until the Doner has fired
|
||||
func FTick(d Doner, f func()) {
|
||||
for range Tick(d) {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
// FTickInterval calls a function repeatedly at a given internval, until the Doner
|
||||
// has fired. Note that FTickInterval ignores the time spent executing a function,
|
||||
// and instead guarantees an interval of `t` between of return of the previous
|
||||
// function call and the invocation of the next function call.
|
||||
func FTickInterval(d Doner, t time.Duration, f func()) {
|
||||
for {
|
||||
select {
|
||||
case <-d.Done():
|
||||
return
|
||||
case <-time.After(t):
|
||||
f()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FDone returns a doner that fires when the function returns or panics
|
||||
func FDone(f func()) C {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
f()
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
63
extra/server-discovery/vendor/github.com/SentimensRG/ctx/sigctx/README.md
generated
vendored
Normal file
63
extra/server-discovery/vendor/github.com/SentimensRG/ctx/sigctx/README.md
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# sigctx
|
||||
|
||||
Package `sigctx` provides contexts for graceful shutdown.
|
||||
|
||||
The `sigctx` package provides a context that terminates when it receives a
|
||||
SIGINT or SIGTERM. This provides a convenient mechanism for triggering
|
||||
graceful application shutdown.
|
||||
|
||||
`sigctx.New` returns a `ctx.C`, which implements the ubiquitous `ctx.Doner`
|
||||
interface. It fires when either SIGINT or SIGTERM is caught.
|
||||
|
||||
## Examples
|
||||
|
||||
```go
|
||||
import (
|
||||
"log"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := sigctx.New() // returns a regular context.Context
|
||||
|
||||
<-ctx.Done() // will unblock on SIGINT and SIGTERM
|
||||
log.Println("exiting.")
|
||||
}
|
||||
```
|
||||
|
||||
`sigctx.Tick` can be used to react to streams of signals. For example, you can
|
||||
implement a graceful shutdown attempt, followed by a forced shutdown.
|
||||
|
||||
```go
|
||||
import (
|
||||
"log"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
"github.com/SentimensRG/ctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := sigctx.Tick()
|
||||
d, cancel := ctx.WithCancel(ctx.Background())
|
||||
|
||||
go func() {
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
// business logic goes here
|
||||
}()
|
||||
|
||||
<-t
|
||||
log.Println("attempting graceful shutdown - press Ctrl + c again to force quit")
|
||||
go func() {
|
||||
defer cancel()
|
||||
// cleanup logic goes here
|
||||
}()
|
||||
|
||||
<-t
|
||||
log.Println("forcing close")
|
||||
}()
|
||||
|
||||
<-d.Done()
|
||||
}
|
||||
|
||||
```
|
||||
60
extra/server-discovery/vendor/github.com/SentimensRG/ctx/sigctx/sigctx.go
generated
vendored
Normal file
60
extra/server-discovery/vendor/github.com/SentimensRG/ctx/sigctx/sigctx.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Package sigctx provides a context that expires when a SIGINT or SIGTERM is
|
||||
// received.
|
||||
package sigctx
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/SentimensRG/ctx"
|
||||
)
|
||||
|
||||
var (
|
||||
c ctx.C
|
||||
sigCh chan os.Signal
|
||||
initC, initSig sync.Once
|
||||
)
|
||||
|
||||
func initSigCh() {
|
||||
sigCh = make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
}
|
||||
|
||||
// New signal-bound ctx.C that terminates when either SIGINT or SIGTERM
|
||||
// is caught.
|
||||
func New() ctx.C {
|
||||
initC.Do(func() {
|
||||
initSig.Do(initSigCh)
|
||||
|
||||
dc := make(chan struct{})
|
||||
c = dc
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-sigCh:
|
||||
close(dc)
|
||||
case <-c.Done():
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Tick returns a channel that recvs each time a either SIGINT or SIGTERM are
|
||||
// caught.
|
||||
func Tick() <-chan struct{} {
|
||||
initSig.Do(initSigCh)
|
||||
|
||||
dc := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
<-sigCh
|
||||
dc <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
return dc
|
||||
}
|
||||
15
extra/server-discovery/vendor/github.com/asaskevich/govalidator/.gitignore
generated
vendored
Normal file
15
extra/server-discovery/vendor/github.com/asaskevich/govalidator/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
bin/
|
||||
.idea/
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
12
extra/server-discovery/vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal file
12
extra/server-discovery/vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
language: go
|
||||
dist: xenial
|
||||
go:
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- '1.12'
|
||||
- '1.13'
|
||||
- 'tip'
|
||||
|
||||
script:
|
||||
- go test -coverpkg=./... -coverprofile=coverage.info -timeout=5s
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
43
extra/server-discovery/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md
generated
vendored
Normal file
43
extra/server-discovery/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
This project adheres to [The Code Manifesto](http://codemanifesto.com)
|
||||
as its guidelines for contributor interactions.
|
||||
|
||||
## The Code Manifesto
|
||||
|
||||
We want to work in an ecosystem that empowers developers to reach their
|
||||
potential — one that encourages growth and effective collaboration. A space
|
||||
that is safe for all.
|
||||
|
||||
A space such as this benefits everyone that participates in it. It encourages
|
||||
new developers to enter our field. It is through discussion and collaboration
|
||||
that we grow, and through growth that we improve.
|
||||
|
||||
In the effort to create such a place, we hold to these values:
|
||||
|
||||
1. **Discrimination limits us.** This includes discrimination on the basis of
|
||||
race, gender, sexual orientation, gender identity, age, nationality,
|
||||
technology and any other arbitrary exclusion of a group of people.
|
||||
2. **Boundaries honor us.** Your comfort levels are not everyone’s comfort
|
||||
levels. Remember that, and if brought to your attention, heed it.
|
||||
3. **We are our biggest assets.** None of us were born masters of our trade.
|
||||
Each of us has been helped along the way. Return that favor, when and where
|
||||
you can.
|
||||
4. **We are resources for the future.** As an extension of #3, share what you
|
||||
know. Make yourself a resource to help those that come after you.
|
||||
5. **Respect defines us.** Treat others as you wish to be treated. Make your
|
||||
discussions, criticisms and debates from a position of respectfulness. Ask
|
||||
yourself, is it true? Is it necessary? Is it constructive? Anything less is
|
||||
unacceptable.
|
||||
6. **Reactions require grace.** Angry responses are valid, but abusive language
|
||||
and vindictive actions are toxic. When something happens that offends you,
|
||||
handle it assertively, but be respectful. Escalate reasonably, and try to
|
||||
allow the offender an opportunity to explain themselves, and possibly
|
||||
correct the issue.
|
||||
7. **Opinions are just that: opinions.** Each and every one of us, due to our
|
||||
background and upbringing, have varying opinions. That is perfectly
|
||||
acceptable. Remember this: if you respect your own opinions, you should
|
||||
respect the opinions of others.
|
||||
8. **To err is human.** You might not intend it, but mistakes do happen and
|
||||
contribute to build experience. Tolerate honest mistakes, and don't
|
||||
hesitate to apologize if you make one yourself.
|
||||
63
extra/server-discovery/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md
generated
vendored
Normal file
63
extra/server-discovery/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#### Support
|
||||
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
|
||||
|
||||
#### What to contribute
|
||||
If you don't know what to do, there are some features and functions that need to be done
|
||||
|
||||
- [ ] Refactor code
|
||||
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
|
||||
- [ ] Create actual list of contributors and projects that currently using this package
|
||||
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
|
||||
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
|
||||
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
|
||||
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
|
||||
- [x] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
|
||||
- [ ] Implement fuzzing testing
|
||||
- [ ] Implement some struct/map/array utilities
|
||||
- [ ] Implement map/array validation
|
||||
- [ ] Implement benchmarking
|
||||
- [ ] Implement batch of examples
|
||||
- [ ] Look at forks for new features and fixes
|
||||
|
||||
#### Advice
|
||||
Feel free to create what you want, but keep in mind when you implement new features:
|
||||
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
|
||||
- Public functions must be documented and described in source file and added to README.md to the list of available functions
|
||||
- There are must be unit-tests for any new functions and improvements
|
||||
|
||||
## Financial contributions
|
||||
|
||||
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/govalidator).
|
||||
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
Thank you to all the people who have already contributed to govalidator!
|
||||
<a href="https://github.com/asaskevich/govalidator/graphs/contributors"><img src="https://opencollective.com/govalidator/contributors.svg?width=890" /></a>
|
||||
|
||||
|
||||
### Backers
|
||||
|
||||
Thank you to all our backers! [[Become a backer](https://opencollective.com/govalidator#backer)]
|
||||
|
||||
<a href="https://opencollective.com/govalidator#backers" target="_blank"><img src="https://opencollective.com/govalidator/backers.svg?width=890"></a>
|
||||
|
||||
|
||||
### Sponsors
|
||||
|
||||
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/govalidator#sponsor))
|
||||
|
||||
<a href="https://opencollective.com/govalidator/sponsor/0/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/1/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/2/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/3/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/4/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/5/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/6/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/7/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/8/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/9/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/9/avatar.svg"></a>
|
||||
21
extra/server-discovery/vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2020 Alex Saskevich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
622
extra/server-discovery/vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
622
extra/server-discovery/vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
@@ -0,0 +1,622 @@
|
||||
govalidator
|
||||
===========
|
||||
[](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://godoc.org/github.com/asaskevich/govalidator)
|
||||
[](https://travis-ci.org/asaskevich/govalidator)
|
||||
[](https://codecov.io/gh/asaskevich/govalidator) [](https://goreportcard.com/report/github.com/asaskevich/govalidator) [](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) [](#backers) [](#sponsors) [](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_shield)
|
||||
|
||||
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
||||
|
||||
#### Installation
|
||||
Make sure that Go is installed on your computer.
|
||||
Type the following command in your terminal:
|
||||
|
||||
go get github.com/asaskevich/govalidator
|
||||
|
||||
or you can get specified release of the package with `gopkg.in`:
|
||||
|
||||
go get gopkg.in/asaskevich/govalidator.v10
|
||||
|
||||
After it the package is ready to use.
|
||||
|
||||
|
||||
#### Import package in your project
|
||||
Add following line in your `*.go` file:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
```
|
||||
If you are unhappy to use long `govalidator`, you can do something like this:
|
||||
```go
|
||||
import (
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
)
|
||||
```
|
||||
|
||||
#### Activate behavior to require all fields have a validation tag by default
|
||||
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
|
||||
|
||||
`SetNilPtrAllowedByRequired` causes validation to pass when struct fields marked by `required` are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between `nil` and `zero value` state can use this. If disabled, both `nil` and `zero` values cause validation errors.
|
||||
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
func init() {
|
||||
govalidator.SetFieldsRequiredByDefault(true)
|
||||
}
|
||||
```
|
||||
|
||||
Here's some code to explain it:
|
||||
```go
|
||||
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||
type exampleStruct struct {
|
||||
Name string ``
|
||||
Email string `valid:"email"`
|
||||
}
|
||||
|
||||
// this, however, will only fail when Email is empty or an invalid email address:
|
||||
type exampleStruct2 struct {
|
||||
Name string `valid:"-"`
|
||||
Email string `valid:"email"`
|
||||
}
|
||||
|
||||
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||
type exampleStruct2 struct {
|
||||
Name string `valid:"-"`
|
||||
Email string `valid:"email,optional"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
|
||||
##### Custom validator function signature
|
||||
A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
// old signature
|
||||
func(i interface{}) bool
|
||||
|
||||
// new signature
|
||||
func(i interface{}, o interface{}) bool
|
||||
```
|
||||
|
||||
##### Adding a custom validator
|
||||
This was changed to prevent data races when accessing custom validators.
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
// before
|
||||
govalidator.CustomTypeTagMap["customByteArrayValidator"] = func(i interface{}, o interface{}) bool {
|
||||
// ...
|
||||
}
|
||||
|
||||
// after
|
||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, o interface{}) bool {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### List of functions:
|
||||
```go
|
||||
func Abs(value float64) float64
|
||||
func BlackList(str, chars string) string
|
||||
func ByteLength(str string, params ...string) bool
|
||||
func CamelCaseToUnderscore(str string) string
|
||||
func Contains(str, substring string) bool
|
||||
func Count(array []interface{}, iterator ConditionIterator) int
|
||||
func Each(array []interface{}, iterator Iterator)
|
||||
func ErrorByField(e error, field string) string
|
||||
func ErrorsByField(e error) map[string]string
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
|
||||
func Find(array []interface{}, iterator ConditionIterator) interface{}
|
||||
func GetLine(s string, index int) (string, error)
|
||||
func GetLines(s string) []string
|
||||
func HasLowerCase(str string) bool
|
||||
func HasUpperCase(str string) bool
|
||||
func HasWhitespace(str string) bool
|
||||
func HasWhitespaceOnly(str string) bool
|
||||
func InRange(value interface{}, left interface{}, right interface{}) bool
|
||||
func InRangeFloat32(value, left, right float32) bool
|
||||
func InRangeFloat64(value, left, right float64) bool
|
||||
func InRangeInt(value, left, right interface{}) bool
|
||||
func IsASCII(str string) bool
|
||||
func IsAlpha(str string) bool
|
||||
func IsAlphanumeric(str string) bool
|
||||
func IsBase64(str string) bool
|
||||
func IsByteLength(str string, min, max int) bool
|
||||
func IsCIDR(str string) bool
|
||||
func IsCRC32(str string) bool
|
||||
func IsCRC32b(str string) bool
|
||||
func IsCreditCard(str string) bool
|
||||
func IsDNSName(str string) bool
|
||||
func IsDataURI(str string) bool
|
||||
func IsDialString(str string) bool
|
||||
func IsDivisibleBy(str, num string) bool
|
||||
func IsEmail(str string) bool
|
||||
func IsExistingEmail(email string) bool
|
||||
func IsFilePath(str string) (bool, int)
|
||||
func IsFloat(str string) bool
|
||||
func IsFullWidth(str string) bool
|
||||
func IsHalfWidth(str string) bool
|
||||
func IsHash(str string, algorithm string) bool
|
||||
func IsHexadecimal(str string) bool
|
||||
func IsHexcolor(str string) bool
|
||||
func IsHost(str string) bool
|
||||
func IsIP(str string) bool
|
||||
func IsIPv4(str string) bool
|
||||
func IsIPv6(str string) bool
|
||||
func IsISBN(str string, version int) bool
|
||||
func IsISBN10(str string) bool
|
||||
func IsISBN13(str string) bool
|
||||
func IsISO3166Alpha2(str string) bool
|
||||
func IsISO3166Alpha3(str string) bool
|
||||
func IsISO4217(str string) bool
|
||||
func IsISO693Alpha2(str string) bool
|
||||
func IsISO693Alpha3b(str string) bool
|
||||
func IsIn(str string, params ...string) bool
|
||||
func IsInRaw(str string, params ...string) bool
|
||||
func IsInt(str string) bool
|
||||
func IsJSON(str string) bool
|
||||
func IsLatitude(str string) bool
|
||||
func IsLongitude(str string) bool
|
||||
func IsLowerCase(str string) bool
|
||||
func IsMAC(str string) bool
|
||||
func IsMD4(str string) bool
|
||||
func IsMD5(str string) bool
|
||||
func IsMagnetURI(str string) bool
|
||||
func IsMongoID(str string) bool
|
||||
func IsMultibyte(str string) bool
|
||||
func IsNatural(value float64) bool
|
||||
func IsNegative(value float64) bool
|
||||
func IsNonNegative(value float64) bool
|
||||
func IsNonPositive(value float64) bool
|
||||
func IsNotNull(str string) bool
|
||||
func IsNull(str string) bool
|
||||
func IsNumeric(str string) bool
|
||||
func IsPort(str string) bool
|
||||
func IsPositive(value float64) bool
|
||||
func IsPrintableASCII(str string) bool
|
||||
func IsRFC3339(str string) bool
|
||||
func IsRFC3339WithoutZone(str string) bool
|
||||
func IsRGBcolor(str string) bool
|
||||
func IsRegex(str string) bool
|
||||
func IsRequestURI(rawurl string) bool
|
||||
func IsRequestURL(rawurl string) bool
|
||||
func IsRipeMD128(str string) bool
|
||||
func IsRipeMD160(str string) bool
|
||||
func IsRsaPub(str string, params ...string) bool
|
||||
func IsRsaPublicKey(str string, keylen int) bool
|
||||
func IsSHA1(str string) bool
|
||||
func IsSHA256(str string) bool
|
||||
func IsSHA384(str string) bool
|
||||
func IsSHA512(str string) bool
|
||||
func IsSSN(str string) bool
|
||||
func IsSemver(str string) bool
|
||||
func IsTiger128(str string) bool
|
||||
func IsTiger160(str string) bool
|
||||
func IsTiger192(str string) bool
|
||||
func IsTime(str string, format string) bool
|
||||
func IsType(v interface{}, params ...string) bool
|
||||
func IsURL(str string) bool
|
||||
func IsUTFDigit(str string) bool
|
||||
func IsUTFLetter(str string) bool
|
||||
func IsUTFLetterNumeric(str string) bool
|
||||
func IsUTFNumeric(str string) bool
|
||||
func IsUUID(str string) bool
|
||||
func IsUUIDv3(str string) bool
|
||||
func IsUUIDv4(str string) bool
|
||||
func IsUUIDv5(str string) bool
|
||||
func IsULID(str string) bool
|
||||
func IsUnixTime(str string) bool
|
||||
func IsUpperCase(str string) bool
|
||||
func IsVariableWidth(str string) bool
|
||||
func IsWhole(value float64) bool
|
||||
func LeftTrim(str, chars string) string
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{}
|
||||
func Matches(str, pattern string) bool
|
||||
func MaxStringLength(str string, params ...string) bool
|
||||
func MinStringLength(str string, params ...string) bool
|
||||
func NormalizeEmail(str string) (string, error)
|
||||
func PadBoth(str string, padStr string, padLen int) string
|
||||
func PadLeft(str string, padStr string, padLen int) string
|
||||
func PadRight(str string, padStr string, padLen int) string
|
||||
func PrependPathToErrors(err error, path string) error
|
||||
func Range(str string, params ...string) bool
|
||||
func RemoveTags(s string) string
|
||||
func ReplacePattern(str, pattern, replace string) string
|
||||
func Reverse(s string) string
|
||||
func RightTrim(str, chars string) string
|
||||
func RuneLength(str string, params ...string) bool
|
||||
func SafeFileName(str string) string
|
||||
func SetFieldsRequiredByDefault(value bool)
|
||||
func SetNilPtrAllowedByRequired(value bool)
|
||||
func Sign(value float64) float64
|
||||
func StringLength(str string, params ...string) bool
|
||||
func StringMatches(s string, params ...string) bool
|
||||
func StripLow(str string, keepNewLines bool) string
|
||||
func ToBoolean(str string) (bool, error)
|
||||
func ToFloat(str string) (float64, error)
|
||||
func ToInt(value interface{}) (res int64, err error)
|
||||
func ToJSON(obj interface{}) (string, error)
|
||||
func ToString(obj interface{}) string
|
||||
func Trim(str, chars string) string
|
||||
func Truncate(str string, length int, ending string) string
|
||||
func TruncatingErrorf(str string, args ...interface{}) error
|
||||
func UnderscoreToCamelCase(s string) string
|
||||
func ValidateMap(inputMap map[string]interface{}, validationMap map[string]interface{}) (bool, error)
|
||||
func ValidateStruct(s interface{}) (bool, error)
|
||||
func WhiteList(str, chars string) string
|
||||
type ConditionIterator
|
||||
type CustomTypeValidator
|
||||
type Error
|
||||
func (e Error) Error() string
|
||||
type Errors
|
||||
func (es Errors) Error() string
|
||||
func (es Errors) Errors() []error
|
||||
type ISO3166Entry
|
||||
type ISO693Entry
|
||||
type InterfaceParamValidator
|
||||
type Iterator
|
||||
type ParamValidator
|
||||
type ResultIterator
|
||||
type UnsupportedTypeError
|
||||
func (e *UnsupportedTypeError) Error() string
|
||||
type Validator
|
||||
```
|
||||
|
||||
#### Examples
|
||||
###### IsURL
|
||||
```go
|
||||
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
|
||||
```
|
||||
###### IsType
|
||||
```go
|
||||
println(govalidator.IsType("Bob", "string"))
|
||||
println(govalidator.IsType(1, "int"))
|
||||
i := 1
|
||||
println(govalidator.IsType(&i, "*int"))
|
||||
```
|
||||
|
||||
IsType can be used through the tag `type` which is essential for map validation:
|
||||
```go
|
||||
type User struct {
|
||||
Name string `valid:"type(string)"`
|
||||
Age int `valid:"type(int)"`
|
||||
Meta interface{} `valid:"type(string)"`
|
||||
}
|
||||
result, err := govalidator.ValidateStruct(User{"Bob", 20, "meta"})
|
||||
if err != nil {
|
||||
println("error: " + err.Error())
|
||||
}
|
||||
println(result)
|
||||
```
|
||||
###### ToString
|
||||
```go
|
||||
type User struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
}
|
||||
|
||||
str := govalidator.ToString(&User{"John", "Juan"})
|
||||
println(str)
|
||||
```
|
||||
###### Each, Map, Filter, Count for slices
|
||||
Each iterates over the slice/array and calls Iterator for every item
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5}
|
||||
var fn govalidator.Iterator = func(value interface{}, index int) {
|
||||
println(value.(int))
|
||||
}
|
||||
govalidator.Each(data, fn)
|
||||
```
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5}
|
||||
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
|
||||
return value.(int) * 3
|
||||
}
|
||||
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
||||
```
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
|
||||
return value.(int)%2 == 0
|
||||
}
|
||||
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
||||
_ = govalidator.Count(data, fn) // result = 5
|
||||
```
|
||||
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
|
||||
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
|
||||
```go
|
||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||
return str == "duck"
|
||||
})
|
||||
```
|
||||
For completely custom validators (interface-based), see below.
|
||||
|
||||
Here is a list of available validators for struct fields (validator - used function):
|
||||
```go
|
||||
"email": IsEmail,
|
||||
"url": IsURL,
|
||||
"dialstring": IsDialString,
|
||||
"requrl": IsRequestURL,
|
||||
"requri": IsRequestURI,
|
||||
"alpha": IsAlpha,
|
||||
"utfletter": IsUTFLetter,
|
||||
"alphanum": IsAlphanumeric,
|
||||
"utfletternum": IsUTFLetterNumeric,
|
||||
"numeric": IsNumeric,
|
||||
"utfnumeric": IsUTFNumeric,
|
||||
"utfdigit": IsUTFDigit,
|
||||
"hexadecimal": IsHexadecimal,
|
||||
"hexcolor": IsHexcolor,
|
||||
"rgbcolor": IsRGBcolor,
|
||||
"lowercase": IsLowerCase,
|
||||
"uppercase": IsUpperCase,
|
||||
"int": IsInt,
|
||||
"float": IsFloat,
|
||||
"null": IsNull,
|
||||
"uuid": IsUUID,
|
||||
"uuidv3": IsUUIDv3,
|
||||
"uuidv4": IsUUIDv4,
|
||||
"uuidv5": IsUUIDv5,
|
||||
"creditcard": IsCreditCard,
|
||||
"isbn10": IsISBN10,
|
||||
"isbn13": IsISBN13,
|
||||
"json": IsJSON,
|
||||
"multibyte": IsMultibyte,
|
||||
"ascii": IsASCII,
|
||||
"printableascii": IsPrintableASCII,
|
||||
"fullwidth": IsFullWidth,
|
||||
"halfwidth": IsHalfWidth,
|
||||
"variablewidth": IsVariableWidth,
|
||||
"base64": IsBase64,
|
||||
"datauri": IsDataURI,
|
||||
"ip": IsIP,
|
||||
"port": IsPort,
|
||||
"ipv4": IsIPv4,
|
||||
"ipv6": IsIPv6,
|
||||
"dns": IsDNSName,
|
||||
"host": IsHost,
|
||||
"mac": IsMAC,
|
||||
"latitude": IsLatitude,
|
||||
"longitude": IsLongitude,
|
||||
"ssn": IsSSN,
|
||||
"semver": IsSemver,
|
||||
"rfc3339": IsRFC3339,
|
||||
"rfc3339WithoutZone": IsRFC3339WithoutZone,
|
||||
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||
"ulid": IsULID,
|
||||
```
|
||||
Validators with parameters
|
||||
|
||||
```go
|
||||
"range(min|max)": Range,
|
||||
"length(min|max)": ByteLength,
|
||||
"runelength(min|max)": RuneLength,
|
||||
"stringlength(min|max)": StringLength,
|
||||
"matches(pattern)": StringMatches,
|
||||
"in(string1|string2|...|stringN)": IsIn,
|
||||
"rsapub(keylength)" : IsRsaPub,
|
||||
"minstringlength(int): MinStringLength,
|
||||
"maxstringlength(int): MaxStringLength,
|
||||
```
|
||||
Validators with parameters for any type
|
||||
|
||||
```go
|
||||
"type(type)": IsType,
|
||||
```
|
||||
|
||||
And here is small example of usage:
|
||||
```go
|
||||
type Post struct {
|
||||
Title string `valid:"alphanum,required"`
|
||||
Message string `valid:"duck,ascii"`
|
||||
Message2 string `valid:"animal(dog)"`
|
||||
AuthorIP string `valid:"ipv4"`
|
||||
Date string `valid:"-"`
|
||||
}
|
||||
post := &Post{
|
||||
Title: "My Example Post",
|
||||
Message: "duck",
|
||||
Message2: "dog",
|
||||
AuthorIP: "123.234.54.3",
|
||||
}
|
||||
|
||||
// Add your own struct validation tags
|
||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||
return str == "duck"
|
||||
})
|
||||
|
||||
// Add your own struct validation tags with parameter
|
||||
govalidator.ParamTagMap["animal"] = govalidator.ParamValidator(func(str string, params ...string) bool {
|
||||
species := params[0]
|
||||
return str == species
|
||||
})
|
||||
govalidator.ParamTagRegexMap["animal"] = regexp.MustCompile("^animal\\((\\w+)\\)$")
|
||||
|
||||
result, err := govalidator.ValidateStruct(post)
|
||||
if err != nil {
|
||||
println("error: " + err.Error())
|
||||
}
|
||||
println(result)
|
||||
```
|
||||
###### ValidateMap [#2](https://github.com/asaskevich/govalidator/pull/338)
|
||||
If you want to validate maps, you can use the map to be validated and a validation map that contain the same tags used in ValidateStruct, both maps have to be in the form `map[string]interface{}`
|
||||
|
||||
So here is small example of usage:
|
||||
```go
|
||||
var mapTemplate = map[string]interface{}{
|
||||
"name":"required,alpha",
|
||||
"family":"required,alpha",
|
||||
"email":"required,email",
|
||||
"cell-phone":"numeric",
|
||||
"address":map[string]interface{}{
|
||||
"line1":"required,alphanum",
|
||||
"line2":"alphanum",
|
||||
"postal-code":"numeric",
|
||||
},
|
||||
}
|
||||
|
||||
var inputMap = map[string]interface{}{
|
||||
"name":"Bob",
|
||||
"family":"Smith",
|
||||
"email":"foo@bar.baz",
|
||||
"address":map[string]interface{}{
|
||||
"line1":"",
|
||||
"line2":"",
|
||||
"postal-code":"",
|
||||
},
|
||||
}
|
||||
|
||||
result, err := govalidator.ValidateMap(inputMap, mapTemplate)
|
||||
if err != nil {
|
||||
println("error: " + err.Error())
|
||||
}
|
||||
println(result)
|
||||
```
|
||||
|
||||
###### WhiteList
|
||||
```go
|
||||
// Remove all characters from string ignoring characters between "a" and "z"
|
||||
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||
```
|
||||
|
||||
###### Custom validation functions
|
||||
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
type CustomByteArray [6]byte // custom types are supported and can be validated
|
||||
|
||||
type StructWithCustomByteArray struct {
|
||||
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
|
||||
Email string `valid:"email"`
|
||||
CustomMinLength int `valid:"-"`
|
||||
}
|
||||
|
||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, context interface{}) bool {
|
||||
switch v := context.(type) { // you can type switch on the context interface being validated
|
||||
case StructWithCustomByteArray:
|
||||
// you can check and validate against some other field in the context,
|
||||
// return early or not validate against the context at all – your choice
|
||||
case SomeOtherType:
|
||||
// ...
|
||||
default:
|
||||
// expecting some other type? Throw/panic here or continue
|
||||
}
|
||||
|
||||
switch v := i.(type) { // type switch on the struct field being validated
|
||||
case CustomByteArray:
|
||||
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
|
||||
if e != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", func(i interface{}, context interface{}) bool {
|
||||
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
|
||||
case StructWithCustomByteArray:
|
||||
return len(v.ID) >= v.CustomMinLength
|
||||
}
|
||||
return false
|
||||
})
|
||||
```
|
||||
|
||||
###### Loop over Error()
|
||||
By default .Error() returns all errors in a single String. To access each error you can do this:
|
||||
```go
|
||||
if err != nil {
|
||||
errs := err.(govalidator.Errors).Errors()
|
||||
for _, e := range errs {
|
||||
fmt.Println(e.Error())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
###### Custom error messages
|
||||
Custom error messages are supported via annotations by adding the `~` separator - here's an example of how to use it:
|
||||
```go
|
||||
type Ticket struct {
|
||||
Id int64 `json:"id"`
|
||||
FirstName string `json:"firstname" valid:"required~First name is blank"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Notes
|
||||
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
||||
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
||||
|
||||
#### Support
|
||||
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
|
||||
|
||||
#### What to contribute
|
||||
If you don't know what to do, there are some features and functions that need to be done
|
||||
|
||||
- [ ] Refactor code
|
||||
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
|
||||
- [ ] Create actual list of contributors and projects that currently using this package
|
||||
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
|
||||
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
|
||||
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
|
||||
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
|
||||
- [x] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
|
||||
- [ ] Implement fuzzing testing
|
||||
- [ ] Implement some struct/map/array utilities
|
||||
- [ ] Implement map/array validation
|
||||
- [ ] Implement benchmarking
|
||||
- [ ] Implement batch of examples
|
||||
- [ ] Look at forks for new features and fixes
|
||||
|
||||
#### Advice
|
||||
Feel free to create what you want, but keep in mind when you implement new features:
|
||||
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
|
||||
- Public functions must be documented and described in source file and added to README.md to the list of available functions
|
||||
- There are must be unit-tests for any new functions and improvements
|
||||
|
||||
## Credits
|
||||
### Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
||||
|
||||
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
||||
* [Daniel Lohse](https://github.com/annismckenzie)
|
||||
* [Attila Oláh](https://github.com/attilaolah)
|
||||
* [Daniel Korner](https://github.com/Dadie)
|
||||
* [Steven Wilkin](https://github.com/stevenwilkin)
|
||||
* [Deiwin Sarjas](https://github.com/deiwin)
|
||||
* [Noah Shibley](https://github.com/slugmobile)
|
||||
* [Nathan Davies](https://github.com/nathj07)
|
||||
* [Matt Sanford](https://github.com/mzsanford)
|
||||
* [Simon ccl1115](https://github.com/ccl1115)
|
||||
|
||||
<a href="https://github.com/asaskevich/govalidator/graphs/contributors"><img src="https://opencollective.com/govalidator/contributors.svg?width=890" /></a>
|
||||
|
||||
|
||||
### Backers
|
||||
|
||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/govalidator#backer)]
|
||||
|
||||
<a href="https://opencollective.com/govalidator#backers" target="_blank"><img src="https://opencollective.com/govalidator/backers.svg?width=890"></a>
|
||||
|
||||
|
||||
### Sponsors
|
||||
|
||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/govalidator#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/govalidator/sponsor/0/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/1/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/2/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/3/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/4/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/5/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/6/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/7/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/8/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/govalidator/sponsor/9/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/9/avatar.svg"></a>
|
||||
|
||||
|
||||
|
||||
|
||||
## License
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_large)
|
||||
87
extra/server-discovery/vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
Normal file
87
extra/server-discovery/vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package govalidator
|
||||
|
||||
// Iterator is the function that accepts element of slice/array and its index
|
||||
type Iterator func(interface{}, int)
|
||||
|
||||
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
||||
type ResultIterator func(interface{}, int) interface{}
|
||||
|
||||
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
||||
type ConditionIterator func(interface{}, int) bool
|
||||
|
||||
// ReduceIterator is the function that accepts two element of slice/array and returns result of merging those values
|
||||
type ReduceIterator func(interface{}, interface{}) interface{}
|
||||
|
||||
// Some validates that any item of array corresponds to ConditionIterator. Returns boolean.
|
||||
func Some(array []interface{}, iterator ConditionIterator) bool {
|
||||
res := false
|
||||
for index, data := range array {
|
||||
res = res || iterator(data, index)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Every validates that every item of array corresponds to ConditionIterator. Returns boolean.
|
||||
func Every(array []interface{}, iterator ConditionIterator) bool {
|
||||
res := true
|
||||
for index, data := range array {
|
||||
res = res && iterator(data, index)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Reduce boils down a list of values into a single value by ReduceIterator
|
||||
func Reduce(array []interface{}, iterator ReduceIterator, initialValue interface{}) interface{} {
|
||||
for _, data := range array {
|
||||
initialValue = iterator(initialValue, data)
|
||||
}
|
||||
return initialValue
|
||||
}
|
||||
|
||||
// Each iterates over the slice and apply Iterator to every item
|
||||
func Each(array []interface{}, iterator Iterator) {
|
||||
for index, data := range array {
|
||||
iterator(data, index)
|
||||
}
|
||||
}
|
||||
|
||||
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
||||
var result = make([]interface{}, len(array))
|
||||
for index, data := range array {
|
||||
result[index] = iterator(data, index)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
||||
func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
||||
var result = make([]interface{}, 0)
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
result = append(result, data)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
||||
func Count(array []interface{}, iterator ConditionIterator) int {
|
||||
count := 0
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
81
extra/server-discovery/vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal file
81
extra/server-discovery/vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package govalidator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ToString convert the input to a string.
|
||||
func ToString(obj interface{}) string {
|
||||
res := fmt.Sprintf("%v", obj)
|
||||
return res
|
||||
}
|
||||
|
||||
// ToJSON convert the input to a valid JSON string
|
||||
func ToJSON(obj interface{}) (string, error) {
|
||||
res, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
res = []byte("")
|
||||
}
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
||||
func ToFloat(value interface{}) (res float64, err error) {
|
||||
val := reflect.ValueOf(value)
|
||||
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
res = float64(val.Int())
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = float64(val.Uint())
|
||||
case float32, float64:
|
||||
res = val.Float()
|
||||
case string:
|
||||
res, err = strconv.ParseFloat(val.String(), 64)
|
||||
if err != nil {
|
||||
res = 0
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("ToInt: unknown interface type %T", value)
|
||||
res = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer.
|
||||
func ToInt(value interface{}) (res int64, err error) {
|
||||
val := reflect.ValueOf(value)
|
||||
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
res = val.Int()
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = int64(val.Uint())
|
||||
case float32, float64:
|
||||
res = int64(val.Float())
|
||||
case string:
|
||||
if IsInt(val.String()) {
|
||||
res, err = strconv.ParseInt(val.String(), 0, 64)
|
||||
if err != nil {
|
||||
res = 0
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("ToInt: invalid numeric format %g", value)
|
||||
res = 0
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("ToInt: unknown interface type %T", value)
|
||||
res = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToBoolean convert the input string to a boolean.
|
||||
func ToBoolean(str string) (bool, error) {
|
||||
return strconv.ParseBool(str)
|
||||
}
|
||||
3
extra/server-discovery/vendor/github.com/asaskevich/govalidator/doc.go
generated
vendored
Normal file
3
extra/server-discovery/vendor/github.com/asaskevich/govalidator/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package govalidator
|
||||
|
||||
// A package of validators and sanitizers for strings, structures and collections.
|
||||
47
extra/server-discovery/vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
Normal file
47
extra/server-discovery/vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package govalidator
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Errors is an array of multiple errors and conforms to the error interface.
|
||||
type Errors []error
|
||||
|
||||
// Errors returns itself.
|
||||
func (es Errors) Errors() []error {
|
||||
return es
|
||||
}
|
||||
|
||||
func (es Errors) Error() string {
|
||||
var errs []string
|
||||
for _, e := range es {
|
||||
errs = append(errs, e.Error())
|
||||
}
|
||||
sort.Strings(errs)
|
||||
return strings.Join(errs, ";")
|
||||
}
|
||||
|
||||
// Error encapsulates a name, an error and whether there's a custom error message or not.
|
||||
type Error struct {
|
||||
Name string
|
||||
Err error
|
||||
CustomErrorMessageExists bool
|
||||
|
||||
// Validator indicates the name of the validator that failed
|
||||
Validator string
|
||||
Path []string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
if e.CustomErrorMessageExists {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
errName := e.Name
|
||||
if len(e.Path) > 0 {
|
||||
errName = strings.Join(append(e.Path, e.Name), ".")
|
||||
}
|
||||
|
||||
return errName + ": " + e.Err.Error()
|
||||
}
|
||||
100
extra/server-discovery/vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal file
100
extra/server-discovery/vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package govalidator
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Abs returns absolute value of number
|
||||
func Abs(value float64) float64 {
|
||||
return math.Abs(value)
|
||||
}
|
||||
|
||||
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
||||
func Sign(value float64) float64 {
|
||||
if value > 0 {
|
||||
return 1
|
||||
} else if value < 0 {
|
||||
return -1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// IsNegative returns true if value < 0
|
||||
func IsNegative(value float64) bool {
|
||||
return value < 0
|
||||
}
|
||||
|
||||
// IsPositive returns true if value > 0
|
||||
func IsPositive(value float64) bool {
|
||||
return value > 0
|
||||
}
|
||||
|
||||
// IsNonNegative returns true if value >= 0
|
||||
func IsNonNegative(value float64) bool {
|
||||
return value >= 0
|
||||
}
|
||||
|
||||
// IsNonPositive returns true if value <= 0
|
||||
func IsNonPositive(value float64) bool {
|
||||
return value <= 0
|
||||
}
|
||||
|
||||
// InRangeInt returns true if value lies between left and right border
|
||||
func InRangeInt(value, left, right interface{}) bool {
|
||||
value64, _ := ToInt(value)
|
||||
left64, _ := ToInt(left)
|
||||
right64, _ := ToInt(right)
|
||||
if left64 > right64 {
|
||||
left64, right64 = right64, left64
|
||||
}
|
||||
return value64 >= left64 && value64 <= right64
|
||||
}
|
||||
|
||||
// InRangeFloat32 returns true if value lies between left and right border
|
||||
func InRangeFloat32(value, left, right float32) bool {
|
||||
if left > right {
|
||||
left, right = right, left
|
||||
}
|
||||
return value >= left && value <= right
|
||||
}
|
||||
|
||||
// InRangeFloat64 returns true if value lies between left and right border
|
||||
func InRangeFloat64(value, left, right float64) bool {
|
||||
if left > right {
|
||||
left, right = right, left
|
||||
}
|
||||
return value >= left && value <= right
|
||||
}
|
||||
|
||||
// InRange returns true if value lies between left and right border, generic type to handle int, float32, float64 and string.
|
||||
// All types must the same type.
|
||||
// False if value doesn't lie in range or if it incompatible or not comparable
|
||||
func InRange(value interface{}, left interface{}, right interface{}) bool {
|
||||
switch value.(type) {
|
||||
case int:
|
||||
intValue, _ := ToInt(value)
|
||||
intLeft, _ := ToInt(left)
|
||||
intRight, _ := ToInt(right)
|
||||
return InRangeInt(intValue, intLeft, intRight)
|
||||
case float32, float64:
|
||||
intValue, _ := ToFloat(value)
|
||||
intLeft, _ := ToFloat(left)
|
||||
intRight, _ := ToFloat(right)
|
||||
return InRangeFloat64(intValue, intLeft, intRight)
|
||||
case string:
|
||||
return value.(string) >= left.(string) && value.(string) <= right.(string)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsWhole returns true if value is whole number
|
||||
func IsWhole(value float64) bool {
|
||||
return math.Remainder(value, 1) == 0
|
||||
}
|
||||
|
||||
// IsNatural returns true if value is natural number (positive and whole)
|
||||
func IsNatural(value float64) bool {
|
||||
return IsWhole(value) && IsPositive(value)
|
||||
}
|
||||
113
extra/server-discovery/vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal file
113
extra/server-discovery/vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
package govalidator
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Basic regular expressions for validating strings
|
||||
const (
|
||||
Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$"
|
||||
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||
ISBN13 string = "^(?:[0-9]{13})$"
|
||||
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
Alpha string = "^[a-zA-Z]+$"
|
||||
Alphanumeric string = "^[a-zA-Z0-9]+$"
|
||||
Numeric string = "^[0-9]+$"
|
||||
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
|
||||
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
|
||||
Hexadecimal string = "^[0-9a-fA-F]+$"
|
||||
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
||||
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
|
||||
ASCII string = "^[\x00-\x7F]+$"
|
||||
Multibyte string = "[^\x00-\x7F]"
|
||||
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||
PrintableASCII string = "^[\x20-\x7E]+$"
|
||||
DataURI string = "^data:.+\\/(.+);base64$"
|
||||
MagnetURI string = "^magnet:\\?xt=urn:[a-zA-Z0-9]+:[a-zA-Z0-9]{32,40}&dn=.+&tr=.+$"
|
||||
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
|
||||
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
|
||||
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
|
||||
URLUsername string = `(\S+(:\S*)?@)`
|
||||
URLPath string = `((\/|\?|#)[^\s]*)`
|
||||
URLPort string = `(:(\d{1,5}))`
|
||||
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3]|24\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-5]))`
|
||||
URLSubdomain string = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))`
|
||||
URL = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
|
||||
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
||||
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||
UnixPath string = `^(/[^/\x00]*)+/?$`
|
||||
WinARPath string = `^(?:(?:[a-zA-Z]:|\\\\[a-z0-9_.$●-]+\\[a-z0-9_.$●-]+)\\|\\?[^\\/:*?"<>|\r\n]+\\?)(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||
UnixARPath string = `^((\.{0,2}/)?([^/\x00]*))+/?$`
|
||||
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
|
||||
tagName string = "valid"
|
||||
hasLowerCase string = ".*[[:lower:]]"
|
||||
hasUpperCase string = ".*[[:upper:]]"
|
||||
hasWhitespace string = ".*[[:space:]]"
|
||||
hasWhitespaceOnly string = "^[[:space:]]+$"
|
||||
IMEI string = "^[0-9a-f]{14}$|^\\d{15}$|^\\d{18}$"
|
||||
IMSI string = "^\\d{14,15}$"
|
||||
E164 string = `^\+?[1-9]\d{1,14}$`
|
||||
)
|
||||
|
||||
// Used by IsFilePath func
|
||||
const (
|
||||
// Unknown is unresolved OS type
|
||||
Unknown = iota
|
||||
// Win is Windows type
|
||||
Win
|
||||
// Unix is *nix OS types
|
||||
Unix
|
||||
)
|
||||
|
||||
var (
|
||||
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
|
||||
hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$")
|
||||
userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})")
|
||||
rxEmail = regexp.MustCompile(Email)
|
||||
rxCreditCard = regexp.MustCompile(CreditCard)
|
||||
rxISBN10 = regexp.MustCompile(ISBN10)
|
||||
rxISBN13 = regexp.MustCompile(ISBN13)
|
||||
rxUUID3 = regexp.MustCompile(UUID3)
|
||||
rxUUID4 = regexp.MustCompile(UUID4)
|
||||
rxUUID5 = regexp.MustCompile(UUID5)
|
||||
rxUUID = regexp.MustCompile(UUID)
|
||||
rxAlpha = regexp.MustCompile(Alpha)
|
||||
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
|
||||
rxNumeric = regexp.MustCompile(Numeric)
|
||||
rxInt = regexp.MustCompile(Int)
|
||||
rxFloat = regexp.MustCompile(Float)
|
||||
rxHexadecimal = regexp.MustCompile(Hexadecimal)
|
||||
rxHexcolor = regexp.MustCompile(Hexcolor)
|
||||
rxRGBcolor = regexp.MustCompile(RGBcolor)
|
||||
rxASCII = regexp.MustCompile(ASCII)
|
||||
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
|
||||
rxMultibyte = regexp.MustCompile(Multibyte)
|
||||
rxFullWidth = regexp.MustCompile(FullWidth)
|
||||
rxHalfWidth = regexp.MustCompile(HalfWidth)
|
||||
rxBase64 = regexp.MustCompile(Base64)
|
||||
rxDataURI = regexp.MustCompile(DataURI)
|
||||
rxMagnetURI = regexp.MustCompile(MagnetURI)
|
||||
rxLatitude = regexp.MustCompile(Latitude)
|
||||
rxLongitude = regexp.MustCompile(Longitude)
|
||||
rxDNSName = regexp.MustCompile(DNSName)
|
||||
rxURL = regexp.MustCompile(URL)
|
||||
rxSSN = regexp.MustCompile(SSN)
|
||||
rxWinPath = regexp.MustCompile(WinPath)
|
||||
rxUnixPath = regexp.MustCompile(UnixPath)
|
||||
rxARWinPath = regexp.MustCompile(WinARPath)
|
||||
rxARUnixPath = regexp.MustCompile(UnixARPath)
|
||||
rxSemver = regexp.MustCompile(Semver)
|
||||
rxHasLowerCase = regexp.MustCompile(hasLowerCase)
|
||||
rxHasUpperCase = regexp.MustCompile(hasUpperCase)
|
||||
rxHasWhitespace = regexp.MustCompile(hasWhitespace)
|
||||
rxHasWhitespaceOnly = regexp.MustCompile(hasWhitespaceOnly)
|
||||
rxIMEI = regexp.MustCompile(IMEI)
|
||||
rxIMSI = regexp.MustCompile(IMSI)
|
||||
rxE164 = regexp.MustCompile(E164)
|
||||
)
|
||||
656
extra/server-discovery/vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
Normal file
656
extra/server-discovery/vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
package govalidator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||
type Validator func(str string) bool
|
||||
|
||||
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
|
||||
type CustomTypeValidator func(i interface{}, o interface{}) bool
|
||||
|
||||
// ParamValidator is a wrapper for validator functions that accept additional parameters.
|
||||
type ParamValidator func(str string, params ...string) bool
|
||||
|
||||
// InterfaceParamValidator is a wrapper for functions that accept variants parameters for an interface value
|
||||
type InterfaceParamValidator func(in interface{}, params ...string) bool
|
||||
type tagOptionsMap map[string]tagOption
|
||||
|
||||
func (t tagOptionsMap) orderedKeys() []string {
|
||||
var keys []string
|
||||
for k := range t {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
sort.Slice(keys, func(a, b int) bool {
|
||||
return t[keys[a]].order < t[keys[b]].order
|
||||
})
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
type tagOption struct {
|
||||
name string
|
||||
customErrorMessage string
|
||||
order int
|
||||
}
|
||||
|
||||
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||
// It implements the methods to sort by string.
|
||||
type stringValues []reflect.Value
|
||||
|
||||
// InterfaceParamTagMap is a map of functions accept variants parameters for an interface value
|
||||
var InterfaceParamTagMap = map[string]InterfaceParamValidator{
|
||||
"type": IsType,
|
||||
}
|
||||
|
||||
// InterfaceParamTagRegexMap maps interface param tags to their respective regexes.
|
||||
var InterfaceParamTagRegexMap = map[string]*regexp.Regexp{
|
||||
"type": regexp.MustCompile(`^type\((.*)\)$`),
|
||||
}
|
||||
|
||||
// ParamTagMap is a map of functions accept variants parameters
|
||||
var ParamTagMap = map[string]ParamValidator{
|
||||
"length": ByteLength,
|
||||
"range": Range,
|
||||
"runelength": RuneLength,
|
||||
"stringlength": StringLength,
|
||||
"matches": StringMatches,
|
||||
"in": IsInRaw,
|
||||
"rsapub": IsRsaPub,
|
||||
"minstringlength": MinStringLength,
|
||||
"maxstringlength": MaxStringLength,
|
||||
}
|
||||
|
||||
// ParamTagRegexMap maps param tags to their respective regexes.
|
||||
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
||||
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"in": regexp.MustCompile(`^in\((.*)\)`),
|
||||
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
|
||||
"rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"),
|
||||
"minstringlength": regexp.MustCompile("^minstringlength\\((\\d+)\\)$"),
|
||||
"maxstringlength": regexp.MustCompile("^maxstringlength\\((\\d+)\\)$"),
|
||||
}
|
||||
|
||||
type customTypeTagMap struct {
|
||||
validators map[string]CustomTypeValidator
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
|
||||
tm.RLock()
|
||||
defer tm.RUnlock()
|
||||
v, ok := tm.validators[name]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
|
||||
tm.Lock()
|
||||
defer tm.Unlock()
|
||||
tm.validators[name] = ctv
|
||||
}
|
||||
|
||||
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
|
||||
|
||||
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||
var TagMap = map[string]Validator{
|
||||
"email": IsEmail,
|
||||
"url": IsURL,
|
||||
"dialstring": IsDialString,
|
||||
"requrl": IsRequestURL,
|
||||
"requri": IsRequestURI,
|
||||
"alpha": IsAlpha,
|
||||
"utfletter": IsUTFLetter,
|
||||
"alphanum": IsAlphanumeric,
|
||||
"utfletternum": IsUTFLetterNumeric,
|
||||
"numeric": IsNumeric,
|
||||
"utfnumeric": IsUTFNumeric,
|
||||
"utfdigit": IsUTFDigit,
|
||||
"hexadecimal": IsHexadecimal,
|
||||
"hexcolor": IsHexcolor,
|
||||
"rgbcolor": IsRGBcolor,
|
||||
"lowercase": IsLowerCase,
|
||||
"uppercase": IsUpperCase,
|
||||
"int": IsInt,
|
||||
"float": IsFloat,
|
||||
"null": IsNull,
|
||||
"notnull": IsNotNull,
|
||||
"uuid": IsUUID,
|
||||
"uuidv3": IsUUIDv3,
|
||||
"uuidv4": IsUUIDv4,
|
||||
"uuidv5": IsUUIDv5,
|
||||
"creditcard": IsCreditCard,
|
||||
"isbn10": IsISBN10,
|
||||
"isbn13": IsISBN13,
|
||||
"json": IsJSON,
|
||||
"multibyte": IsMultibyte,
|
||||
"ascii": IsASCII,
|
||||
"printableascii": IsPrintableASCII,
|
||||
"fullwidth": IsFullWidth,
|
||||
"halfwidth": IsHalfWidth,
|
||||
"variablewidth": IsVariableWidth,
|
||||
"base64": IsBase64,
|
||||
"datauri": IsDataURI,
|
||||
"ip": IsIP,
|
||||
"port": IsPort,
|
||||
"ipv4": IsIPv4,
|
||||
"ipv6": IsIPv6,
|
||||
"dns": IsDNSName,
|
||||
"host": IsHost,
|
||||
"mac": IsMAC,
|
||||
"latitude": IsLatitude,
|
||||
"longitude": IsLongitude,
|
||||
"ssn": IsSSN,
|
||||
"semver": IsSemver,
|
||||
"rfc3339": IsRFC3339,
|
||||
"rfc3339WithoutZone": IsRFC3339WithoutZone,
|
||||
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||
"ISO4217": IsISO4217,
|
||||
"IMEI": IsIMEI,
|
||||
"ulid": IsULID,
|
||||
}
|
||||
|
||||
// ISO3166Entry stores country codes
|
||||
type ISO3166Entry struct {
|
||||
EnglishShortName string
|
||||
FrenchShortName string
|
||||
Alpha2Code string
|
||||
Alpha3Code string
|
||||
Numeric string
|
||||
}
|
||||
|
||||
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
||||
var ISO3166List = []ISO3166Entry{
|
||||
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
|
||||
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
|
||||
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
|
||||
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
|
||||
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
|
||||
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
|
||||
{"Angola", "Angola (l')", "AO", "AGO", "024"},
|
||||
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
|
||||
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
|
||||
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
|
||||
{"Australia", "Australie (l')", "AU", "AUS", "036"},
|
||||
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
|
||||
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
|
||||
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
|
||||
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
|
||||
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
|
||||
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
|
||||
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
|
||||
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
|
||||
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
|
||||
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
|
||||
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
|
||||
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
|
||||
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
|
||||
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
|
||||
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
|
||||
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
|
||||
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
|
||||
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
|
||||
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
|
||||
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
|
||||
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
|
||||
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
|
||||
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
|
||||
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
|
||||
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
|
||||
{"Canada", "Canada (le)", "CA", "CAN", "124"},
|
||||
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
|
||||
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
|
||||
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
|
||||
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
|
||||
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
|
||||
{"Chile", "Chili (le)", "CL", "CHL", "152"},
|
||||
{"China", "Chine (la)", "CN", "CHN", "156"},
|
||||
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
|
||||
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
|
||||
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
|
||||
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
|
||||
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
|
||||
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
|
||||
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
|
||||
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
|
||||
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
|
||||
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
|
||||
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
|
||||
{"Cuba", "Cuba", "CU", "CUB", "192"},
|
||||
{"Cyprus", "Chypre", "CY", "CYP", "196"},
|
||||
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
|
||||
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
|
||||
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
|
||||
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
|
||||
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
|
||||
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
|
||||
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
|
||||
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
|
||||
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
|
||||
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
|
||||
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
|
||||
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
|
||||
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
|
||||
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
|
||||
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
|
||||
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
|
||||
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
|
||||
{"France", "France (la)", "FR", "FRA", "250"},
|
||||
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
|
||||
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
|
||||
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
|
||||
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
|
||||
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
|
||||
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
|
||||
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
|
||||
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
|
||||
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
|
||||
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
|
||||
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
|
||||
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
|
||||
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
|
||||
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
|
||||
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
|
||||
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
|
||||
{"Guam", "Guam", "GU", "GUM", "316"},
|
||||
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
|
||||
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
|
||||
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
|
||||
{"Haiti", "Haïti", "HT", "HTI", "332"},
|
||||
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
|
||||
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
|
||||
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
|
||||
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
|
||||
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
|
||||
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
|
||||
{"India", "Inde (l')", "IN", "IND", "356"},
|
||||
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
|
||||
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
|
||||
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
|
||||
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
|
||||
{"Israel", "Israël", "IL", "ISR", "376"},
|
||||
{"Italy", "Italie (l')", "IT", "ITA", "380"},
|
||||
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
|
||||
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
|
||||
{"Japan", "Japon (le)", "JP", "JPN", "392"},
|
||||
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
|
||||
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
|
||||
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
|
||||
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
|
||||
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
|
||||
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
|
||||
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
|
||||
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
|
||||
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
|
||||
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
|
||||
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
|
||||
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
|
||||
{"Libya", "Libye (la)", "LY", "LBY", "434"},
|
||||
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
|
||||
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
|
||||
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
|
||||
{"Macao", "Macao", "MO", "MAC", "446"},
|
||||
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
|
||||
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
|
||||
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
|
||||
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
|
||||
{"Mali", "Mali (le)", "ML", "MLI", "466"},
|
||||
{"Malta", "Malte", "MT", "MLT", "470"},
|
||||
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
|
||||
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
|
||||
{"Mauritius", "Maurice", "MU", "MUS", "480"},
|
||||
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
|
||||
{"Monaco", "Monaco", "MC", "MCO", "492"},
|
||||
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
|
||||
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
|
||||
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
|
||||
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
|
||||
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
|
||||
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
|
||||
{"Oman", "Oman", "OM", "OMN", "512"},
|
||||
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
|
||||
{"Nauru", "Nauru", "NR", "NRU", "520"},
|
||||
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
|
||||
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
|
||||
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
|
||||
{"Aruba", "Aruba", "AW", "ABW", "533"},
|
||||
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
|
||||
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
|
||||
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
|
||||
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
|
||||
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
|
||||
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
|
||||
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
|
||||
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
|
||||
{"Niue", "Niue", "NU", "NIU", "570"},
|
||||
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
|
||||
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
|
||||
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
|
||||
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
|
||||
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
|
||||
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
|
||||
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
|
||||
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
|
||||
{"Panama", "Panama (le)", "PA", "PAN", "591"},
|
||||
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
|
||||
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
|
||||
{"Peru", "Pérou (le)", "PE", "PER", "604"},
|
||||
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
|
||||
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
|
||||
{"Poland", "Pologne (la)", "PL", "POL", "616"},
|
||||
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
|
||||
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
|
||||
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
|
||||
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
|
||||
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
|
||||
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
|
||||
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
|
||||
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
|
||||
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
|
||||
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
|
||||
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
|
||||
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
|
||||
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
|
||||
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
|
||||
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
|
||||
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
|
||||
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
|
||||
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
|
||||
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
|
||||
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
|
||||
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
|
||||
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
|
||||
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
|
||||
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
|
||||
{"Singapore", "Singapour", "SG", "SGP", "702"},
|
||||
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
|
||||
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
|
||||
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
|
||||
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
|
||||
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
|
||||
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
|
||||
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
|
||||
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
|
||||
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
|
||||
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
|
||||
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
|
||||
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
|
||||
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
|
||||
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
|
||||
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
|
||||
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
|
||||
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
|
||||
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
|
||||
{"Togo", "Togo (le)", "TG", "TGO", "768"},
|
||||
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
|
||||
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
|
||||
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
|
||||
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
|
||||
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
|
||||
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
|
||||
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
|
||||
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
|
||||
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
|
||||
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
|
||||
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
|
||||
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
|
||||
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
|
||||
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
|
||||
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
|
||||
{"Jersey", "Jersey", "JE", "JEY", "832"},
|
||||
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
|
||||
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
|
||||
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
|
||||
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
|
||||
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
|
||||
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
|
||||
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
|
||||
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
|
||||
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
|
||||
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
|
||||
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
|
||||
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
|
||||
}
|
||||
|
||||
// ISO4217List is the list of ISO currency codes
|
||||
var ISO4217List = []string{
|
||||
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
|
||||
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
|
||||
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
|
||||
"DJF", "DKK", "DOP", "DZD",
|
||||
"EGP", "ERN", "ETB", "EUR",
|
||||
"FJD", "FKP",
|
||||
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
|
||||
"HKD", "HNL", "HRK", "HTG", "HUF",
|
||||
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
|
||||
"JMD", "JOD", "JPY",
|
||||
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
|
||||
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
|
||||
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
|
||||
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
|
||||
"OMR",
|
||||
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
|
||||
"QAR",
|
||||
"RON", "RSD", "RUB", "RWF",
|
||||
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL",
|
||||
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
|
||||
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UYW", "UZS",
|
||||
"VEF", "VES", "VND", "VUV",
|
||||
"WST",
|
||||
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
|
||||
"YER",
|
||||
"ZAR", "ZMW", "ZWL",
|
||||
}
|
||||
|
||||
// ISO693Entry stores ISO language codes
|
||||
type ISO693Entry struct {
|
||||
Alpha3bCode string
|
||||
Alpha2Code string
|
||||
English string
|
||||
}
|
||||
|
||||
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
|
||||
var ISO693List = []ISO693Entry{
|
||||
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
|
||||
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
|
||||
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
|
||||
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
|
||||
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
|
||||
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
|
||||
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
|
||||
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
|
||||
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
|
||||
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
|
||||
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
|
||||
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
|
||||
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
|
||||
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
|
||||
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
|
||||
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
|
||||
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
|
||||
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
|
||||
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
|
||||
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
|
||||
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
|
||||
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
|
||||
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
|
||||
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
|
||||
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
|
||||
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
|
||||
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
|
||||
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
|
||||
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
|
||||
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
|
||||
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
|
||||
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
|
||||
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
|
||||
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
|
||||
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
|
||||
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
|
||||
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
|
||||
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
|
||||
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
|
||||
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
|
||||
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
|
||||
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
|
||||
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
|
||||
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
|
||||
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
|
||||
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
|
||||
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
|
||||
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
|
||||
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
|
||||
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
|
||||
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
|
||||
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
|
||||
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
|
||||
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
|
||||
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
|
||||
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
|
||||
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
|
||||
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
|
||||
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
|
||||
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
|
||||
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
|
||||
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
|
||||
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
|
||||
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
|
||||
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
|
||||
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
|
||||
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
|
||||
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
|
||||
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
|
||||
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
|
||||
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
|
||||
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
|
||||
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
|
||||
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
|
||||
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
|
||||
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
|
||||
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
|
||||
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
|
||||
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
|
||||
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
|
||||
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
|
||||
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
|
||||
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
|
||||
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
|
||||
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
|
||||
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
|
||||
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
|
||||
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
|
||||
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
|
||||
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
|
||||
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
|
||||
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
|
||||
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
|
||||
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
|
||||
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
|
||||
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
|
||||
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
|
||||
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
|
||||
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
|
||||
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
|
||||
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
|
||||
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
|
||||
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
|
||||
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
|
||||
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
|
||||
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
|
||||
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
|
||||
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
|
||||
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
|
||||
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
|
||||
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
|
||||
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
|
||||
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
|
||||
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
|
||||
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
|
||||
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
|
||||
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
|
||||
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
|
||||
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
|
||||
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
|
||||
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
|
||||
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
|
||||
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
|
||||
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
|
||||
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
|
||||
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
|
||||
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
|
||||
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
|
||||
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
|
||||
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
|
||||
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
|
||||
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
|
||||
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
|
||||
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
|
||||
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
|
||||
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
|
||||
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
|
||||
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
|
||||
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
|
||||
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
|
||||
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
|
||||
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
|
||||
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
|
||||
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
|
||||
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
|
||||
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
|
||||
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
|
||||
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
|
||||
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
|
||||
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
|
||||
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
|
||||
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
|
||||
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
|
||||
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
|
||||
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
|
||||
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
|
||||
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
|
||||
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
|
||||
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
|
||||
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
|
||||
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
|
||||
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
|
||||
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
|
||||
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
|
||||
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
|
||||
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
|
||||
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
|
||||
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
|
||||
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
|
||||
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
|
||||
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
|
||||
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
|
||||
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
|
||||
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
|
||||
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
|
||||
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
|
||||
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
|
||||
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
|
||||
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
|
||||
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
|
||||
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
|
||||
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
|
||||
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
|
||||
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
|
||||
}
|
||||
270
extra/server-discovery/vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
Normal file
270
extra/server-discovery/vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
package govalidator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"math"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Contains checks if the string contains the substring.
|
||||
func Contains(str, substring string) bool {
|
||||
return strings.Contains(str, substring)
|
||||
}
|
||||
|
||||
// Matches checks if string matches the pattern (pattern is regular expression)
|
||||
// In case of error return false
|
||||
func Matches(str, pattern string) bool {
|
||||
match, _ := regexp.MatchString(pattern, str)
|
||||
return match
|
||||
}
|
||||
|
||||
// LeftTrim trims characters from the left side of the input.
|
||||
// If second argument is empty, it will remove leading spaces.
|
||||
func LeftTrim(str, chars string) string {
|
||||
if chars == "" {
|
||||
return strings.TrimLeftFunc(str, unicode.IsSpace)
|
||||
}
|
||||
r, _ := regexp.Compile("^[" + chars + "]+")
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// RightTrim trims characters from the right side of the input.
|
||||
// If second argument is empty, it will remove trailing spaces.
|
||||
func RightTrim(str, chars string) string {
|
||||
if chars == "" {
|
||||
return strings.TrimRightFunc(str, unicode.IsSpace)
|
||||
}
|
||||
r, _ := regexp.Compile("[" + chars + "]+$")
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// Trim trims characters from both sides of the input.
|
||||
// If second argument is empty, it will remove spaces.
|
||||
func Trim(str, chars string) string {
|
||||
return LeftTrim(RightTrim(str, chars), chars)
|
||||
}
|
||||
|
||||
// WhiteList removes characters that do not appear in the whitelist.
|
||||
func WhiteList(str, chars string) string {
|
||||
pattern := "[^" + chars + "]+"
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// BlackList removes characters that appear in the blacklist.
|
||||
func BlackList(str, chars string) string {
|
||||
pattern := "[" + chars + "]+"
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// StripLow removes characters with a numerical value < 32 and 127, mostly control characters.
|
||||
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
||||
func StripLow(str string, keepNewLines bool) string {
|
||||
chars := ""
|
||||
if keepNewLines {
|
||||
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
|
||||
} else {
|
||||
chars = "\x00-\x1F\x7F"
|
||||
}
|
||||
return BlackList(str, chars)
|
||||
}
|
||||
|
||||
// ReplacePattern replaces regular expression pattern in string
|
||||
func ReplacePattern(str, pattern, replace string) string {
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return r.ReplaceAllString(str, replace)
|
||||
}
|
||||
|
||||
// Escape replaces <, >, & and " with HTML entities.
|
||||
var Escape = html.EscapeString
|
||||
|
||||
func addSegment(inrune, segment []rune) []rune {
|
||||
if len(segment) == 0 {
|
||||
return inrune
|
||||
}
|
||||
if len(inrune) != 0 {
|
||||
inrune = append(inrune, '_')
|
||||
}
|
||||
inrune = append(inrune, segment...)
|
||||
return inrune
|
||||
}
|
||||
|
||||
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
||||
// Ex.: my_func => MyFunc
|
||||
func UnderscoreToCamelCase(s string) string {
|
||||
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
|
||||
}
|
||||
|
||||
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
||||
// Ex.: MyFunc => my_func
|
||||
func CamelCaseToUnderscore(str string) string {
|
||||
var output []rune
|
||||
var segment []rune
|
||||
for _, r := range str {
|
||||
|
||||
// not treat number as separate segment
|
||||
if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) {
|
||||
output = addSegment(output, segment)
|
||||
segment = nil
|
||||
}
|
||||
segment = append(segment, unicode.ToLower(r))
|
||||
}
|
||||
output = addSegment(output, segment)
|
||||
return string(output)
|
||||
}
|
||||
|
||||
// Reverse returns reversed string
|
||||
func Reverse(s string) string {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// GetLines splits string by "\n" and return array of lines
|
||||
func GetLines(s string) []string {
|
||||
return strings.Split(s, "\n")
|
||||
}
|
||||
|
||||
// GetLine returns specified line of multiline string
|
||||
func GetLine(s string, index int) (string, error) {
|
||||
lines := GetLines(s)
|
||||
if index < 0 || index >= len(lines) {
|
||||
return "", errors.New("line index out of bounds")
|
||||
}
|
||||
return lines[index], nil
|
||||
}
|
||||
|
||||
// RemoveTags removes all tags from HTML string
|
||||
func RemoveTags(s string) string {
|
||||
return ReplacePattern(s, "<[^>]*>", "")
|
||||
}
|
||||
|
||||
// SafeFileName returns safe string that can be used in file names
|
||||
func SafeFileName(str string) string {
|
||||
name := strings.ToLower(str)
|
||||
name = path.Clean(path.Base(name))
|
||||
name = strings.Trim(name, " ")
|
||||
separators, err := regexp.Compile(`[ &_=+:]`)
|
||||
if err == nil {
|
||||
name = separators.ReplaceAllString(name, "-")
|
||||
}
|
||||
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
|
||||
if err == nil {
|
||||
name = legal.ReplaceAllString(name, "")
|
||||
}
|
||||
for strings.Contains(name, "--") {
|
||||
name = strings.Replace(name, "--", "-", -1)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// NormalizeEmail canonicalize an email address.
|
||||
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
||||
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
||||
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
||||
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
||||
// normalized to @gmail.com.
|
||||
func NormalizeEmail(str string) (string, error) {
|
||||
if !IsEmail(str) {
|
||||
return "", fmt.Errorf("%s is not an email", str)
|
||||
}
|
||||
parts := strings.Split(str, "@")
|
||||
parts[0] = strings.ToLower(parts[0])
|
||||
parts[1] = strings.ToLower(parts[1])
|
||||
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
|
||||
parts[1] = "gmail.com"
|
||||
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
|
||||
}
|
||||
return strings.Join(parts, "@"), nil
|
||||
}
|
||||
|
||||
// Truncate a string to the closest length without breaking words.
|
||||
func Truncate(str string, length int, ending string) string {
|
||||
var aftstr, befstr string
|
||||
if len(str) > length {
|
||||
words := strings.Fields(str)
|
||||
before, present := 0, 0
|
||||
for i := range words {
|
||||
befstr = aftstr
|
||||
before = present
|
||||
aftstr = aftstr + words[i] + " "
|
||||
present = len(aftstr)
|
||||
if present > length && i != 0 {
|
||||
if (length - before) < (present - length) {
|
||||
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// PadLeft pads left side of a string if size of string is less then indicated pad length
|
||||
func PadLeft(str string, padStr string, padLen int) string {
|
||||
return buildPadStr(str, padStr, padLen, true, false)
|
||||
}
|
||||
|
||||
// PadRight pads right side of a string if size of string is less then indicated pad length
|
||||
func PadRight(str string, padStr string, padLen int) string {
|
||||
return buildPadStr(str, padStr, padLen, false, true)
|
||||
}
|
||||
|
||||
// PadBoth pads both sides of a string if size of string is less then indicated pad length
|
||||
func PadBoth(str string, padStr string, padLen int) string {
|
||||
return buildPadStr(str, padStr, padLen, true, true)
|
||||
}
|
||||
|
||||
// PadString either left, right or both sides.
|
||||
// Note that padding string can be unicode and more then one character
|
||||
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
|
||||
|
||||
// When padded length is less then the current string size
|
||||
if padLen < utf8.RuneCountInString(str) {
|
||||
return str
|
||||
}
|
||||
|
||||
padLen -= utf8.RuneCountInString(str)
|
||||
|
||||
targetLen := padLen
|
||||
|
||||
targetLenLeft := targetLen
|
||||
targetLenRight := targetLen
|
||||
if padLeft && padRight {
|
||||
targetLenLeft = padLen / 2
|
||||
targetLenRight = padLen - targetLenLeft
|
||||
}
|
||||
|
||||
strToRepeatLen := utf8.RuneCountInString(padStr)
|
||||
|
||||
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
|
||||
repeatedString := strings.Repeat(padStr, repeatTimes)
|
||||
|
||||
leftSide := ""
|
||||
if padLeft {
|
||||
leftSide = repeatedString[0:targetLenLeft]
|
||||
}
|
||||
|
||||
rightSide := ""
|
||||
if padRight {
|
||||
rightSide = repeatedString[0:targetLenRight]
|
||||
}
|
||||
|
||||
return leftSide + str + rightSide
|
||||
}
|
||||
|
||||
// TruncatingErrorf removes extra args from fmt.Errorf if not formatted in the str object
|
||||
func TruncatingErrorf(str string, args ...interface{}) error {
|
||||
n := strings.Count(str, "%s")
|
||||
return fmt.Errorf(str, args[:n]...)
|
||||
}
|
||||
1768
extra/server-discovery/vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
1768
extra/server-discovery/vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
extra/server-discovery/vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
15
extra/server-discovery/vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
box: golang
|
||||
build:
|
||||
steps:
|
||||
- setup-go-workspace
|
||||
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
go version
|
||||
go get -t ./...
|
||||
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test -race -v ./...
|
||||
22
extra/server-discovery/vendor/github.com/aymerick/douceur/LICENSE
generated
vendored
Normal file
22
extra/server-discovery/vendor/github.com/aymerick/douceur/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Aymerick JEHANNE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
60
extra/server-discovery/vendor/github.com/aymerick/douceur/css/declaration.go
generated
vendored
Normal file
60
extra/server-discovery/vendor/github.com/aymerick/douceur/css/declaration.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package css
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Declaration represents a parsed style property
|
||||
type Declaration struct {
|
||||
Property string
|
||||
Value string
|
||||
Important bool
|
||||
}
|
||||
|
||||
// NewDeclaration instanciates a new Declaration
|
||||
func NewDeclaration() *Declaration {
|
||||
return &Declaration{}
|
||||
}
|
||||
|
||||
// Returns string representation of the Declaration
|
||||
func (decl *Declaration) String() string {
|
||||
return decl.StringWithImportant(true)
|
||||
}
|
||||
|
||||
// StringWithImportant returns string representation with optional !important part
|
||||
func (decl *Declaration) StringWithImportant(option bool) string {
|
||||
result := fmt.Sprintf("%s: %s", decl.Property, decl.Value)
|
||||
|
||||
if option && decl.Important {
|
||||
result += " !important"
|
||||
}
|
||||
|
||||
result += ";"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Equal returns true if both Declarations are equals
|
||||
func (decl *Declaration) Equal(other *Declaration) bool {
|
||||
return (decl.Property == other.Property) && (decl.Value == other.Value) && (decl.Important == other.Important)
|
||||
}
|
||||
|
||||
//
|
||||
// DeclarationsByProperty
|
||||
//
|
||||
|
||||
// DeclarationsByProperty represents sortable style declarations
|
||||
type DeclarationsByProperty []*Declaration
|
||||
|
||||
// Implements sort.Interface
|
||||
func (declarations DeclarationsByProperty) Len() int {
|
||||
return len(declarations)
|
||||
}
|
||||
|
||||
// Implements sort.Interface
|
||||
func (declarations DeclarationsByProperty) Swap(i, j int) {
|
||||
declarations[i], declarations[j] = declarations[j], declarations[i]
|
||||
}
|
||||
|
||||
// Implements sort.Interface
|
||||
func (declarations DeclarationsByProperty) Less(i, j int) bool {
|
||||
return declarations[i].Property < declarations[j].Property
|
||||
}
|
||||
230
extra/server-discovery/vendor/github.com/aymerick/douceur/css/rule.go
generated
vendored
Normal file
230
extra/server-discovery/vendor/github.com/aymerick/douceur/css/rule.go
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
package css
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
indentSpace = 2
|
||||
)
|
||||
|
||||
// RuleKind represents a Rule kind
|
||||
type RuleKind int
|
||||
|
||||
// Rule kinds
|
||||
const (
|
||||
QualifiedRule RuleKind = iota
|
||||
AtRule
|
||||
)
|
||||
|
||||
// At Rules than have Rules inside their block instead of Declarations
|
||||
var atRulesWithRulesBlock = []string{
|
||||
"@document", "@font-feature-values", "@keyframes", "@media", "@supports",
|
||||
}
|
||||
|
||||
// Rule represents a parsed CSS rule
|
||||
type Rule struct {
|
||||
Kind RuleKind
|
||||
|
||||
// At Rule name (eg: "@media")
|
||||
Name string
|
||||
|
||||
// Raw prelude
|
||||
Prelude string
|
||||
|
||||
// Qualified Rule selectors parsed from prelude
|
||||
Selectors []string
|
||||
|
||||
// Style properties
|
||||
Declarations []*Declaration
|
||||
|
||||
// At Rule embedded rules
|
||||
Rules []*Rule
|
||||
|
||||
// Current rule embedding level
|
||||
EmbedLevel int
|
||||
}
|
||||
|
||||
// NewRule instanciates a new Rule
|
||||
func NewRule(kind RuleKind) *Rule {
|
||||
return &Rule{
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns string representation of rule kind
|
||||
func (kind RuleKind) String() string {
|
||||
switch kind {
|
||||
case QualifiedRule:
|
||||
return "Qualified Rule"
|
||||
case AtRule:
|
||||
return "At Rule"
|
||||
default:
|
||||
return "WAT"
|
||||
}
|
||||
}
|
||||
|
||||
// EmbedsRules returns true if this rule embeds another rules
|
||||
func (rule *Rule) EmbedsRules() bool {
|
||||
if rule.Kind == AtRule {
|
||||
for _, atRuleName := range atRulesWithRulesBlock {
|
||||
if rule.Name == atRuleName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Equal returns true if both rules are equals
|
||||
func (rule *Rule) Equal(other *Rule) bool {
|
||||
if (rule.Kind != other.Kind) ||
|
||||
(rule.Prelude != other.Prelude) ||
|
||||
(rule.Name != other.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (len(rule.Selectors) != len(other.Selectors)) ||
|
||||
(len(rule.Declarations) != len(other.Declarations)) ||
|
||||
(len(rule.Rules) != len(other.Rules)) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, sel := range rule.Selectors {
|
||||
if sel != other.Selectors[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i, decl := range rule.Declarations {
|
||||
if !decl.Equal(other.Declarations[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i, rule := range rule.Rules {
|
||||
if !rule.Equal(other.Rules[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Diff returns a string representation of rules differences
|
||||
func (rule *Rule) Diff(other *Rule) []string {
|
||||
result := []string{}
|
||||
|
||||
if rule.Kind != other.Kind {
|
||||
result = append(result, fmt.Sprintf("Kind: %s | %s", rule.Kind.String(), other.Kind.String()))
|
||||
}
|
||||
|
||||
if rule.Prelude != other.Prelude {
|
||||
result = append(result, fmt.Sprintf("Prelude: \"%s\" | \"%s\"", rule.Prelude, other.Prelude))
|
||||
}
|
||||
|
||||
if rule.Name != other.Name {
|
||||
result = append(result, fmt.Sprintf("Name: \"%s\" | \"%s\"", rule.Name, other.Name))
|
||||
}
|
||||
|
||||
if len(rule.Selectors) != len(other.Selectors) {
|
||||
result = append(result, fmt.Sprintf("Selectors: %v | %v", strings.Join(rule.Selectors, ", "), strings.Join(other.Selectors, ", ")))
|
||||
} else {
|
||||
for i, sel := range rule.Selectors {
|
||||
if sel != other.Selectors[i] {
|
||||
result = append(result, fmt.Sprintf("Selector: \"%s\" | \"%s\"", sel, other.Selectors[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Declarations) != len(other.Declarations) {
|
||||
result = append(result, fmt.Sprintf("Declarations Nb: %d | %d", len(rule.Declarations), len(other.Declarations)))
|
||||
} else {
|
||||
for i, decl := range rule.Declarations {
|
||||
if !decl.Equal(other.Declarations[i]) {
|
||||
result = append(result, fmt.Sprintf("Declaration: \"%s\" | \"%s\"", decl.String(), other.Declarations[i].String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Rules) != len(other.Rules) {
|
||||
result = append(result, fmt.Sprintf("Rules Nb: %d | %d", len(rule.Rules), len(other.Rules)))
|
||||
} else {
|
||||
|
||||
for i, rule := range rule.Rules {
|
||||
if !rule.Equal(other.Rules[i]) {
|
||||
result = append(result, fmt.Sprintf("Rule: \"%s\" | \"%s\"", rule.String(), other.Rules[i].String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the string representation of a rule
|
||||
func (rule *Rule) String() string {
|
||||
result := ""
|
||||
|
||||
if rule.Kind == QualifiedRule {
|
||||
for i, sel := range rule.Selectors {
|
||||
if i != 0 {
|
||||
result += ", "
|
||||
}
|
||||
result += sel
|
||||
}
|
||||
} else {
|
||||
// AtRule
|
||||
result += fmt.Sprintf("%s", rule.Name)
|
||||
|
||||
if rule.Prelude != "" {
|
||||
if result != "" {
|
||||
result += " "
|
||||
}
|
||||
result += fmt.Sprintf("%s", rule.Prelude)
|
||||
}
|
||||
}
|
||||
|
||||
if (len(rule.Declarations) == 0) && (len(rule.Rules) == 0) {
|
||||
result += ";"
|
||||
} else {
|
||||
result += " {\n"
|
||||
|
||||
if rule.EmbedsRules() {
|
||||
for _, subRule := range rule.Rules {
|
||||
result += fmt.Sprintf("%s%s\n", rule.indent(), subRule.String())
|
||||
}
|
||||
} else {
|
||||
for _, decl := range rule.Declarations {
|
||||
result += fmt.Sprintf("%s%s\n", rule.indent(), decl.String())
|
||||
}
|
||||
}
|
||||
|
||||
result += fmt.Sprintf("%s}", rule.indentEndBlock())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns identation spaces for declarations and rules
|
||||
func (rule *Rule) indent() string {
|
||||
result := ""
|
||||
|
||||
for i := 0; i < ((rule.EmbedLevel + 1) * indentSpace); i++ {
|
||||
result += " "
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns identation spaces for end of block character
|
||||
func (rule *Rule) indentEndBlock() string {
|
||||
result := ""
|
||||
|
||||
for i := 0; i < (rule.EmbedLevel * indentSpace); i++ {
|
||||
result += " "
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
25
extra/server-discovery/vendor/github.com/aymerick/douceur/css/stylesheet.go
generated
vendored
Normal file
25
extra/server-discovery/vendor/github.com/aymerick/douceur/css/stylesheet.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package css
|
||||
|
||||
// Stylesheet represents a parsed stylesheet
|
||||
type Stylesheet struct {
|
||||
Rules []*Rule
|
||||
}
|
||||
|
||||
// NewStylesheet instanciate a new Stylesheet
|
||||
func NewStylesheet() *Stylesheet {
|
||||
return &Stylesheet{}
|
||||
}
|
||||
|
||||
// Returns string representation of the Stylesheet
|
||||
func (sheet *Stylesheet) String() string {
|
||||
result := ""
|
||||
|
||||
for _, rule := range sheet.Rules {
|
||||
if result != "" {
|
||||
result += "\n"
|
||||
}
|
||||
result += rule.String()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
409
extra/server-discovery/vendor/github.com/aymerick/douceur/parser/parser.go
generated
vendored
Normal file
409
extra/server-discovery/vendor/github.com/aymerick/douceur/parser/parser.go
generated
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/css/scanner"
|
||||
|
||||
"github.com/aymerick/douceur/css"
|
||||
)
|
||||
|
||||
const (
|
||||
importantSuffixRegexp = `(?i)\s*!important\s*$`
|
||||
)
|
||||
|
||||
var (
|
||||
importantRegexp *regexp.Regexp
|
||||
)
|
||||
|
||||
// Parser represents a CSS parser
|
||||
type Parser struct {
|
||||
scan *scanner.Scanner // Tokenizer
|
||||
|
||||
// Tokens parsed but not consumed yet
|
||||
tokens []*scanner.Token
|
||||
|
||||
// Rule embedding level
|
||||
embedLevel int
|
||||
}
|
||||
|
||||
func init() {
|
||||
importantRegexp = regexp.MustCompile(importantSuffixRegexp)
|
||||
}
|
||||
|
||||
// NewParser instanciates a new parser
|
||||
func NewParser(txt string) *Parser {
|
||||
return &Parser{
|
||||
scan: scanner.New(txt),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses a whole stylesheet
|
||||
func Parse(text string) (*css.Stylesheet, error) {
|
||||
result, err := NewParser(text).ParseStylesheet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseDeclarations parses CSS declarations
|
||||
func ParseDeclarations(text string) ([]*css.Declaration, error) {
|
||||
result, err := NewParser(text).ParseDeclarations()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseStylesheet parses a stylesheet
|
||||
func (parser *Parser) ParseStylesheet() (*css.Stylesheet, error) {
|
||||
result := css.NewStylesheet()
|
||||
|
||||
// Parse BOM
|
||||
if _, err := parser.parseBOM(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Parse list of rules
|
||||
rules, err := parser.ParseRules()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Rules = rules
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseRules parses a list of rules
|
||||
func (parser *Parser) ParseRules() ([]*css.Rule, error) {
|
||||
result := []*css.Rule{}
|
||||
|
||||
inBlock := false
|
||||
if parser.tokenChar("{") {
|
||||
// parsing a block of rules
|
||||
inBlock = true
|
||||
parser.embedLevel++
|
||||
|
||||
parser.shiftToken()
|
||||
}
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenIgnorable() {
|
||||
parser.shiftToken()
|
||||
} else if parser.tokenChar("}") {
|
||||
if !inBlock {
|
||||
errMsg := fmt.Sprintf("Unexpected } character: %s", parser.nextToken().String())
|
||||
return result, errors.New(errMsg)
|
||||
}
|
||||
|
||||
parser.shiftToken()
|
||||
parser.embedLevel--
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
rule, err := parser.ParseRule()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
rule.EmbedLevel = parser.embedLevel
|
||||
result = append(result, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// ParseRule parses a rule
|
||||
func (parser *Parser) ParseRule() (*css.Rule, error) {
|
||||
if parser.tokenAtKeyword() {
|
||||
return parser.parseAtRule()
|
||||
}
|
||||
|
||||
return parser.parseQualifiedRule()
|
||||
}
|
||||
|
||||
// ParseDeclarations parses a list of declarations
|
||||
func (parser *Parser) ParseDeclarations() ([]*css.Declaration, error) {
|
||||
result := []*css.Declaration{}
|
||||
|
||||
if parser.tokenChar("{") {
|
||||
parser.shiftToken()
|
||||
}
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenIgnorable() {
|
||||
parser.shiftToken()
|
||||
} else if parser.tokenChar("}") {
|
||||
// end of block
|
||||
parser.shiftToken()
|
||||
break
|
||||
} else {
|
||||
declaration, err := parser.ParseDeclaration()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result = append(result, declaration)
|
||||
}
|
||||
}
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// ParseDeclaration parses a declaration
|
||||
func (parser *Parser) ParseDeclaration() (*css.Declaration, error) {
|
||||
result := css.NewDeclaration()
|
||||
curValue := ""
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenChar(":") {
|
||||
result.Property = strings.TrimSpace(curValue)
|
||||
curValue = ""
|
||||
|
||||
parser.shiftToken()
|
||||
} else if parser.tokenChar(";") || parser.tokenChar("}") {
|
||||
if result.Property == "" {
|
||||
errMsg := fmt.Sprintf("Unexpected ; character: %s", parser.nextToken().String())
|
||||
return result, errors.New(errMsg)
|
||||
}
|
||||
|
||||
if importantRegexp.MatchString(curValue) {
|
||||
result.Important = true
|
||||
curValue = importantRegexp.ReplaceAllString(curValue, "")
|
||||
}
|
||||
|
||||
result.Value = strings.TrimSpace(curValue)
|
||||
|
||||
if parser.tokenChar(";") {
|
||||
parser.shiftToken()
|
||||
}
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
token := parser.shiftToken()
|
||||
curValue += token.Value
|
||||
}
|
||||
}
|
||||
|
||||
// log.Printf("[parsed] Declaration: %s", result.String())
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse an At Rule
|
||||
func (parser *Parser) parseAtRule() (*css.Rule, error) {
|
||||
// parse rule name (eg: "@import")
|
||||
token := parser.shiftToken()
|
||||
|
||||
result := css.NewRule(css.AtRule)
|
||||
result.Name = token.Value
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenChar(";") {
|
||||
parser.shiftToken()
|
||||
|
||||
// finished
|
||||
break
|
||||
} else if parser.tokenChar("{") {
|
||||
if result.EmbedsRules() {
|
||||
// parse rules block
|
||||
rules, err := parser.ParseRules()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Rules = rules
|
||||
} else {
|
||||
// parse declarations block
|
||||
declarations, err := parser.ParseDeclarations()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Declarations = declarations
|
||||
}
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
// parse prelude
|
||||
prelude, err := parser.parsePrelude()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Prelude = prelude
|
||||
}
|
||||
}
|
||||
|
||||
// log.Printf("[parsed] Rule: %s", result.String())
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse a Qualified Rule
|
||||
func (parser *Parser) parseQualifiedRule() (*css.Rule, error) {
|
||||
result := css.NewRule(css.QualifiedRule)
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenChar("{") {
|
||||
if result.Prelude == "" {
|
||||
errMsg := fmt.Sprintf("Unexpected { character: %s", parser.nextToken().String())
|
||||
return result, errors.New(errMsg)
|
||||
}
|
||||
|
||||
// parse declarations block
|
||||
declarations, err := parser.ParseDeclarations()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Declarations = declarations
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
// parse prelude
|
||||
prelude, err := parser.parsePrelude()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Prelude = prelude
|
||||
}
|
||||
}
|
||||
|
||||
result.Selectors = strings.Split(result.Prelude, ",")
|
||||
for i, sel := range result.Selectors {
|
||||
result.Selectors[i] = strings.TrimSpace(sel)
|
||||
}
|
||||
|
||||
// log.Printf("[parsed] Rule: %s", result.String())
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse Rule prelude
|
||||
func (parser *Parser) parsePrelude() (string, error) {
|
||||
result := ""
|
||||
|
||||
for parser.tokenParsable() && !parser.tokenEndOfPrelude() {
|
||||
token := parser.shiftToken()
|
||||
result += token.Value
|
||||
}
|
||||
|
||||
result = strings.TrimSpace(result)
|
||||
|
||||
// log.Printf("[parsed] prelude: %s", result)
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse BOM
|
||||
func (parser *Parser) parseBOM() (bool, error) {
|
||||
if parser.nextToken().Type == scanner.TokenBOM {
|
||||
parser.shiftToken()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, parser.err()
|
||||
}
|
||||
|
||||
// Returns next token without removing it from tokens buffer
|
||||
func (parser *Parser) nextToken() *scanner.Token {
|
||||
if len(parser.tokens) == 0 {
|
||||
// fetch next token
|
||||
nextToken := parser.scan.Next()
|
||||
|
||||
// log.Printf("[token] %s => %v", nextToken.Type.String(), nextToken.Value)
|
||||
|
||||
// queue it
|
||||
parser.tokens = append(parser.tokens, nextToken)
|
||||
}
|
||||
|
||||
return parser.tokens[0]
|
||||
}
|
||||
|
||||
// Returns next token and remove it from the tokens buffer
|
||||
func (parser *Parser) shiftToken() *scanner.Token {
|
||||
var result *scanner.Token
|
||||
|
||||
result, parser.tokens = parser.tokens[0], parser.tokens[1:]
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns tokenizer error, or nil if no error
|
||||
func (parser *Parser) err() error {
|
||||
if parser.tokenError() {
|
||||
token := parser.nextToken()
|
||||
return fmt.Errorf("Tokenizer error: %s", token.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true if next token is Error
|
||||
func (parser *Parser) tokenError() bool {
|
||||
return parser.nextToken().Type == scanner.TokenError
|
||||
}
|
||||
|
||||
// Returns true if next token is EOF
|
||||
func (parser *Parser) tokenEOF() bool {
|
||||
return parser.nextToken().Type == scanner.TokenEOF
|
||||
}
|
||||
|
||||
// Returns true if next token is a whitespace
|
||||
func (parser *Parser) tokenWS() bool {
|
||||
return parser.nextToken().Type == scanner.TokenS
|
||||
}
|
||||
|
||||
// Returns true if next token is a comment
|
||||
func (parser *Parser) tokenComment() bool {
|
||||
return parser.nextToken().Type == scanner.TokenComment
|
||||
}
|
||||
|
||||
// Returns true if next token is a CDO or a CDC
|
||||
func (parser *Parser) tokenCDOorCDC() bool {
|
||||
switch parser.nextToken().Type {
|
||||
case scanner.TokenCDO, scanner.TokenCDC:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if next token is ignorable
|
||||
func (parser *Parser) tokenIgnorable() bool {
|
||||
return parser.tokenWS() || parser.tokenComment() || parser.tokenCDOorCDC()
|
||||
}
|
||||
|
||||
// Returns true if next token is parsable
|
||||
func (parser *Parser) tokenParsable() bool {
|
||||
return !parser.tokenEOF() && !parser.tokenError()
|
||||
}
|
||||
|
||||
// Returns true if next token is an At Rule keyword
|
||||
func (parser *Parser) tokenAtKeyword() bool {
|
||||
return parser.nextToken().Type == scanner.TokenAtKeyword
|
||||
}
|
||||
|
||||
// Returns true if next token is given character
|
||||
func (parser *Parser) tokenChar(value string) bool {
|
||||
token := parser.nextToken()
|
||||
return (token.Type == scanner.TokenChar) && (token.Value == value)
|
||||
}
|
||||
|
||||
// Returns true if next token marks the end of a prelude
|
||||
func (parser *Parser) tokenEndOfPrelude() bool {
|
||||
return parser.tokenChar(";") || parser.tokenChar("{")
|
||||
}
|
||||
117
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/attachment.go
generated
vendored
Normal file
117
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/attachment.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
Attachment struct {
|
||||
ID uint64 `json:"attachmentID,string"`
|
||||
OwnerID uint64 `json:"ownerID,string"`
|
||||
Kind string `json:"-"`
|
||||
Url string `json:"url,omitempty"`
|
||||
PreviewUrl string `json:"previewUrl,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Meta AttachmentMeta `json:"meta"`
|
||||
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
// AttachmentFilter is used for filtering and as a return value from Find
|
||||
AttachmentFilter struct {
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
PageID uint64 `json:"pageID,string,omitempty"`
|
||||
RecordID uint64 `json:"recordID,string,omitempty"`
|
||||
ModuleID uint64 `json:"moduleID,string,omitempty"`
|
||||
FieldName string `json:"fieldName,omitempty"`
|
||||
Filter string `json:"filter"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*Attachment) (bool, error)
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
|
||||
AttachmentImageMeta struct {
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
Animated bool `json:"animated"`
|
||||
}
|
||||
|
||||
AttachmentFileMeta struct {
|
||||
Size int64 `json:"size"`
|
||||
Extension string `json:"ext"`
|
||||
Mimetype string `json:"mimetype"`
|
||||
Image *AttachmentImageMeta `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
AttachmentIconMeta struct {
|
||||
Name string `json:"name"`
|
||||
Library string `json:"library"`
|
||||
}
|
||||
|
||||
AttachmentIconSvgMeta struct {
|
||||
Src string `json:"src"`
|
||||
}
|
||||
|
||||
AttachmentMeta struct {
|
||||
Original AttachmentFileMeta `json:"original"`
|
||||
Preview *AttachmentFileMeta `json:"preview,omitempty"`
|
||||
|
||||
Icon *AttachmentIconMeta `json:"icon,omitempty"`
|
||||
IconSvg *AttachmentIconSvgMeta `json:"iconSvg,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
PageAttachment string = "page"
|
||||
IconAttachment string = "icon"
|
||||
RecordAttachment string = "record"
|
||||
NamespaceAttachment string = "namespace"
|
||||
)
|
||||
|
||||
func (a *Attachment) SetOriginalImageMeta(width, height int, animated bool) *AttachmentFileMeta {
|
||||
a.imageMeta(&a.Meta.Original, width, height, animated)
|
||||
return &a.Meta.Original
|
||||
}
|
||||
|
||||
func (a *Attachment) SetPreviewImageMeta(width, height int, animated bool) *AttachmentFileMeta {
|
||||
if a.Meta.Preview == nil {
|
||||
a.Meta.Preview = &AttachmentFileMeta{}
|
||||
}
|
||||
|
||||
a.imageMeta(a.Meta.Preview, width, height, animated)
|
||||
return a.Meta.Preview
|
||||
}
|
||||
|
||||
func (a *Attachment) imageMeta(in *AttachmentFileMeta, width, height int, animated bool) {
|
||||
if in.Image == nil {
|
||||
in.Image = &AttachmentImageMeta{}
|
||||
}
|
||||
|
||||
if width > 0 && height > 0 {
|
||||
in.Image.Animated = animated
|
||||
in.Image.Width = width
|
||||
in.Image.Height = height
|
||||
}
|
||||
}
|
||||
|
||||
func (meta *AttachmentMeta) Scan(src any) error { return sql.ParseJSON(src, meta) }
|
||||
func (meta AttachmentMeta) Value() (driver.Value, error) { return json.Marshal(meta) }
|
||||
312
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/chart.go
generated
vendored
Normal file
312
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/chart.go
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
Chart struct {
|
||||
ID uint64 `json:"chartID,string"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
Config ChartConfig `json:"config"`
|
||||
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
ChartConfig struct {
|
||||
Reports []*ChartConfigReport `json:"reports,omitempty"`
|
||||
ColorScheme string `json:"colorScheme,omitempty"`
|
||||
NoAnimation bool `json:"noAnimation,omitempty"`
|
||||
Toolbox map[string]interface{} `json:"toolbox,omitempty"`
|
||||
}
|
||||
|
||||
ChartConfigReport struct {
|
||||
ReportID uint64 `json:"reportID,string,omitempty"`
|
||||
Filter string `json:"filter"`
|
||||
ModuleID uint64 `json:"moduleID,string,omitempty"`
|
||||
Metrics []map[string]interface{} `json:"metrics,omitempty"`
|
||||
Dimensions []map[string]interface{} `json:"dimensions,omitempty"`
|
||||
YAxis map[string]interface{} `json:"yAxis,omitempty"`
|
||||
Legend map[string]interface{} `json:"legend,omitempty"`
|
||||
Tooltip map[string]interface{} `json:"tooltip,omitempty"`
|
||||
Offset map[string]interface{} `json:"offset,omitempty"`
|
||||
Renderer struct {
|
||||
Version string `json:"version,omitempty" `
|
||||
} `json:"renderer,omitempty"`
|
||||
}
|
||||
|
||||
ChartFilter struct {
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
ChartID []string `json:"chartID"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
|
||||
LabeledIDs []uint64 `json:"-"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*Chart) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
|
||||
func (c Chart) decodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
for i, report := range c.Config.Reports {
|
||||
if report == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// apply translated label for YAxis
|
||||
if aux = tt.FindByKey(LocaleKeyChartYAxisLabel.Path); aux != nil {
|
||||
if c.Config.Reports[i].YAxis == nil {
|
||||
c.Config.Reports[i].YAxis = make(map[string]interface{})
|
||||
}
|
||||
|
||||
c.Config.Reports[i].YAxis["label"] = aux.Msg
|
||||
}
|
||||
|
||||
// apply translated labels for metrics
|
||||
report.WalkMetrics(func(metricID string, metric map[string]interface{}) {
|
||||
mpl := strings.NewReplacer("{{metricID}}", metricID)
|
||||
|
||||
aux = tt.FindByKey(mpl.Replace(LocaleKeyChartMetricsMetricIDLabel.Path))
|
||||
if aux == nil {
|
||||
return
|
||||
}
|
||||
|
||||
metric["label"] = aux.Msg
|
||||
})
|
||||
|
||||
// apply translated labels for each dimension/step
|
||||
report.WalkDimensionSteps(func(dimensionID, stepID string, step map[string]interface{}) {
|
||||
mpl := strings.NewReplacer(
|
||||
"{{dimensionID}}", dimensionID,
|
||||
"{{stepID}}", stepID,
|
||||
)
|
||||
|
||||
aux = tt.FindByKey(mpl.Replace(LocaleKeyChartDimensionsDimensionIDMetaStepsStepIDLabel.Path))
|
||||
if aux == nil {
|
||||
return
|
||||
}
|
||||
|
||||
step["label"] = aux.Msg
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c Chart) encodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = make(locale.ResourceTranslationSet, 0, 12)
|
||||
|
||||
for _, report := range c.Config.Reports {
|
||||
// collect labels from chart config: YAxis
|
||||
if _, ok := report.YAxis["label"]; ok {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: c.ResourceTranslation(),
|
||||
Key: LocaleKeyChartYAxisLabel.Path,
|
||||
Msg: cast.ToString(report.YAxis["label"]),
|
||||
})
|
||||
}
|
||||
|
||||
// collect labels from chart config: metrics
|
||||
report.WalkMetrics(func(metricID string, m map[string]interface{}) {
|
||||
mpl := strings.NewReplacer(
|
||||
"{{metricID}}", metricID,
|
||||
)
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: c.ResourceTranslation(),
|
||||
Key: mpl.Replace(LocaleKeyChartMetricsMetricIDLabel.Path),
|
||||
Msg: cast.ToString(m["label"]),
|
||||
})
|
||||
})
|
||||
|
||||
// collect labels from chart config: dimensions/steps
|
||||
report.WalkDimensionSteps(func(dimID, stepID string, step map[string]interface{}) {
|
||||
mpl := strings.NewReplacer(
|
||||
"{{dimensionID}}", dimID,
|
||||
"{{stepID}}", stepID,
|
||||
)
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: c.ResourceTranslation(),
|
||||
Key: mpl.Replace(LocaleKeyChartDimensionsDimensionIDMetaStepsStepIDLabel.Path),
|
||||
Msg: cast.ToString(step["label"]),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Chart) setValue(name string, pos uint, value any) (err error) {
|
||||
pp := strings.Split(name, ".")
|
||||
|
||||
switch pp[0] {
|
||||
case "Config":
|
||||
if pp[1] == "Reports" {
|
||||
return c.Config.Reports[cast.ToInt(pp[2])].setValue(pp[3], 0, value)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *ChartConfigReport) setValue(name string, pos uint, value any) (err error) {
|
||||
switch name {
|
||||
case "moduleID", "ModuleID":
|
||||
r.ModuleID = cast.ToUint64(value)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByHandle finds chart by it's handle
|
||||
func (set ChartSet) FindByHandle(handle string) *Chart {
|
||||
for i := range set {
|
||||
if set[i].Handle == handle {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cc *ChartConfig) Scan(src any) error { return sql.ParseJSON(src, cc) }
|
||||
func (cc ChartConfig) Value() (driver.Value, error) { return json.Marshal(cc) }
|
||||
|
||||
func (r *ChartConfigReport) WalkMetrics(fn func(string, map[string]interface{})) {
|
||||
for m := range r.Metrics {
|
||||
metricID, ok := r.Metrics[m]["metricID"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(r.Metrics[m]) == 0 {
|
||||
// avoid problems with nil maps
|
||||
r.Metrics[m] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
fn(metricID.(string), r.Metrics[m])
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ChartConfigReport) WalkDimensionSteps(fn func(string, string, map[string]interface{})) {
|
||||
for d := range r.Dimensions {
|
||||
dimensionID, ok := r.Dimensions[d]["dimensionID"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
meta, is := r.Dimensions[d]["meta"].(map[string]interface{})
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
var steps []map[string]interface{}
|
||||
|
||||
switch aux := meta["steps"].(type) {
|
||||
case []interface{}:
|
||||
for _, i := range aux {
|
||||
if kv, is := i.(map[string]interface{}); is {
|
||||
steps = append(steps, kv)
|
||||
}
|
||||
}
|
||||
case []map[string]interface{}:
|
||||
steps = aux
|
||||
}
|
||||
|
||||
for s := range steps {
|
||||
stepID, has := steps[s]["stepID"]
|
||||
if !has {
|
||||
return
|
||||
}
|
||||
|
||||
fn(dimensionID.(string), stepID.(string), steps[s])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChartConfig) GenerateIDs(nextID func() uint64) {
|
||||
// Ensure chart report IDs
|
||||
for r := range c.Reports {
|
||||
c.Reports[r].ReportID = nextID()
|
||||
|
||||
// Ensure chart report metric IDs
|
||||
for m := range c.Reports[r].Metrics {
|
||||
met := c.Reports[r].Metrics[m]
|
||||
if _, has := met["metricID"]; has {
|
||||
continue
|
||||
}
|
||||
|
||||
met["metricID"] = strconv.FormatUint(nextID(), 10)
|
||||
}
|
||||
|
||||
for d := range c.Reports[r].Dimensions {
|
||||
dim := c.Reports[r].Dimensions[d]
|
||||
|
||||
if _, has := dim["dimensionID"]; !has {
|
||||
dim["dimensionID"] = strconv.FormatUint(nextID(), 10)
|
||||
}
|
||||
|
||||
meta, is := dim["meta"].(map[string]interface{})
|
||||
if !is {
|
||||
// no meta, no steps
|
||||
continue
|
||||
}
|
||||
|
||||
var steps []map[string]interface{}
|
||||
|
||||
switch aux := meta["steps"].(type) {
|
||||
case []interface{}:
|
||||
for _, i := range aux {
|
||||
if kv, is := i.(map[string]interface{}); is {
|
||||
steps = append(steps, kv)
|
||||
}
|
||||
}
|
||||
case []map[string]interface{}:
|
||||
steps = aux
|
||||
}
|
||||
|
||||
for s := range steps {
|
||||
_, has := steps[s]["stepID"]
|
||||
if has {
|
||||
continue
|
||||
}
|
||||
|
||||
steps[s]["stepID"] = strconv.FormatUint(nextID(), 10)
|
||||
}
|
||||
|
||||
meta["steps"] = steps
|
||||
dim["meta"] = meta
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
38
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/data_privacy.go
generated
vendored
Normal file
38
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/data_privacy.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
sysTypes "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
PrivacyModule struct {
|
||||
Module PrivacyModuleMeta `json:"module"`
|
||||
Namespace PrivacyNamespaceMeta `json:"namespace"`
|
||||
|
||||
ConnectionID uint64 `json:"-"`
|
||||
Connection *sysTypes.DalConnection `json:"connection"`
|
||||
}
|
||||
|
||||
PrivacyModuleMeta struct {
|
||||
ID uint64 `json:"moduleID,string"`
|
||||
Name string `json:"name"`
|
||||
Handle string `json:"handle"`
|
||||
Fields ModuleFieldSet `json:"fields"`
|
||||
}
|
||||
|
||||
PrivacyNamespaceMeta struct {
|
||||
ID uint64 `json:"namespaceID,string"`
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
PrivacyModuleFilter struct {
|
||||
NamespaceID uint64 `json:"-"`
|
||||
ConnectionID []string `json:"connectionID"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
528
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/getters_setters.gen.go
generated
vendored
Normal file
528
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/getters_setters.gen.go
generated
vendored
Normal file
@@ -0,0 +1,528 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/cast2"
|
||||
)
|
||||
|
||||
func (r Attachment) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Attachment) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "kind", "Kind":
|
||||
return r.Kind, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return r.NamespaceID, nil
|
||||
case "ownerID", "OwnerID":
|
||||
return r.OwnerID, nil
|
||||
case "previewUrl", "PreviewUrl":
|
||||
return r.PreviewUrl, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "url", "Url":
|
||||
return r.Url, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Attachment) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Attachment{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "kind", "Kind":
|
||||
return cast2.String(value, &r.Kind)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "namespaceID", "NamespaceID":
|
||||
return cast2.Uint64(value, &r.NamespaceID)
|
||||
case "ownerID", "OwnerID":
|
||||
return cast2.Uint64(value, &r.OwnerID)
|
||||
case "previewUrl", "PreviewUrl":
|
||||
return cast2.String(value, &r.PreviewUrl)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "url", "Url":
|
||||
return cast2.String(value, &r.Url)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Chart) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Chart) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "handle", "Handle":
|
||||
return r.Handle, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return r.NamespaceID, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Chart) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Chart{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "handle", "Handle":
|
||||
return cast2.String(value, &r.Handle)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "namespaceID", "NamespaceID":
|
||||
return cast2.Uint64(value, &r.NamespaceID)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
|
||||
default:
|
||||
return r.setValue(name, pos, value)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Module) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Module) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "handle", "Handle":
|
||||
return r.Handle, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return r.NamespaceID, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Module) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Module{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "handle", "Handle":
|
||||
return cast2.String(value, &r.Handle)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "namespaceID", "NamespaceID":
|
||||
return cast2.Uint64(value, &r.NamespaceID)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ModuleField) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *ModuleField) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "kind", "Kind":
|
||||
return r.Kind, nil
|
||||
case "label", "Label":
|
||||
return r.Label, nil
|
||||
case "moduleID", "ModuleID":
|
||||
return r.ModuleID, nil
|
||||
case "multi", "Multi":
|
||||
return r.Multi, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "place", "Place":
|
||||
return r.Place, nil
|
||||
case "required", "Required":
|
||||
return r.Required, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *ModuleField) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &ModuleField{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "kind", "Kind":
|
||||
return cast2.String(value, &r.Kind)
|
||||
case "label", "Label":
|
||||
return cast2.String(value, &r.Label)
|
||||
case "moduleID", "ModuleID":
|
||||
return cast2.Uint64(value, &r.ModuleID)
|
||||
case "multi", "Multi":
|
||||
return cast2.Bool(value, &r.Multi)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "place", "Place":
|
||||
return cast2.Int(value, &r.Place)
|
||||
case "required", "Required":
|
||||
return cast2.Bool(value, &r.Required)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
|
||||
default:
|
||||
return r.setValue(name, pos, value)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Namespace) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Namespace) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "enabled", "Enabled":
|
||||
return r.Enabled, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "slug", "Slug":
|
||||
return r.Slug, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Namespace) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Namespace{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "enabled", "Enabled":
|
||||
return cast2.Bool(value, &r.Enabled)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "slug", "Slug":
|
||||
return cast2.String(value, &r.Slug)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Page) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Page) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "description", "Description":
|
||||
return r.Description, nil
|
||||
case "handle", "Handle":
|
||||
return r.Handle, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "moduleID", "ModuleID":
|
||||
return r.ModuleID, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return r.NamespaceID, nil
|
||||
case "selfID", "SelfID":
|
||||
return r.SelfID, nil
|
||||
case "title", "Title":
|
||||
return r.Title, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "visible", "Visible":
|
||||
return r.Visible, nil
|
||||
case "weight", "Weight":
|
||||
return r.Weight, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Page) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Page{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "description", "Description":
|
||||
return cast2.String(value, &r.Description)
|
||||
case "handle", "Handle":
|
||||
return cast2.String(value, &r.Handle)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "moduleID", "ModuleID":
|
||||
return cast2.Uint64(value, &r.ModuleID)
|
||||
case "namespaceID", "NamespaceID":
|
||||
return cast2.Uint64(value, &r.NamespaceID)
|
||||
case "selfID", "SelfID":
|
||||
return cast2.Uint64(value, &r.SelfID)
|
||||
case "title", "Title":
|
||||
return cast2.String(value, &r.Title)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "visible", "Visible":
|
||||
return cast2.Bool(value, &r.Visible)
|
||||
case "weight", "Weight":
|
||||
return cast2.Int(value, &r.Weight)
|
||||
|
||||
default:
|
||||
return r.setValue(name, pos, value)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r PageLayout) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *PageLayout) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "handle", "Handle":
|
||||
return r.Handle, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return r.NamespaceID, nil
|
||||
case "ownedBy", "OwnedBy":
|
||||
return r.OwnedBy, nil
|
||||
case "pageID", "PageID":
|
||||
return r.PageID, nil
|
||||
case "parentID", "ParentID":
|
||||
return r.ParentID, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "weight", "Weight":
|
||||
return r.Weight, nil
|
||||
|
||||
default:
|
||||
return r.getValue(name, pos)
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *PageLayout) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &PageLayout{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "handle", "Handle":
|
||||
return cast2.String(value, &r.Handle)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "namespaceID", "NamespaceID":
|
||||
return cast2.Uint64(value, &r.NamespaceID)
|
||||
case "ownedBy", "OwnedBy":
|
||||
return cast2.Uint64(value, &r.OwnedBy)
|
||||
case "pageID", "PageID":
|
||||
return cast2.Uint64(value, &r.PageID)
|
||||
case "parentID", "ParentID":
|
||||
return cast2.Uint64(value, &r.ParentID)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "weight", "Weight":
|
||||
return cast2.Int(value, &r.Weight)
|
||||
|
||||
default:
|
||||
return r.setValue(name, pos, value)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Record) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Record) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "createdBy", "CreatedBy", "created_by":
|
||||
return r.CreatedBy, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "deletedBy", "DeletedBy", "deleted_by":
|
||||
return r.DeletedBy, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "meta", "Meta":
|
||||
return r.Meta, nil
|
||||
case "moduleID", "ModuleID":
|
||||
return r.ModuleID, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return r.NamespaceID, nil
|
||||
case "ownedBy", "OwnedBy", "owned_by":
|
||||
return r.OwnedBy, nil
|
||||
case "revision", "Revision":
|
||||
return r.Revision, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "updatedBy", "UpdatedBy", "updated_by":
|
||||
return r.UpdatedBy, nil
|
||||
|
||||
default:
|
||||
return r.getValue(name, pos)
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Record) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Record{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "createdBy", "CreatedBy", "created_by":
|
||||
return cast2.Uint64(value, &r.CreatedBy)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "deletedBy", "DeletedBy", "deleted_by":
|
||||
return cast2.Uint64(value, &r.DeletedBy)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "meta", "Meta":
|
||||
return cast2.Meta(value, &r.Meta)
|
||||
case "moduleID", "ModuleID":
|
||||
return cast2.Uint64(value, &r.ModuleID)
|
||||
case "namespaceID", "NamespaceID":
|
||||
return cast2.Uint64(value, &r.NamespaceID)
|
||||
case "ownedBy", "OwnedBy", "owned_by":
|
||||
return cast2.Uint64(value, &r.OwnedBy)
|
||||
case "revision", "Revision":
|
||||
return cast2.Int(value, &r.Revision)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "updatedBy", "UpdatedBy", "updated_by":
|
||||
return cast2.Uint64(value, &r.UpdatedBy)
|
||||
|
||||
default:
|
||||
return r.setValue(name, pos, value)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
39
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/getters_setters.go
generated
vendored
Normal file
39
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/getters_setters.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package types
|
||||
|
||||
func (r *Record) GetValues(name string) ([]any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return []any{r.CreatedAt}, nil
|
||||
case "createdBy", "CreatedBy", "created_by":
|
||||
return []any{r.CreatedBy}, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return []any{r.DeletedAt}, nil
|
||||
case "deletedBy", "DeletedBy", "deleted_by":
|
||||
return []any{r.DeletedBy}, nil
|
||||
case "id", "ID":
|
||||
return []any{r.ID}, nil
|
||||
case "meta", "Meta":
|
||||
return []any{r.Meta}, nil
|
||||
case "moduleID", "ModuleID":
|
||||
return []any{r.ModuleID}, nil
|
||||
case "namespaceID", "NamespaceID":
|
||||
return []any{r.NamespaceID}, nil
|
||||
case "ownedBy", "OwnedBy", "owned_by":
|
||||
return []any{r.OwnedBy}, nil
|
||||
case "revision", "Revision":
|
||||
return []any{r.Revision}, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return []any{r.UpdatedAt}, nil
|
||||
case "updatedBy", "UpdatedBy", "updated_by":
|
||||
return []any{r.UpdatedBy}, nil
|
||||
|
||||
default:
|
||||
return r.getValues(name)
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
44
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/icon.go
generated
vendored
Normal file
44
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/icon.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
Icon struct {
|
||||
}
|
||||
|
||||
IconFilter struct {
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(icon *Icon) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
|
||||
IconType string
|
||||
)
|
||||
|
||||
const (
|
||||
// IconTypeLink empty or "link" (default):
|
||||
// Indicate that src will contain an absolute or relative link to an icon.
|
||||
// Can also be used for inline images (storing "base64:" prefixed string in source).
|
||||
// This type and reference is not validated by the backend.
|
||||
IconTypeLink IconType = "link" // type, source
|
||||
// IconTypeLibrary "library"
|
||||
// Source references an icon from a library. Ref's value should be in the following
|
||||
// notation: "font-awesome://<icon-identifier>".
|
||||
// This type and source is not validated by the backend.
|
||||
IconTypeLibrary IconType = "library" // type, library(font-awesome), icon name
|
||||
// IconTypeInlineSvg "svg"
|
||||
// SRC contains raw SVG document
|
||||
IconTypeInlineSvg IconType = "inline-svg" // source
|
||||
// IconTypeAttachment "attachment"
|
||||
// Reference (ID) to an existing attachment in local Corteza instance is expected
|
||||
// This type and reference must be validated by the backend.
|
||||
IconTypeAttachment IconType = "attachment" // type, Icon, name
|
||||
)
|
||||
427
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/locale.gen.go
generated
vendored
Normal file
427
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/locale.gen.go
generated
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
LocaleKey struct {
|
||||
Name string
|
||||
Resource string
|
||||
Path string
|
||||
CustomHandler string
|
||||
}
|
||||
)
|
||||
|
||||
// Types and stuff
|
||||
const (
|
||||
ChartResourceTranslationType = "compose:chart"
|
||||
ModuleResourceTranslationType = "compose:module"
|
||||
ModuleFieldResourceTranslationType = "compose:module-field"
|
||||
NamespaceResourceTranslationType = "compose:namespace"
|
||||
PageResourceTranslationType = "compose:page"
|
||||
PageLayoutResourceTranslationType = "compose:page-layout"
|
||||
)
|
||||
|
||||
var (
|
||||
// @todo can we remove LocaleKey struct for string constant?
|
||||
LocaleKeyChartYAxisLabel = LocaleKey{Path: "yAxis.label"}
|
||||
LocaleKeyChartMetricsMetricIDLabel = LocaleKey{Path: "metrics.{{metricID}}.label"}
|
||||
LocaleKeyChartDimensionsDimensionIDMetaStepsStepIDLabel = LocaleKey{Path: "dimensions.{{dimensionID}}.meta.steps.{{stepID}}.label"}
|
||||
LocaleKeyModuleName = LocaleKey{Path: "name"}
|
||||
LocaleKeyModuleFieldLabel = LocaleKey{Path: "label"}
|
||||
LocaleKeyModuleFieldMetaDescriptionView = LocaleKey{Path: "meta.description.view"}
|
||||
LocaleKeyModuleFieldMetaDescriptionEdit = LocaleKey{Path: "meta.description.edit"}
|
||||
LocaleKeyModuleFieldMetaHintView = LocaleKey{Path: "meta.hint.view"}
|
||||
LocaleKeyModuleFieldMetaHintEdit = LocaleKey{Path: "meta.hint.edit"}
|
||||
LocaleKeyModuleFieldExpressionValidatorValidatorIDError = LocaleKey{Path: "expression.validator.{{validatorID}}.error"}
|
||||
LocaleKeyModuleFieldMetaOptionsValueText = LocaleKey{Path: "meta.options.{{value}}.text"}
|
||||
LocaleKeyModuleFieldMetaBoolValueLabel = LocaleKey{Path: "meta.bool.{{value}}.label"}
|
||||
LocaleKeyNamespaceName = LocaleKey{Path: "name"}
|
||||
LocaleKeyNamespaceMetaSubtitle = LocaleKey{Path: "meta.subtitle"}
|
||||
LocaleKeyNamespaceMetaDescription = LocaleKey{Path: "meta.description"}
|
||||
LocaleKeyPageTitle = LocaleKey{Path: "title"}
|
||||
LocaleKeyPageDescription = LocaleKey{Path: "description"}
|
||||
LocaleKeyPagePageBlockBlockIDTitle = LocaleKey{Path: "pageBlock.{{blockID}}.title"}
|
||||
LocaleKeyPagePageBlockBlockIDDescription = LocaleKey{Path: "pageBlock.{{blockID}}.description"}
|
||||
LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel = LocaleKey{Path: "pageBlock.{{blockID}}.button.{{buttonID}}.label"}
|
||||
LocaleKeyPagePageBlockBlockIDContentBody = LocaleKey{Path: "pageBlock.{{blockID}}.content.body"}
|
||||
LocaleKeyPageLayoutMetaTitle = LocaleKey{Path: "meta.title"}
|
||||
LocaleKeyPageLayoutMetaDescription = LocaleKey{Path: "meta.description"}
|
||||
LocaleKeyPageLayoutConfigButtonsNewLabel = LocaleKey{Path: "config.buttons.new.label"}
|
||||
LocaleKeyPageLayoutConfigButtonsEditLabel = LocaleKey{Path: "config.buttons.edit.label"}
|
||||
LocaleKeyPageLayoutConfigButtonsSubmitLabel = LocaleKey{Path: "config.buttons.submit.label"}
|
||||
LocaleKeyPageLayoutConfigButtonsDeleteLabel = LocaleKey{Path: "config.buttons.delete.label"}
|
||||
LocaleKeyPageLayoutConfigButtonsCloneLabel = LocaleKey{Path: "config.buttons.clone.label"}
|
||||
LocaleKeyPageLayoutConfigButtonsBackLabel = LocaleKey{Path: "config.buttons.back.label"}
|
||||
LocaleKeyPageLayoutConfigActionsActionIDMetaLabel = LocaleKey{Path: "config.actions.{{actionID}}.meta.label"}
|
||||
)
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Chart by calling ChartResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in "compose:chart/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Chart) ResourceTranslation() string {
|
||||
return ChartResourceTranslation(r.NamespaceID, r.ID)
|
||||
}
|
||||
|
||||
// ChartResourceTranslation returns string representation of Locale resource for Chart
|
||||
//
|
||||
// Locale resource is in the compose:chart/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ChartResourceTranslation(NamespaceID uint64, ID uint64) string {
|
||||
cpts := []interface{}{
|
||||
ChartResourceTranslationType,
|
||||
strconv.FormatUint(NamespaceID, 10),
|
||||
strconv.FormatUint(ID, 10),
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ChartResourceTranslationTpl(), cpts...)
|
||||
}
|
||||
|
||||
func ChartResourceTranslationTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
func (r *Chart) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
|
||||
r.decodeTranslations(tt)
|
||||
}
|
||||
|
||||
func (r *Chart) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
|
||||
out = append(out, r.encodeTranslations()...)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Module by calling ModuleResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in "compose:module/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Module) ResourceTranslation() string {
|
||||
return ModuleResourceTranslation(r.NamespaceID, r.ID)
|
||||
}
|
||||
|
||||
// ModuleResourceTranslation returns string representation of Locale resource for Module
|
||||
//
|
||||
// Locale resource is in the compose:module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ModuleResourceTranslation(NamespaceID uint64, ID uint64) string {
|
||||
cpts := []interface{}{
|
||||
ModuleResourceTranslationType,
|
||||
strconv.FormatUint(NamespaceID, 10),
|
||||
strconv.FormatUint(ID, 10),
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ModuleResourceTranslationTpl(), cpts...)
|
||||
}
|
||||
|
||||
func ModuleResourceTranslationTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
func (r *Module) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyModuleName.Path); aux != nil {
|
||||
r.Name = aux.Msg
|
||||
}
|
||||
r.decodeTranslations(tt)
|
||||
}
|
||||
|
||||
func (r *Module) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleName.Path,
|
||||
Msg: locale.SanitizeMessage(r.Name),
|
||||
})
|
||||
out = append(out, r.encodeTranslations()...)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for ModuleField by calling ModuleFieldResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in "compose:module-field/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r ModuleField) ResourceTranslation() string {
|
||||
return ModuleFieldResourceTranslation(r.NamespaceID, r.ModuleID, r.ID)
|
||||
}
|
||||
|
||||
// ModuleFieldResourceTranslation returns string representation of Locale resource for ModuleField
|
||||
//
|
||||
// Locale resource is in the compose:module-field/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ModuleFieldResourceTranslation(NamespaceID uint64, ModuleID uint64, ID uint64) string {
|
||||
cpts := []interface{}{
|
||||
ModuleFieldResourceTranslationType,
|
||||
strconv.FormatUint(NamespaceID, 10),
|
||||
strconv.FormatUint(ModuleID, 10),
|
||||
strconv.FormatUint(ID, 10),
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ModuleFieldResourceTranslationTpl(), cpts...)
|
||||
}
|
||||
|
||||
func ModuleFieldResourceTranslationTpl() string {
|
||||
return "%s/%s/%s/%s"
|
||||
}
|
||||
|
||||
func (r *ModuleField) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyModuleFieldLabel.Path); aux != nil {
|
||||
r.Label = aux.Msg
|
||||
}
|
||||
|
||||
r.decodeTranslationsMetaDescriptionView(tt)
|
||||
|
||||
r.decodeTranslationsMetaDescriptionEdit(tt)
|
||||
|
||||
r.decodeTranslationsMetaHintView(tt)
|
||||
|
||||
r.decodeTranslationsMetaHintEdit(tt)
|
||||
|
||||
r.decodeTranslationsExpressionValidatorValidatorIDError(tt)
|
||||
|
||||
r.decodeTranslationsMetaOptionsValueText(tt)
|
||||
|
||||
r.decodeTranslationsMetaBoolValueLabel(tt)
|
||||
|
||||
}
|
||||
|
||||
func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleFieldLabel.Path,
|
||||
Msg: locale.SanitizeMessage(r.Label),
|
||||
})
|
||||
|
||||
out = append(out, r.encodeTranslationsMetaDescriptionView()...)
|
||||
|
||||
out = append(out, r.encodeTranslationsMetaDescriptionEdit()...)
|
||||
|
||||
out = append(out, r.encodeTranslationsMetaHintView()...)
|
||||
|
||||
out = append(out, r.encodeTranslationsMetaHintEdit()...)
|
||||
|
||||
out = append(out, r.encodeTranslationsExpressionValidatorValidatorIDError()...)
|
||||
|
||||
out = append(out, r.encodeTranslationsMetaOptionsValueText()...)
|
||||
|
||||
out = append(out, r.encodeTranslationsMetaBoolValueLabel()...)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Namespace by calling NamespaceResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in "compose:namespace/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Namespace) ResourceTranslation() string {
|
||||
return NamespaceResourceTranslation(r.ID)
|
||||
}
|
||||
|
||||
// NamespaceResourceTranslation returns string representation of Locale resource for Namespace
|
||||
//
|
||||
// Locale resource is in the compose:namespace/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func NamespaceResourceTranslation(ID uint64) string {
|
||||
cpts := []interface{}{
|
||||
NamespaceResourceTranslationType,
|
||||
strconv.FormatUint(ID, 10),
|
||||
}
|
||||
|
||||
return fmt.Sprintf(NamespaceResourceTranslationTpl(), cpts...)
|
||||
}
|
||||
|
||||
func NamespaceResourceTranslationTpl() string {
|
||||
return "%s/%s"
|
||||
}
|
||||
|
||||
func (r *Namespace) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyNamespaceName.Path); aux != nil {
|
||||
r.Name = aux.Msg
|
||||
}
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyNamespaceMetaSubtitle.Path); aux != nil {
|
||||
r.Meta.Subtitle = aux.Msg
|
||||
}
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyNamespaceMetaDescription.Path); aux != nil {
|
||||
r.Meta.Description = aux.Msg
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyNamespaceName.Path,
|
||||
Msg: locale.SanitizeMessage(r.Name),
|
||||
})
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyNamespaceMetaSubtitle.Path,
|
||||
Msg: locale.SanitizeMessage(r.Meta.Subtitle),
|
||||
})
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyNamespaceMetaDescription.Path,
|
||||
Msg: locale.SanitizeMessage(r.Meta.Description),
|
||||
})
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Page by calling PageResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in "compose:page/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Page) ResourceTranslation() string {
|
||||
return PageResourceTranslation(r.NamespaceID, r.ID)
|
||||
}
|
||||
|
||||
// PageResourceTranslation returns string representation of Locale resource for Page
|
||||
//
|
||||
// Locale resource is in the compose:page/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func PageResourceTranslation(NamespaceID uint64, ID uint64) string {
|
||||
cpts := []interface{}{
|
||||
PageResourceTranslationType,
|
||||
strconv.FormatUint(NamespaceID, 10),
|
||||
strconv.FormatUint(ID, 10),
|
||||
}
|
||||
|
||||
return fmt.Sprintf(PageResourceTranslationTpl(), cpts...)
|
||||
}
|
||||
|
||||
func PageResourceTranslationTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
func (r *Page) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyPageTitle.Path); aux != nil {
|
||||
r.Title = aux.Msg
|
||||
}
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyPageDescription.Path); aux != nil {
|
||||
r.Description = aux.Msg
|
||||
}
|
||||
|
||||
r.decodeTranslations(tt)
|
||||
}
|
||||
|
||||
func (r *Page) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyPageTitle.Path,
|
||||
Msg: locale.SanitizeMessage(r.Title),
|
||||
})
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyPageDescription.Path,
|
||||
Msg: locale.SanitizeMessage(r.Description),
|
||||
})
|
||||
|
||||
out = append(out, r.encodeTranslations()...)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for PageLayout by calling PageLayoutResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in "compose:page-layout/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r PageLayout) ResourceTranslation() string {
|
||||
return PageLayoutResourceTranslation(r.NamespaceID, r.PageID, r.ID)
|
||||
}
|
||||
|
||||
// PageLayoutResourceTranslation returns string representation of Locale resource for PageLayout
|
||||
//
|
||||
// Locale resource is in the compose:page-layout/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func PageLayoutResourceTranslation(NamespaceID uint64, PageID uint64, ID uint64) string {
|
||||
cpts := []interface{}{
|
||||
PageLayoutResourceTranslationType,
|
||||
strconv.FormatUint(NamespaceID, 10),
|
||||
strconv.FormatUint(PageID, 10),
|
||||
strconv.FormatUint(ID, 10),
|
||||
}
|
||||
|
||||
return fmt.Sprintf(PageLayoutResourceTranslationTpl(), cpts...)
|
||||
}
|
||||
|
||||
func PageLayoutResourceTranslationTpl() string {
|
||||
return "%s/%s/%s/%s"
|
||||
}
|
||||
|
||||
func (r *PageLayout) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutMetaTitle.Path); aux != nil {
|
||||
r.Meta.Title = aux.Msg
|
||||
}
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutMetaDescription.Path); aux != nil {
|
||||
r.Meta.Description = aux.Msg
|
||||
}
|
||||
|
||||
r.decodeTranslations(tt)
|
||||
}
|
||||
|
||||
func (r *PageLayout) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyPageLayoutMetaTitle.Path,
|
||||
Msg: locale.SanitizeMessage(r.Meta.Title),
|
||||
})
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyPageLayoutMetaDescription.Path,
|
||||
Msg: locale.SanitizeMessage(r.Meta.Description),
|
||||
})
|
||||
|
||||
out = append(out, r.encodeTranslations()...)
|
||||
|
||||
return out
|
||||
}
|
||||
191
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module.go
generated
vendored
Normal file
191
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
"github.com/jmoiron/sqlx/types"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/dal"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
)
|
||||
|
||||
type (
|
||||
Module struct {
|
||||
ID uint64 `json:"moduleID,string"`
|
||||
Handle string `json:"handle"`
|
||||
|
||||
// collection of configurations for various subsystems that
|
||||
// use this module and how it affects their behaviour
|
||||
Config ModuleConfig `json:"config"`
|
||||
|
||||
// @todo should be removed and placed into a separate subsystem
|
||||
// mostly because we want to allow client apps to store
|
||||
// application configs away from the module config
|
||||
// using separate access-control
|
||||
Meta types.JSONText `json:"meta"`
|
||||
|
||||
Fields ModuleFieldSet `json:"fields"`
|
||||
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Issues []dal.Issue `json:"issues,omitempty"`
|
||||
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
ModuleConfig struct {
|
||||
// How and where the records of this module are stored in the database
|
||||
DAL ModuleConfigDAL `json:"dal"`
|
||||
|
||||
// Record data privacy settings
|
||||
Privacy ModuleConfigDataPrivacy `json:"privacy"`
|
||||
|
||||
Discovery ModuleConfigDiscovery `json:"discovery"`
|
||||
|
||||
RecordRevisions ModuleConfigRecordRevisions `json:"recordRevisions"`
|
||||
|
||||
// RecordDeDup value duplicate detection settings
|
||||
RecordDeDup ModuleConfigRecordDeDup `json:"recordDeDup"`
|
||||
}
|
||||
|
||||
ModuleConfigDAL struct {
|
||||
ConnectionID uint64 `json:"connectionID,string"`
|
||||
|
||||
Constraints map[string][]any `json:"constraints"`
|
||||
|
||||
// model identifier (table, collection on the database)
|
||||
// can contain {{placeholders}}
|
||||
Ident string `json:"ident"`
|
||||
|
||||
SystemFieldEncoding SystemFieldEncoding `json:"systemFieldEncoding"`
|
||||
}
|
||||
|
||||
ModuleConfigRecordRevisions struct {
|
||||
// enable or disable revisions
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// where are record revisions stored
|
||||
Ident string `json:"ident"`
|
||||
}
|
||||
|
||||
ModuleConfigDataPrivacy struct {
|
||||
// Define the highest sensitivity level which
|
||||
// can be configured on the module fields
|
||||
SensitivityLevelID uint64 `json:"sensitivityLevelID,string,omitempty"`
|
||||
|
||||
UsageDisclosure string `json:"usageDisclosure"`
|
||||
}
|
||||
|
||||
ModuleConfigRecordDeDup struct {
|
||||
// strictly restrict record saving
|
||||
// otherwise show a warning with list of duplicated records
|
||||
Strict bool `json:"-"`
|
||||
|
||||
// list of duplicate detection rules applied to module's fields
|
||||
Rules DeDupRuleSet `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
ModuleConfigDiscovery struct {
|
||||
Public DiscoveryResult `json:"public"`
|
||||
Private DiscoveryResult `json:"private"`
|
||||
Protected DiscoveryResult `json:"protected"`
|
||||
}
|
||||
|
||||
DiscoveryResult struct {
|
||||
Result []struct {
|
||||
Lang string `json:"lang"`
|
||||
Fields []string `json:"fields"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
ModuleFilter struct {
|
||||
ModuleID []string `json:"moduleID"`
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
Query string `json:"query"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
|
||||
LabeledIDs []uint64 `json:"-"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*Module) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
|
||||
func (m Module) Clone() *Module {
|
||||
c := &m
|
||||
c.Fields = m.Fields.Clone()
|
||||
return c
|
||||
}
|
||||
|
||||
func (m Module) HasIssues() bool {
|
||||
return len(m.Issues) > 0
|
||||
}
|
||||
|
||||
// We won't worry about fields at this point
|
||||
func (m *Module) decodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
// We won't worry about fields at this point
|
||||
func (m *Module) encodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Module) ModelRef() dal.ModelRef {
|
||||
return dal.ModelRef{
|
||||
ConnectionID: m.Config.DAL.ConnectionID,
|
||||
|
||||
ResourceID: m.ID,
|
||||
|
||||
ResourceType: ModuleResourceType,
|
||||
// @todo will use this for now but should probably change
|
||||
Resource: m.RbacResource(),
|
||||
}
|
||||
}
|
||||
|
||||
// FindByHandle finds module by it's handle
|
||||
func (set ModuleSet) FindByHandle(handle string) *Module {
|
||||
for i := range set {
|
||||
if set[i].Handle == handle {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ModuleConfig) Scan(src any) error { return sql.ParseJSON(src, c) }
|
||||
func (c ModuleConfig) Value() (driver.Value, error) { return json.Marshal(c) }
|
||||
|
||||
func ParseModuleConfig(ss []string) (m ModuleConfig, err error) {
|
||||
if len(ss) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(ss[0]), &m)
|
||||
return
|
||||
}
|
||||
600
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module_field.go
generated
vendored
Normal file
600
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module_field.go
generated
vendored
Normal file
@@ -0,0 +1,600 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
// Modules - CRM module definitions
|
||||
ModuleField struct {
|
||||
ID uint64 `json:"fieldID,string"`
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
ModuleID uint64 `json:"moduleID,string"`
|
||||
Place int `json:"-"`
|
||||
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// Options relevant to field type
|
||||
Options ModuleFieldOptions `json:"options"`
|
||||
|
||||
// Configuration - how sub-services and sub-systems like DAL and record revisions
|
||||
// are configured to work with this field
|
||||
Config ModuleFieldConfig `json:"config"`
|
||||
|
||||
Required bool `json:"isRequired"`
|
||||
Multi bool `json:"isMulti"`
|
||||
DefaultValue RecordValueSet `json:"defaultValue"`
|
||||
|
||||
Expressions ModuleFieldExpr `json:"expressions"`
|
||||
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
ModuleFieldConfig struct {
|
||||
DAL ModuleFieldConfigDAL `json:"dal"`
|
||||
Privacy ModuleFieldConfigDataPrivacy `json:"privacy"`
|
||||
|
||||
RecordRevisions ModuleFieldConfigRecordRevisions `json:"recordRevisions"`
|
||||
}
|
||||
|
||||
// ModuleFieldConfigDAL holds DAL configuration for a specific field
|
||||
//
|
||||
// If strategy is not set for a specific field (nil)
|
||||
// then a default strategy is used
|
||||
ModuleFieldConfigDAL struct {
|
||||
EncodingStrategy *EncodingStrategy `json:"encodingStrategy"`
|
||||
}
|
||||
|
||||
ModuleFieldConfigDataPrivacy struct {
|
||||
// Define the highest sensitivity level which
|
||||
// can be configured on the module fields
|
||||
SensitivityLevelID uint64 `json:"sensitivityLevelID,string,omitempty"`
|
||||
|
||||
UsageDisclosure string `json:"usageDisclosure"`
|
||||
}
|
||||
|
||||
ModuleFieldConfigRecordRevisions struct {
|
||||
// when true, skip record revisions for this field
|
||||
Skip bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// SystemFieldEncoding holds configuration for encoding record system fields
|
||||
//
|
||||
// If strategy is not set for a specific field (nil)
|
||||
// then a default strategy is used, assuming system field/column presence
|
||||
SystemFieldEncoding struct {
|
||||
ID *EncodingStrategy `json:"id"`
|
||||
|
||||
ModuleID *EncodingStrategy `json:"moduleID"`
|
||||
NamespaceID *EncodingStrategy `json:"namespaceID"`
|
||||
|
||||
Revision *EncodingStrategy `json:"revision"`
|
||||
Meta *EncodingStrategy `json:"meta"`
|
||||
|
||||
OwnedBy *EncodingStrategy `json:"ownedBy"`
|
||||
|
||||
CreatedAt *EncodingStrategy `json:"createdAt"`
|
||||
CreatedBy *EncodingStrategy `json:"createdBy"`
|
||||
|
||||
UpdatedAt *EncodingStrategy `json:"updatedAt"`
|
||||
UpdatedBy *EncodingStrategy `json:"updatedBy"`
|
||||
|
||||
DeletedAt *EncodingStrategy `json:"deletedAt"`
|
||||
DeletedBy *EncodingStrategy `json:"deletedBy"`
|
||||
}
|
||||
|
||||
// EncodingStrategy is used by both: Module (for system fields) and ModuleField
|
||||
//
|
||||
EncodingStrategy struct {
|
||||
//Type string `json:"type"`
|
||||
//TypeParams map[string]any `json:"typeParams"`
|
||||
|
||||
Omit bool `json:"omit,omitempty"`
|
||||
|
||||
*EncodingStrategyAlias `json:"alias,omitempty"`
|
||||
*EncodingStrategyJSON `json:"json,omitempty"`
|
||||
*EncodingStrategyPlain `json:"plain,omitempty"`
|
||||
}
|
||||
|
||||
EncodingStrategyAlias struct {
|
||||
Ident string `json:"ident"`
|
||||
}
|
||||
|
||||
EncodingStrategyJSON struct {
|
||||
Ident string `json:"ident"`
|
||||
}
|
||||
|
||||
EncodingStrategyPlain struct{}
|
||||
|
||||
ModuleFieldFilter struct {
|
||||
ModuleID []uint64
|
||||
Deleted filter.State
|
||||
Limit uint
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
_ sort.Interface = &ModuleFieldSet{}
|
||||
)
|
||||
|
||||
func (f *ModuleField) SelectOptions() (out []string) {
|
||||
if f.Kind != "Select" {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
options, has = f.Options["options"]
|
||||
)
|
||||
|
||||
if !has {
|
||||
return
|
||||
}
|
||||
|
||||
switch oo := options.(type) {
|
||||
case []string:
|
||||
out = oo
|
||||
case []interface{}:
|
||||
for _, o := range oo {
|
||||
switch c := o.(type) {
|
||||
case string:
|
||||
out = append(out, c)
|
||||
case map[string]string:
|
||||
if value, has := c["value"]; has {
|
||||
out = append(out, value)
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if value, has := c["value"]; has {
|
||||
if value, ok := value.(string); ok {
|
||||
out = append(out, value)
|
||||
}
|
||||
}
|
||||
case ModuleFieldOptions:
|
||||
if value, has := c["value"]; has {
|
||||
if value, ok := value.(string); ok {
|
||||
out = append(out, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (f *ModuleField) decodeTranslationsExpressionValidatorValidatorIDError(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
for i, e := range f.Expressions.Validators {
|
||||
validatorID := locale.ContentID(e.ValidatorID, i)
|
||||
rpl := strings.NewReplacer(
|
||||
"{{validatorID}}", strconv.FormatUint(validatorID, 10),
|
||||
)
|
||||
|
||||
if aux = tt.FindByKey(rpl.Replace(LocaleKeyModuleFieldExpressionValidatorValidatorIDError.Path)); aux != nil {
|
||||
f.Expressions.Validators[i].Error = aux.Msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ModuleField) decodeTranslationsMetaDescriptionView(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyModuleFieldMetaDescriptionView.Path); aux != nil {
|
||||
f.setOptionKey(aux.Msg, "description", "view")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ModuleField) decodeTranslationsMetaDescriptionEdit(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyModuleFieldMetaDescriptionEdit.Path); aux != nil {
|
||||
f.setOptionKey(aux.Msg, "description", "edit")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ModuleField) decodeTranslationsMetaHintView(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyModuleFieldMetaHintView.Path); aux != nil {
|
||||
f.setOptionKey(aux.Msg, "hint", "view")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ModuleField) decodeTranslationsMetaHintEdit(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyModuleFieldMetaHintEdit.Path); aux != nil {
|
||||
f.setOptionKey(aux.Msg, "hint", "edit")
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes translations and modifies options
|
||||
//
|
||||
// Why "options-option-texts"? Because we're translating option txts under options key-value
|
||||
func (f *ModuleField) decodeTranslationsMetaOptionsValueText(tt locale.ResourceTranslationIndex) {
|
||||
var (
|
||||
tr *locale.ResourceTranslation
|
||||
)
|
||||
|
||||
optsUnknown, has := f.Options["options"]
|
||||
if !has {
|
||||
return
|
||||
}
|
||||
|
||||
optsSlice, is := optsUnknown.([]interface{})
|
||||
if !is {
|
||||
return
|
||||
}
|
||||
|
||||
for i, optUnknown := range optsSlice {
|
||||
outOpt := map[string]any{
|
||||
"value": "",
|
||||
"text": "",
|
||||
"style": map[string]any{
|
||||
"textColor": "",
|
||||
"backgroundColor": "",
|
||||
},
|
||||
}
|
||||
|
||||
// what is this we're dealing with? slice of strings (values) or map (value+text)
|
||||
switch opt := optUnknown.(type) {
|
||||
case string:
|
||||
// cast string (value) into map (value+text)
|
||||
// and use value as text (as a fallback in case
|
||||
// of missing translation)
|
||||
outOpt["value"] = opt
|
||||
outOpt["text"] = opt
|
||||
|
||||
case map[string]interface{}:
|
||||
outOpt["value"], is = opt["value"].(string)
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
outOpt["text"], _ = opt["text"].(string)
|
||||
|
||||
if style, ok := opt["style"].(map[string]any); ok {
|
||||
outOpt["style"] = style
|
||||
}
|
||||
}
|
||||
|
||||
// find the translation for that value
|
||||
// and update the option (effectively overwriting
|
||||
// the original text value (in case of map option)
|
||||
trKey := strings.NewReplacer("{{value}}", outOpt["value"].(string)).Replace(LocaleKeyModuleFieldMetaOptionsValueText.Path)
|
||||
if tr = tt.FindByKey(trKey); tr != nil {
|
||||
outOpt["text"] = tr.Msg
|
||||
}
|
||||
|
||||
// Update slice item with translated option
|
||||
optsSlice[i] = outOpt
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes translations and modifies options
|
||||
func (f *ModuleField) decodeTranslationsMetaBoolValueLabel(tt locale.ResourceTranslationIndex) {
|
||||
if f.Kind != "Bool" {
|
||||
return
|
||||
}
|
||||
|
||||
var aux *locale.ResourceTranslation
|
||||
if aux = tt.FindByKey(strings.NewReplacer("{{value}}", "true").Replace(LocaleKeyModuleFieldMetaBoolValueLabel.Path)); aux != nil {
|
||||
f.setOptionKey(aux.Msg, "trueLabel")
|
||||
}
|
||||
if aux = tt.FindByKey(strings.NewReplacer("{{value}}", "false").Replace(LocaleKeyModuleFieldMetaBoolValueLabel.Path)); aux != nil {
|
||||
f.setOptionKey(aux.Msg, "falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ModuleField) encodeTranslationsExpressionValidatorValidatorIDError() (out locale.ResourceTranslationSet) {
|
||||
out = make(locale.ResourceTranslationSet, 0, 3)
|
||||
|
||||
// Module field expressions
|
||||
for i, e := range m.Expressions.Validators {
|
||||
validatorID := locale.ContentID(e.ValidatorID, i)
|
||||
rpl := strings.NewReplacer(
|
||||
"{{validatorID}}", strconv.FormatUint(validatorID, 10),
|
||||
)
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: m.ResourceTranslation(),
|
||||
Key: rpl.Replace(LocaleKeyModuleFieldExpressionValidatorValidatorIDError.Path),
|
||||
Msg: e.Error,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (f *ModuleField) encodeTranslationsMetaDescriptionView() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
t := &locale.ResourceTranslation{
|
||||
Resource: f.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleFieldMetaDescriptionView.Path,
|
||||
}
|
||||
if v := f.getOptionKey("description", "view"); v != nil {
|
||||
t.Msg = cast.ToString(v)
|
||||
}
|
||||
out = append(out, t)
|
||||
return out
|
||||
}
|
||||
|
||||
func (f *ModuleField) encodeTranslationsMetaDescriptionEdit() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
t := &locale.ResourceTranslation{
|
||||
Resource: f.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleFieldMetaDescriptionEdit.Path,
|
||||
}
|
||||
if v := f.getOptionKey("description", "edit"); v != nil {
|
||||
t.Msg = cast.ToString(v)
|
||||
}
|
||||
out = append(out, t)
|
||||
return out
|
||||
}
|
||||
|
||||
func (f *ModuleField) encodeTranslationsMetaHintView() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
t := &locale.ResourceTranslation{
|
||||
Resource: f.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleFieldMetaHintView.Path,
|
||||
}
|
||||
if v := f.getOptionKey("hint", "view"); v != nil {
|
||||
t.Msg = cast.ToString(v)
|
||||
}
|
||||
out = append(out, t)
|
||||
return out
|
||||
}
|
||||
|
||||
func (f *ModuleField) encodeTranslationsMetaHintEdit() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{}
|
||||
t := &locale.ResourceTranslation{
|
||||
Resource: f.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleFieldMetaHintEdit.Path,
|
||||
}
|
||||
if v := f.getOptionKey("hint", "edit"); v != nil {
|
||||
t.Msg = cast.ToString(v)
|
||||
}
|
||||
out = append(out, t)
|
||||
return out
|
||||
}
|
||||
|
||||
// extracts option texts and converts (encodes) them to translations
|
||||
func (f *ModuleField) encodeTranslationsMetaOptionsValueText() (out locale.ResourceTranslationSet) {
|
||||
out = make(locale.ResourceTranslationSet, 0, 3)
|
||||
|
||||
optsUnknown, has := f.Options["options"]
|
||||
if !has {
|
||||
return
|
||||
}
|
||||
|
||||
optsSlice, is := optsUnknown.([]interface{})
|
||||
if !is {
|
||||
return
|
||||
}
|
||||
|
||||
for _, optUnknown := range optsSlice {
|
||||
// we only care about maps (items with value & text)
|
||||
switch opt := optUnknown.(type) {
|
||||
case map[string]interface{}:
|
||||
value, _ := opt["value"].(string)
|
||||
text, _ := opt["text"].(string)
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: f.ResourceTranslation(),
|
||||
Key: strings.NewReplacer("{{value}}", value).
|
||||
Replace(LocaleKeyModuleFieldMetaOptionsValueText.Path),
|
||||
Msg: text,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *ModuleField) encodeTranslationsMetaBoolValueLabel() (out locale.ResourceTranslationSet) {
|
||||
if m.Kind != "Bool" {
|
||||
return
|
||||
}
|
||||
|
||||
out = make(locale.ResourceTranslationSet, 0, 3)
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: m.ResourceTranslation(),
|
||||
Key: strings.NewReplacer("{{value}}", "true").Replace(LocaleKeyModuleFieldMetaBoolValueLabel.Path),
|
||||
Msg: m.Options.String("trueLabel"),
|
||||
})
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: m.ResourceTranslation(),
|
||||
Key: strings.NewReplacer("{{value}}", "false").Replace(LocaleKeyModuleFieldMetaBoolValueLabel.Path),
|
||||
Msg: m.Options.String("falseLabel"),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (f ModuleField) Clone() *ModuleField {
|
||||
return &f
|
||||
}
|
||||
|
||||
func (f ModuleField) setOptionKey(v interface{}, kk ...string) {
|
||||
opt := f.Options
|
||||
if opt == nil {
|
||||
opt = ModuleFieldOptions{}
|
||||
}
|
||||
|
||||
for _, k := range kk[0 : len(kk)-1] {
|
||||
_, ok := opt[k]
|
||||
if !ok {
|
||||
opt = map[string]interface{}{k: make(map[string]interface{})}
|
||||
}
|
||||
var aux ModuleFieldOptions
|
||||
switch c := opt[k].(type) {
|
||||
case map[string]interface{}:
|
||||
aux = ModuleFieldOptions(c)
|
||||
case ModuleFieldOptions:
|
||||
aux = c
|
||||
}
|
||||
|
||||
opt = aux
|
||||
}
|
||||
|
||||
k := kk[len(kk)-1]
|
||||
opt[k] = v
|
||||
}
|
||||
|
||||
func (f ModuleField) getOptionKey(kk ...string) interface{} {
|
||||
if len(f.Options) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
opt := f.Options
|
||||
|
||||
for _, k := range kk[0 : len(kk)-1] {
|
||||
_, ok := opt[k]
|
||||
if !ok {
|
||||
opt = map[string]interface{}{k: make(map[string]interface{})}
|
||||
}
|
||||
|
||||
var aux ModuleFieldOptions
|
||||
switch c := opt[k].(type) {
|
||||
case map[string]interface{}:
|
||||
aux = ModuleFieldOptions(c)
|
||||
case ModuleFieldOptions:
|
||||
aux = c
|
||||
}
|
||||
|
||||
opt = aux
|
||||
}
|
||||
|
||||
return opt[kk[len(kk)-1]]
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) Clone() (out ModuleFieldSet) {
|
||||
out = make([]*ModuleField, len(set))
|
||||
for i := range set {
|
||||
out[i] = set[i].Clone()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (set *ModuleFieldSet) Scan(src any) error { return sql.ParseJSON(src, set) }
|
||||
func (set ModuleFieldSet) Value() (driver.Value, error) { return json.Marshal(set) }
|
||||
|
||||
func (set ModuleFieldSet) Names() (names []string) {
|
||||
names = make([]string, len(set))
|
||||
|
||||
for i := range set {
|
||||
names[i] = set[i].Name
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) HasName(name string) bool {
|
||||
for i := range set {
|
||||
if name == set[i].Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) FindByName(name string) *ModuleField {
|
||||
for i := range set {
|
||||
if name == set[i].Name {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) FilterByModule(moduleID uint64) (ff ModuleFieldSet) {
|
||||
for i := range set {
|
||||
if set[i].ModuleID == moduleID {
|
||||
ff = append(ff, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) Len() int {
|
||||
return len(set)
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) Less(i, j int) bool {
|
||||
return set[i].Place < set[j].Place
|
||||
}
|
||||
|
||||
func (set ModuleFieldSet) Swap(i, j int) {
|
||||
set[i], set[j] = set[j], set[i]
|
||||
}
|
||||
|
||||
func (f ModuleField) IsBoolean() bool {
|
||||
return f.Kind == "Bool"
|
||||
}
|
||||
|
||||
func (f ModuleField) IsNumeric() bool {
|
||||
return f.Kind == "Number"
|
||||
}
|
||||
|
||||
func (f ModuleField) IsDateTime() bool {
|
||||
return f.Kind == "DateTime" && !f.IsDateOnly() && !f.IsTimeOnly()
|
||||
}
|
||||
|
||||
func (f ModuleField) IsDateOnly() bool {
|
||||
return f.Kind == "DateTime" && f.Options.Bool("onlyDate")
|
||||
}
|
||||
|
||||
func (f ModuleField) IsTimeOnly() bool {
|
||||
return f.Kind == "DateTime" && f.Options.Bool("onlyTime")
|
||||
}
|
||||
|
||||
// IsRef tells us if value of this field be a reference to something
|
||||
// (another record, file , user)?
|
||||
func (f ModuleField) IsRef() bool {
|
||||
return f.Kind == "Record" || f.Kind == "User" || f.Kind == "File"
|
||||
}
|
||||
|
||||
func (f ModuleField) IsSensitive() bool {
|
||||
return f.Config.Privacy.SensitivityLevelID > 0
|
||||
}
|
||||
|
||||
func (f *ModuleField) setValue(name string, pos uint, value any) (err error) {
|
||||
switch name {
|
||||
// @todo consider moving this to the .cue definition; figure out why it wasn't yet
|
||||
case "NamespaceID", "namespaceID":
|
||||
f.NamespaceID = cast.ToUint64(value)
|
||||
case "Options.ModuleID":
|
||||
f.Options["moduleID"] = cast.ToString(value)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ModuleFieldConfig) Scan(src any) error { return sql.ParseJSON(src, p) }
|
||||
func (p ModuleFieldConfig) Value() (driver.Value, error) { return json.Marshal(p) }
|
||||
30
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module_field_expr.go
generated
vendored
Normal file
30
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module_field_expr.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
)
|
||||
|
||||
type (
|
||||
ModuleFieldExpr struct {
|
||||
ValueExpr string `json:"value,omitempty"`
|
||||
|
||||
Sanitizers []string `json:"sanitizers,omitempty"`
|
||||
|
||||
Validators []ModuleFieldValidator `json:"validators,omitempty"`
|
||||
DisableDefaultValidators bool `json:"disableDefaultValidators,omitempty"`
|
||||
|
||||
Formatters []string `json:"formatters,omitempty"`
|
||||
DisableDefaultFormatters bool `json:"disableDefaultFormatters,omitempty"`
|
||||
}
|
||||
|
||||
ModuleFieldValidator struct {
|
||||
ValidatorID uint64 `json:"validatorID,string,omitempty"`
|
||||
Test string `json:"test,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
func (opt *ModuleFieldExpr) Scan(src any) error { return sql.ParseJSON(src, opt) }
|
||||
func (opt ModuleFieldExpr) Value() (driver.Value, error) { return json.Marshal(opt) }
|
||||
183
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module_field_options.go
generated
vendored
Normal file
183
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/module_field_options.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
"github.com/spf13/cast"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
ModuleFieldOptions map[string]interface{}
|
||||
|
||||
ModuleFieldOptionIndex struct {
|
||||
// Applicable to ref field kinds where we use nested/object type
|
||||
// and we want to control how deep do we want to follow the references
|
||||
NestingDepth map[string]bool `json:"nestingDepth,omitempty"`
|
||||
|
||||
// When embedding this field into a parent doc, do we skip it?
|
||||
SkipNested bool `json:"skipNested,omitempty"`
|
||||
|
||||
// @todo we'll probably define aggregation/buckets params here
|
||||
// so that we can handle faceting properly?
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
moduleFieldOptionExpression = "expression"
|
||||
moduleFieldOptionIsUnique = "isUnique"
|
||||
moduleFieldOptionIsUniqueMultiValue = "isUniqueMultiValue"
|
||||
moduleFieldOptionIndex = "index"
|
||||
|
||||
moduleFieldOptionOptions = "options"
|
||||
|
||||
moduleFieldNumberOptionPrecision = "precision"
|
||||
moduleFieldNumberOptionPrecisionMin uint = 0
|
||||
moduleFieldNumberOptionPrecisionMax uint = 6
|
||||
)
|
||||
|
||||
func (opt *ModuleFieldOptions) Scan(src any) error { return sql.ParseJSON(src, opt) }
|
||||
func (opt ModuleFieldOptions) Value() (driver.Value, error) { return json.Marshal(opt) }
|
||||
|
||||
// Bool returns option value for key as boolean true or false
|
||||
//
|
||||
// Invalid, non-existing are returned as false
|
||||
func (opt ModuleFieldOptions) Bool(key string) bool {
|
||||
if _, has := opt[key]; has {
|
||||
if v, ok := opt[key].(bool); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) UInt64(key string) uint64 {
|
||||
return opt.UInt64Def(key, 0)
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) UInt64Def(key string, def uint64) uint64 {
|
||||
if val, has := opt[key]; has {
|
||||
v, err := cast.ToUint64E(val)
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) Int64(key string) int64 {
|
||||
return opt.Int64Def(key, 0)
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) Int64Def(key string, def int64) int64 {
|
||||
if val, has := opt[key]; has {
|
||||
switch conv := val.(type) {
|
||||
case int:
|
||||
return int64(conv)
|
||||
case int64:
|
||||
return conv
|
||||
default:
|
||||
// to avoid covering every possible type, just convert value into string
|
||||
strVal := fmt.Sprintf("%v", val)
|
||||
|
||||
if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil {
|
||||
return intVal
|
||||
}
|
||||
|
||||
if floatVal, err := strconv.ParseFloat(strVal, 64); err == nil {
|
||||
return int64(floatVal)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// Uint64 returns option value for key casted to uint64
|
||||
func (opt ModuleFieldOptions) Uint64(key string) uint64 {
|
||||
|
||||
if val, has := opt[key]; has {
|
||||
return cast.ToUint64(val)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Strings returns option value for key as slice of strings
|
||||
//
|
||||
// Invalid, non-existing are returned as nil
|
||||
func (opt ModuleFieldOptions) Strings(key string) []string {
|
||||
if _, has := opt[key]; has {
|
||||
if v, ok := opt[key].([]string); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns option value for key as single string
|
||||
//
|
||||
// Invalid, non-existing are returned as empty string ("")
|
||||
func (opt ModuleFieldOptions) String(key string) string {
|
||||
if _, has := opt[key]; has {
|
||||
if v, ok := opt[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsUnique - should value in this field be unique across records?
|
||||
func (opt ModuleFieldOptions) IsUnique() bool {
|
||||
return opt.Bool(moduleFieldOptionIsUnique)
|
||||
}
|
||||
|
||||
// SetIsUnique - should value in this field be unique across records?
|
||||
func (opt ModuleFieldOptions) SetIsUnique(value bool) {
|
||||
opt[moduleFieldOptionIsUnique] = value
|
||||
}
|
||||
|
||||
// IsUniqueMultiValue - should value in this field be unique in the multi-value set?
|
||||
func (opt ModuleFieldOptions) IsUniqueMultiValue() bool {
|
||||
return opt.Bool(moduleFieldOptionIsUniqueMultiValue)
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) SetIsUniqueMultiValue(value bool) {
|
||||
// SetIsUniqueMultiValue - should value in this field be unique in the multi-value set?
|
||||
opt[moduleFieldOptionIsUniqueMultiValue] = value
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) Precision() (p uint) {
|
||||
p = uint(opt.Int64(moduleFieldNumberOptionPrecision))
|
||||
|
||||
if p < moduleFieldNumberOptionPrecisionMin {
|
||||
p = moduleFieldNumberOptionPrecisionMin
|
||||
} else if p > moduleFieldNumberOptionPrecisionMax {
|
||||
p = moduleFieldNumberOptionPrecisionMax
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (opt ModuleFieldOptions) SetPrecision(p uint) {
|
||||
opt[moduleFieldNumberOptionPrecision] = p
|
||||
}
|
||||
|
||||
// IsUnique - should value in this field be unique across records?
|
||||
func (opt ModuleFieldOptions) Index() *ModuleFieldOptionIndex {
|
||||
if val, has := opt[moduleFieldOptionIndex]; has {
|
||||
if i, is := val.(*ModuleFieldOptionIndex); is {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
93
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/namespace.go
generated
vendored
Normal file
93
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/namespace.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
)
|
||||
|
||||
type (
|
||||
Namespace struct {
|
||||
ID uint64 `json:"namespaceID,string"`
|
||||
Slug string `json:"slug"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Meta NamespaceMeta `json:"meta"`
|
||||
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
NamespaceFilter struct {
|
||||
NamespaceID []string `json:"namespaceID"`
|
||||
|
||||
Query string `json:"query"`
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
|
||||
LabeledIDs []uint64 `json:"-"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*Namespace) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
|
||||
NamespaceMeta struct {
|
||||
// Temporary icon & logo URLs
|
||||
// @todo rework this when we rework attachment management
|
||||
Icon string `json:"icon,omitempty"`
|
||||
IconID uint64 `json:"iconID,string"`
|
||||
Logo string `json:"logo,omitempty"`
|
||||
LogoID uint64 `json:"logoID,string"`
|
||||
LogoEnabled bool `json:"logoEnabled,omitempty"`
|
||||
HideSidebar bool `json:"hideSidebar"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Subtitle string `json:"subtitle,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
func (n Namespace) Clone() *Namespace {
|
||||
c := &n
|
||||
return c
|
||||
}
|
||||
|
||||
// FindByHandle finds namespace by it's handle/slug
|
||||
func (set NamespaceSet) FindByHandle(handle string) *Namespace {
|
||||
for i := range set {
|
||||
if set[i].Slug == handle {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nm *NamespaceMeta) Scan(src any) error { return sql.ParseJSON(src, nm) }
|
||||
func (nm NamespaceMeta) Value() (driver.Value, error) { return json.Marshal(nm) }
|
||||
13
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/notification.go
generated
vendored
Normal file
13
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/notification.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
EmailNotification struct {
|
||||
To []string
|
||||
Cc []string
|
||||
ReplyTo string
|
||||
Subject string
|
||||
ContentPlain string
|
||||
ContentHTML string
|
||||
RemoteAttachments []string
|
||||
}
|
||||
)
|
||||
488
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page.go
generated
vendored
Normal file
488
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page.go
generated
vendored
Normal file
@@ -0,0 +1,488 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
"github.com/modern-go/reflect2"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
Page struct {
|
||||
ID uint64 `json:"pageID,string"`
|
||||
SelfID uint64 `json:"selfID,string"`
|
||||
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
|
||||
ModuleID uint64 `json:"moduleID,string"`
|
||||
|
||||
Handle string `json:"handle"`
|
||||
|
||||
Config PageConfig `json:"config"`
|
||||
Blocks PageBlocks `json:"blocks"`
|
||||
|
||||
Meta PageMeta `json:"meta"`
|
||||
|
||||
Children PageSet `json:"children,omitempty"`
|
||||
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Visible bool `json:"visible"`
|
||||
Weight int `json:"weight"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Title string `json:"title"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
PageBlocks []PageBlock
|
||||
|
||||
PageBlock struct {
|
||||
BlockID uint64 `json:"blockID,string,omitempty"`
|
||||
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
Style PageBlockStyle `json:"style,omitempty"`
|
||||
Kind string `json:"kind"`
|
||||
XYWH [4]int `json:"xywh"` // x,y,w,h
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Title string `json:"title,omitempty"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
PageMeta struct {
|
||||
AllowPersonalLayouts bool `json:"allowPersonalLayouts"`
|
||||
Notifications map[string]any `json:"notifications,omitempty"`
|
||||
}
|
||||
|
||||
PageBlockStyle struct {
|
||||
Variants map[string]string `json:"variants,omitempty"`
|
||||
Wrap map[string]string `json:"wrap,omitempty"`
|
||||
Border map[string]interface{} `json:"border,omitempty"`
|
||||
}
|
||||
|
||||
PageConfig struct {
|
||||
// How page is presented in the navigation
|
||||
NavItem struct {
|
||||
// Expanded menu
|
||||
Expanded bool `json:"expanded"`
|
||||
Icon *PageConfigIcon `json:"icon,omitempty"`
|
||||
} `json:"navItem"`
|
||||
}
|
||||
|
||||
PageConfigIcon struct {
|
||||
// Icon types and sources
|
||||
//
|
||||
// Note that backed does not enforce or validate all src value (types due to a limited
|
||||
// awareness of capabilities and
|
||||
//
|
||||
// Type: empty or "link" (default):
|
||||
// Indicate that src will contain an absolute or relative link to an icon.
|
||||
// Can also be used for inline images (storing "base64:" prefixed string in source).
|
||||
// This type and reference is not validated by the backend.
|
||||
//
|
||||
// Type: "library"
|
||||
// Source references an icon from a library. Ref's value should be in the following
|
||||
// notation: "font-awesome://<icon-identifier>".
|
||||
// This type and source is not validated by the backend.
|
||||
//
|
||||
// Type: "svg"
|
||||
// SRC contains raw SVG document
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Other types that might be implemented in the future:
|
||||
// "attachment"
|
||||
// Reference (ID) to an existing attachment in local Corteza instance is expected
|
||||
// This type and reference must be validated by the backend.
|
||||
|
||||
Type IconType `json:"type,omitempty"`
|
||||
Src string `json:"src"`
|
||||
|
||||
// Any custom styling that should be applied to the icon
|
||||
Style map[string]string `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
PageFilter struct {
|
||||
NamespaceID uint64 `json:"namespaceID"`
|
||||
PageID []string `json:"pageID,string"`
|
||||
ParentID uint64 `json:"parentID,string,omitempty"`
|
||||
ModuleID uint64 `json:"moduleID,string,omitempty"`
|
||||
Root bool `json:"root,omitempty"`
|
||||
Handle string `json:"handle"`
|
||||
Title string `json:"title"`
|
||||
Query string `json:"query"`
|
||||
|
||||
LabeledIDs []uint64 `json:"-"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*Page) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
|
||||
PageChildrenDeleteStrategy string
|
||||
)
|
||||
|
||||
const (
|
||||
PageChildrenOnDeleteAbort PageChildrenDeleteStrategy = "abort"
|
||||
PageChildrenOnDeleteForce PageChildrenDeleteStrategy = "force"
|
||||
PageChildrenOnDeleteRebase PageChildrenDeleteStrategy = "rebase"
|
||||
PageChildrenOnDeleteCascade PageChildrenDeleteStrategy = "cascade"
|
||||
)
|
||||
|
||||
func (m Page) Clone() *Page {
|
||||
c := &m
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *Page) decodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
for i, block := range p.Blocks {
|
||||
blockID := locale.ContentID(block.BlockID, i)
|
||||
rpl := strings.NewReplacer(
|
||||
"{{blockID}}", strconv.FormatUint(blockID, 10),
|
||||
)
|
||||
|
||||
// - generic page block stuff
|
||||
if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDTitle.Path)); aux != nil {
|
||||
p.Blocks[i].Title = aux.Msg
|
||||
}
|
||||
if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDDescription.Path)); aux != nil {
|
||||
p.Blocks[i].Description = aux.Msg
|
||||
}
|
||||
|
||||
// - automation page block stuff
|
||||
switch block.Kind {
|
||||
case "Automation":
|
||||
bb, _ := block.Options["buttons"].([]interface{})
|
||||
p.decodeRecordListButtons(tt, bb, blockID)
|
||||
|
||||
case "RecordList":
|
||||
bb, _ := block.Options["selectionButtons"].([]interface{})
|
||||
p.decodeRecordListButtons(tt, bb, blockID)
|
||||
|
||||
case "Content":
|
||||
if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDContentBody.Path)); aux != nil {
|
||||
block.Options["body"] = aux.Msg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Page) decodeRecordListButtons(tt locale.ResourceTranslationIndex, bb []interface{}, blockID uint64) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
for j, auxBtn := range bb {
|
||||
btn := auxBtn.(map[string]interface{})
|
||||
|
||||
buttonID := uint64(0)
|
||||
if aux, ok := btn["buttonID"]; ok {
|
||||
buttonID = cast.ToUint64(aux)
|
||||
}
|
||||
buttonID = locale.ContentID(buttonID, j)
|
||||
|
||||
rpl := strings.NewReplacer(
|
||||
"{{blockID}}", strconv.FormatUint(blockID, 10),
|
||||
"{{buttonID}}", strconv.FormatUint(buttonID, 10),
|
||||
)
|
||||
|
||||
if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel.Path)); aux != nil {
|
||||
btn["label"] = aux.Msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Page) encodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = make(locale.ResourceTranslationSet, 0, 12)
|
||||
|
||||
// Button translations don't need to happen here as we don't do anything with buttons at the moment
|
||||
// @todo when we add custom buttons this should change
|
||||
|
||||
// Page blocks
|
||||
for i, block := range p.Blocks {
|
||||
blockID := locale.ContentID(block.BlockID, i)
|
||||
rpl := strings.NewReplacer(
|
||||
"{{blockID}}", strconv.FormatUint(uint64(blockID), 10),
|
||||
)
|
||||
|
||||
// - generic page block stuff
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: p.ResourceTranslation(),
|
||||
Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDTitle.Path),
|
||||
Msg: block.Title,
|
||||
})
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: p.ResourceTranslation(),
|
||||
Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDDescription.Path),
|
||||
Msg: block.Description,
|
||||
})
|
||||
|
||||
// - automation page block stuff
|
||||
|
||||
switch block.Kind {
|
||||
case "Automation":
|
||||
bb, _ := block.Options["buttons"].([]interface{})
|
||||
out = append(out, p.encodeRecordListButtons(bb, blockID)...)
|
||||
|
||||
case "RecordList":
|
||||
bb, _ := block.Options["selectionButtons"].([]interface{})
|
||||
out = append(out, p.encodeRecordListButtons(bb, blockID)...)
|
||||
|
||||
case "Content":
|
||||
body, _ := block.Options["body"].(string)
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: p.ResourceTranslation(),
|
||||
Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDContentBody.Path),
|
||||
Msg: body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Page) encodeRecordListButtons(bb []interface{}, blockID uint64) (out locale.ResourceTranslationSet) {
|
||||
for j, auxBtn := range bb {
|
||||
btn := auxBtn.(map[string]interface{})
|
||||
|
||||
if _, ok := btn["label"]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
buttonID := uint64(0)
|
||||
if aux, ok := btn["buttonID"]; ok {
|
||||
buttonID = cast.ToUint64(aux)
|
||||
}
|
||||
buttonID = locale.ContentID(buttonID, j)
|
||||
|
||||
rpl := strings.NewReplacer(
|
||||
"{{blockID}}", strconv.FormatUint(blockID, 10),
|
||||
"{{buttonID}}", strconv.FormatUint(buttonID, 10),
|
||||
)
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: p.ResourceTranslation(),
|
||||
Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel.Path),
|
||||
Msg: btn["label"].(string),
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Page) setValue(name string, pos uint, value any) (err error) {
|
||||
pp := strings.Split(name, ".")
|
||||
switch pp[0] {
|
||||
case "Blocks":
|
||||
return p.Blocks[cast.ToUint(pp[1])].setValue(strings.Join(pp[2:], "."), pos, value)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Page) Prune(rt string) {
|
||||
for i := len(p.Blocks) - 1; i >= 0; i-- {
|
||||
b := p.Blocks[i]
|
||||
|
||||
switch b.Kind {
|
||||
// Implement the rest when support is needed
|
||||
case "Automation":
|
||||
if rt != "corteza::automation:workflow" {
|
||||
continue
|
||||
}
|
||||
|
||||
p.removeBlock(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Page block utilities
|
||||
func (p *Page) removeBlock(i int) {
|
||||
// do the swap remove thing
|
||||
p.Blocks[i] = p.Blocks[len(p.Blocks)-1]
|
||||
p.Blocks = p.Blocks[:len(p.Blocks)-1]
|
||||
}
|
||||
|
||||
func (b *PageBlock) setValue(name string, pos uint, value any) (err error) {
|
||||
pp := strings.Split(name, ".")
|
||||
|
||||
switch pp[0] {
|
||||
case "Options":
|
||||
return b.setOptionValue(pp[1:], pos, value)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b *PageBlock) setOptionValue(path []string, pos uint, value any) (err error) {
|
||||
setProgressValue := func(k string) {
|
||||
v := make(map[string]any)
|
||||
if !reflect2.IsNil(b.Options[k]) {
|
||||
v = b.Options[k].(map[string]any)
|
||||
}
|
||||
|
||||
v["moduleID"] = cast.ToString(value)
|
||||
b.Options[k] = v
|
||||
}
|
||||
|
||||
switch path[0] {
|
||||
case "feeds":
|
||||
// Get the feed on the correct index in the correct type
|
||||
feed := (b.Options["feeds"].([]any))[cast.ToInt(path[1])].(map[string]any)
|
||||
|
||||
// Getthe feed options in the correct type
|
||||
options := feed["options"].(map[string]any)
|
||||
|
||||
// Set the value
|
||||
options["moduleID"] = cast.ToString(value)
|
||||
|
||||
case "metrics":
|
||||
// Get the metric on the correct index in the correct type
|
||||
metric := (b.Options["metrics"].([]any))[cast.ToInt(path[1])].(map[string]any)
|
||||
|
||||
metric["moduleID"] = cast.ToString(value)
|
||||
|
||||
case "minValue":
|
||||
setProgressValue("minValue")
|
||||
|
||||
case "maxValue":
|
||||
setProgressValue("maxValue")
|
||||
|
||||
case "value":
|
||||
setProgressValue("value")
|
||||
|
||||
case "moduleID", "ModuleID":
|
||||
b.Options["moduleID"] = cast.ToString(value)
|
||||
|
||||
case "chartID", "ChartID":
|
||||
b.Options["chartID"] = cast.ToString(value)
|
||||
|
||||
case "buttons":
|
||||
switch path[2] {
|
||||
case "workflowID", "WorkflowID":
|
||||
button := (b.Options["buttons"].([]any))[cast.ToInt(path[1])].(map[string]interface{})
|
||||
button["workflowID"] = cast.ToString(value)
|
||||
delete(button, "workflow")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByHandle finds page by it's handle
|
||||
func (set PageSet) FindByHandle(handle string) *Page {
|
||||
for i := range set {
|
||||
if set[i].Handle == handle {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bb *PageBlocks) Scan(src any) error { return sql.ParseJSON(src, bb) }
|
||||
func (bb PageBlocks) Value() (driver.Value, error) { return json.Marshal(bb) }
|
||||
|
||||
func (bb *PageMeta) Scan(src any) error { return sql.ParseJSON(src, bb) }
|
||||
func (bb PageMeta) Value() (driver.Value, error) { return json.Marshal(bb) }
|
||||
|
||||
// Helper to extract old encoding to new one
|
||||
func (b *PageBlock) UnmarshalJSON(data []byte) (err error) {
|
||||
type internalPageBlock PageBlock
|
||||
i := struct {
|
||||
internalPageBlock
|
||||
X int `json:"x,omitempty"`
|
||||
Y int `json:"y,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
}{}
|
||||
|
||||
if err = json.Unmarshal(data, &i); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*b = PageBlock(i.internalPageBlock)
|
||||
if i.XYWH[0]+i.XYWH[1]+i.XYWH[2]+i.XYWH[3] > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if i.X+i.Y+i.Width+i.Height > 0 {
|
||||
// Cast old x,y,w,h structure to this:
|
||||
b.XYWH = [4]int{i.X, i.Y, i.Width, i.Height}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (set PageSet) FindByParent(parentID uint64) (out PageSet) {
|
||||
out = PageSet{}
|
||||
for i := range set {
|
||||
if set[i].SelfID == parentID {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RecursiveWalk through all child pages
|
||||
func (set PageSet) RecursiveWalk(parent *Page, fn func(c *Page, parent *Page) error) (err error) {
|
||||
if parent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, page := range set {
|
||||
if page.SelfID != parent.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = fn(page, parent); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = set.RecursiveWalk(page, fn); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (bb *PageConfig) Scan(src any) error { return sql.ParseJSON(src, bb) }
|
||||
func (bb PageConfig) Value() (driver.Value, error) { return json.Marshal(bb) }
|
||||
240
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page_layout.go
generated
vendored
Normal file
240
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page_layout.go
generated
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/cast2"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
PageLayout struct {
|
||||
ID uint64 `json:"pageLayoutID,string"`
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
PageID uint64 `json:"pageID,string"`
|
||||
ParentID uint64 `json:"parentID,string"`
|
||||
Handle string `json:"handle"`
|
||||
Primary bool `json:"primary"`
|
||||
|
||||
Weight int `json:"weight"`
|
||||
|
||||
Meta PageLayoutMeta `json:"meta,omitempty"`
|
||||
|
||||
Config PageLayoutConfig `json:"config"`
|
||||
Blocks PageLayoutBlocks `json:"blocks,omitempty"`
|
||||
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
OwnedBy uint64 `json:"ownedBy,string"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
PageLayoutBlocks []PageLayoutBlock
|
||||
|
||||
PageLayoutBlock struct {
|
||||
BlockID uint64 `json:"blockID,string,omitempty" yaml:"blockID"`
|
||||
XYWH [4]int `json:"xywh" yaml:"xywh"`
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
PageLayoutMeta struct {
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Title string `json:"title"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Description string `json:"description"`
|
||||
|
||||
Style map[string]any `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
PageLayoutButton struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
PageLayoutButtonConfig struct {
|
||||
New PageLayoutButton `json:"new"`
|
||||
Edit PageLayoutButton `json:"edit"`
|
||||
Submit PageLayoutButton `json:"submit"`
|
||||
Delete PageLayoutButton `json:"delete"`
|
||||
Clone PageLayoutButton `json:"clone"`
|
||||
Back PageLayoutButton `json:"back"`
|
||||
}
|
||||
|
||||
PageLayoutConfig struct {
|
||||
Visibility PageLayoutVisibility `json:"visibility"`
|
||||
|
||||
Buttons PageLayoutButtonConfig `json:"buttons"`
|
||||
Actions []PageLayoutAction `json:"actions,omitempty"`
|
||||
|
||||
UseTitle bool `json:"useTitle"`
|
||||
}
|
||||
|
||||
PageLayoutVisibility struct {
|
||||
Expression string `json:"expression"`
|
||||
Roles []string `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
PageLayoutAction struct {
|
||||
ActionID uint64 `json:"actionID,string"`
|
||||
Placement string `json:"placement"`
|
||||
Meta PageLayoutActionMeta `json:"meta"`
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Kind and Params specify the action's behavior and the parameters it
|
||||
// can use for execution
|
||||
Kind string `json:"kind"`
|
||||
Params any `json:"params"`
|
||||
}
|
||||
|
||||
PageLayoutActionMeta struct {
|
||||
// Warning: value of this field is now handled via resource-translation facility
|
||||
// struct field is kept for the convenience for now since it allows us
|
||||
// easy encoding/decoding of the outgoing/incoming values
|
||||
Label string `json:"label"`
|
||||
|
||||
Style map[string]any `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
PageLayoutFilter struct {
|
||||
PageLayoutID []string `json:"pageLayoutID"`
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
PageID uint64 `json:"pageID,string,omitempty"`
|
||||
ParentID uint64 `json:"ParentID,string,omitempty"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
|
||||
LabeledIDs []uint64 `json:"-"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*PageLayout) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
|
||||
func (m PageLayout) Clone() *PageLayout {
|
||||
c := &m
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *PageLayout) decodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
// @note not doing blocks because they are simply copied from the page's index
|
||||
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutConfigButtonsNewLabel.Path); aux != nil {
|
||||
p.Config.Buttons.New.Label = aux.Msg
|
||||
}
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutConfigButtonsEditLabel.Path); aux != nil {
|
||||
p.Config.Buttons.Edit.Label = aux.Msg
|
||||
}
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutConfigButtonsSubmitLabel.Path); aux != nil {
|
||||
p.Config.Buttons.Submit.Label = aux.Msg
|
||||
}
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutConfigButtonsDeleteLabel.Path); aux != nil {
|
||||
p.Config.Buttons.Delete.Label = aux.Msg
|
||||
}
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutConfigButtonsCloneLabel.Path); aux != nil {
|
||||
p.Config.Buttons.Clone.Label = aux.Msg
|
||||
}
|
||||
if aux = tt.FindByKey(LocaleKeyPageLayoutConfigButtonsBackLabel.Path); aux != nil {
|
||||
p.Config.Buttons.Back.Label = aux.Msg
|
||||
}
|
||||
|
||||
for i, action := range p.Config.Actions {
|
||||
actionID := locale.ContentID(action.ActionID, i)
|
||||
rpl := strings.NewReplacer(
|
||||
"{{actionID}}", strconv.FormatUint(actionID, 10),
|
||||
)
|
||||
|
||||
if aux = tt.FindByKey(rpl.Replace(LocaleKeyPageLayoutConfigActionsActionIDMetaLabel.Path)); aux != nil {
|
||||
p.Config.Actions[i].Meta.Label = aux.Msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PageLayout) encodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = make(locale.ResourceTranslationSet, 0, 8)
|
||||
|
||||
// @note not doing blocks because they are simply copied from the page's index
|
||||
|
||||
// Actions
|
||||
for i, action := range p.Config.Actions {
|
||||
actionID := locale.ContentID(action.ActionID, i)
|
||||
rpl := strings.NewReplacer(
|
||||
"{{actionID}}", strconv.FormatUint(actionID, 10),
|
||||
)
|
||||
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: p.ResourceTranslation(),
|
||||
Key: rpl.Replace(LocaleKeyPageLayoutConfigActionsActionIDMetaLabel.Path),
|
||||
Msg: action.Meta.Label,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByHandle finds pageLayout by it's handle
|
||||
func (set PageLayoutSet) FindByHandle(handle string) *PageLayout {
|
||||
for i := range set {
|
||||
if set[i].Handle == handle {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PageLayout) getValue(name string, pos uint) (any, error) {
|
||||
switch name {
|
||||
case "selfD", "SelfD":
|
||||
return r.PageID, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (r *PageLayout) setValue(name string, pos uint, value any) (err error) {
|
||||
switch name {
|
||||
case "selfID", "SelfID":
|
||||
return cast2.Uint64(value, &r.PageID)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bb *PageLayoutConfig) Scan(src any) error { return sql.ParseJSON(src, &bb) }
|
||||
func (bb PageLayoutConfig) Value() (driver.Value, error) { return json.Marshal(bb) }
|
||||
|
||||
func (vv *PageLayoutMeta) Scan(src any) error { return sql.ParseJSON(src, &vv) }
|
||||
func (vv PageLayoutMeta) Value() (driver.Value, error) { return json.Marshal(vv) }
|
||||
|
||||
func (bb *PageLayoutBlocks) Scan(src any) error { return sql.ParseJSON(src, bb) }
|
||||
func (bb PageLayoutBlocks) Value() (driver.Value, error) { return json.Marshal(bb) }
|
||||
21
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/parsers.go
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/parsers.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package types
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
func ParsePageLayoutMeta(ss []string) (p PageLayoutMeta, err error) {
|
||||
p = PageLayoutMeta{}
|
||||
return p, parseStringsInput(ss, &p)
|
||||
}
|
||||
|
||||
func ParsePageMeta(ss []string) (p PageMeta, err error) {
|
||||
p = PageMeta{}
|
||||
return p, parseStringsInput(ss, &p)
|
||||
}
|
||||
|
||||
func parseStringsInput(ss []string, p interface{}) (err error) {
|
||||
if len(ss) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(ss[0]), &p)
|
||||
}
|
||||
311
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/rbac.gen.go
generated
vendored
Normal file
311
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/rbac.gen.go
generated
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
// Component struct serves as a virtual resource type for the compose component
|
||||
//
|
||||
// This struct is auto-generated
|
||||
Component struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
_ = fmt.Printf
|
||||
_ = strconv.FormatUint
|
||||
)
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Chart by calling ChartRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:chart/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Chart) RbacResource() string {
|
||||
return ChartRbacResource(r.NamespaceID, r.ID)
|
||||
}
|
||||
|
||||
// ChartRbacResource returns string representation of RBAC resource for Chart
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:chart/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ChartRbacResource(namespaceID uint64, id uint64) string {
|
||||
cpts := []interface{}{ChartResourceType}
|
||||
if namespaceID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(namespaceID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ChartRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func ChartRbacResourceTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Module by calling ModuleRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Module) RbacResource() string {
|
||||
return ModuleRbacResource(r.NamespaceID, r.ID)
|
||||
}
|
||||
|
||||
// ModuleRbacResource returns string representation of RBAC resource for Module
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ModuleRbacResource(namespaceID uint64, id uint64) string {
|
||||
cpts := []interface{}{ModuleResourceType}
|
||||
if namespaceID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(namespaceID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ModuleRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func ModuleRbacResourceTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for ModuleField by calling ModuleFieldRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:module-field/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r ModuleField) RbacResource() string {
|
||||
return ModuleFieldRbacResource(r.NamespaceID, r.ModuleID, r.ID)
|
||||
}
|
||||
|
||||
// ModuleFieldRbacResource returns string representation of RBAC resource for ModuleField
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:module-field/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ModuleFieldRbacResource(namespaceID uint64, moduleID uint64, id uint64) string {
|
||||
cpts := []interface{}{ModuleFieldResourceType}
|
||||
if namespaceID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(namespaceID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if moduleID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(moduleID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ModuleFieldRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func ModuleFieldRbacResourceTpl() string {
|
||||
return "%s/%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Namespace by calling NamespaceRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:namespace/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Namespace) RbacResource() string {
|
||||
return NamespaceRbacResource(r.ID)
|
||||
}
|
||||
|
||||
// NamespaceRbacResource returns string representation of RBAC resource for Namespace
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:namespace/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func NamespaceRbacResource(id uint64) string {
|
||||
cpts := []interface{}{NamespaceResourceType}
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(NamespaceRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func NamespaceRbacResourceTpl() string {
|
||||
return "%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Page by calling PageRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:page/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Page) RbacResource() string {
|
||||
return PageRbacResource(r.NamespaceID, r.ID)
|
||||
}
|
||||
|
||||
// PageRbacResource returns string representation of RBAC resource for Page
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:page/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func PageRbacResource(namespaceID uint64, id uint64) string {
|
||||
cpts := []interface{}{PageResourceType}
|
||||
if namespaceID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(namespaceID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(PageRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func PageRbacResourceTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for PageLayout by calling PageLayoutRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:page-layout/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r PageLayout) RbacResource() string {
|
||||
return PageLayoutRbacResource(r.NamespaceID, r.PageID, r.ID)
|
||||
}
|
||||
|
||||
// PageLayoutRbacResource returns string representation of RBAC resource for PageLayout
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:page-layout/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func PageLayoutRbacResource(namespaceID uint64, pageID uint64, id uint64) string {
|
||||
cpts := []interface{}{PageLayoutResourceType}
|
||||
if namespaceID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(namespaceID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if pageID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(pageID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(PageLayoutRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func PageLayoutRbacResourceTpl() string {
|
||||
return "%s/%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Record by calling RecordRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:record/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Record) RbacResource() string {
|
||||
return RecordRbacResource(r.NamespaceID, r.ModuleID, r.ID)
|
||||
}
|
||||
|
||||
// RecordRbacResource returns string representation of RBAC resource for Record
|
||||
//
|
||||
// RBAC resource is in the corteza::compose:record/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func RecordRbacResource(namespaceID uint64, moduleID uint64, id uint64) string {
|
||||
cpts := []interface{}{RecordResourceType}
|
||||
if namespaceID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(namespaceID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if moduleID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(moduleID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(RecordRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func RecordRbacResourceTpl() string {
|
||||
return "%s/%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Component by calling ComponentRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::compose/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Component) RbacResource() string {
|
||||
return ComponentRbacResource()
|
||||
}
|
||||
|
||||
// ComponentRbacResource returns string representation of RBAC resource for Component
|
||||
//
|
||||
// RBAC resource is in the corteza::compose/ format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ComponentRbacResource() string {
|
||||
return ComponentResourceType + "/"
|
||||
|
||||
}
|
||||
|
||||
func ComponentRbacResourceTpl() string {
|
||||
return "%s"
|
||||
}
|
||||
485
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/record.go
generated
vendored
Normal file
485
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/record.go
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
bulkPreProcess func(m, r *Record) (*Record, error)
|
||||
|
||||
OperationType string
|
||||
|
||||
RecordBulkOperation struct {
|
||||
Record *Record
|
||||
RecordID uint64
|
||||
NamespaceID uint64
|
||||
ModuleID uint64
|
||||
|
||||
LinkBy string
|
||||
Operation OperationType
|
||||
ID string
|
||||
}
|
||||
RecordBulkOperationResult struct {
|
||||
Record *Record
|
||||
Error error
|
||||
ValueError *RecordValueErrorSet
|
||||
DuplicationError *RecordValueErrorSet
|
||||
}
|
||||
|
||||
RecordBulk struct {
|
||||
RefField string `json:"refField,omitempty"`
|
||||
IDPrefix string `json:"idPrefix,omitempty"`
|
||||
Set RecordSet `json:"set,omitempty"`
|
||||
}
|
||||
|
||||
RecordBulkSet []*RecordBulk
|
||||
|
||||
// Record is a stored row in the `record` table
|
||||
Record struct {
|
||||
ID uint64 `json:"recordID,string"`
|
||||
ModuleID uint64 `json:"moduleID,string"`
|
||||
|
||||
Revision int `json:"revision,omitempty"`
|
||||
|
||||
module *Module
|
||||
|
||||
Values RecordValueSet `json:"values,omitempty"`
|
||||
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
|
||||
OwnedBy uint64 `json:"ownedBy,string"`
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
CreatedBy uint64 `json:"createdBy,string" `
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
UpdatedBy uint64 `json:"updatedBy,string,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
DeletedBy uint64 `json:"deletedBy,string,omitempty"`
|
||||
}
|
||||
|
||||
RecordSummary struct {
|
||||
Name string
|
||||
|
||||
Min float64
|
||||
Max float64
|
||||
Avg float64
|
||||
Sum float64
|
||||
|
||||
EmptyCount int
|
||||
NotEmptyCount int
|
||||
UniqueCount int
|
||||
|
||||
Earliest time.Time
|
||||
Latest time.Time
|
||||
|
||||
Count int
|
||||
}
|
||||
|
||||
RecordSummaryReq struct {
|
||||
Name string `json:"name"`
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
RecordFilter struct {
|
||||
Summaries []RecordSummaryReq `json:"summaries,omitempty"`
|
||||
ModuleID uint64 `json:"moduleID,string"`
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
Query string `json:"query"`
|
||||
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(*Record) (bool, error) `json:"-"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
|
||||
SensitiveRecord struct {
|
||||
RecordID uint64
|
||||
Values []map[string]any
|
||||
}
|
||||
|
||||
SensitiveRecordSet struct {
|
||||
// Contextual metadata
|
||||
ConnectionID uint64
|
||||
Module *Module
|
||||
Namespace *Namespace
|
||||
|
||||
Records []SensitiveRecord
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
OperationTypeCreate OperationType = "create"
|
||||
OperationTypeUpdate OperationType = "update"
|
||||
OperationTypeDelete OperationType = "delete"
|
||||
OperationTypePatch OperationType = "patch"
|
||||
OperationTypeUndelete OperationType = "undelete"
|
||||
|
||||
DateOnlyLayout = "2006-01-02"
|
||||
TimeOnlyLayout = "15:04:05"
|
||||
)
|
||||
|
||||
func (f RecordFilter) ToConstraintedFilter(c map[string][]any) filter.Filter {
|
||||
return filter.Generic(
|
||||
// combine constraints with namespace and module
|
||||
filter.WithConstraints(c),
|
||||
filter.WithExpression(f.Query),
|
||||
filter.WithOrderBy(f.Sort),
|
||||
filter.WithLimit(f.Limit),
|
||||
filter.WithCursor(f.PageCursor),
|
||||
filter.WithMetaConstraints(f.Meta),
|
||||
filter.WithStateConstraint("deletedAt", f.Deleted),
|
||||
)
|
||||
}
|
||||
|
||||
// UserIDs returns a slice of user IDs from all items in the set
|
||||
func (set RecordSet) UserIDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, 0)
|
||||
|
||||
loop:
|
||||
for i := range set {
|
||||
for _, id := range IDs {
|
||||
if id == set[i].OwnedBy {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
IDs = append(IDs, set[i].OwnedBy)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Sets/updates module ptr
|
||||
//
|
||||
// Only if not previously set and if matches record specs
|
||||
func (r *Record) SetModule(m *Module) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if (r.module == nil || r.module.ID == m.ID) && r.ModuleID == m.ID {
|
||||
r.module = m
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Record) GetModule() *Module {
|
||||
return r.module
|
||||
}
|
||||
|
||||
func (r Record) Clone() *Record {
|
||||
c := &r
|
||||
c.Values = r.Values.Clone()
|
||||
return c
|
||||
}
|
||||
|
||||
func (r *Record) getValue(name string, pos uint) (any, error) {
|
||||
if r.Values == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if val := r.Values.Get(name, pos); val != nil {
|
||||
return val.Value, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Record) getValues(name string) ([]any, error) {
|
||||
if r.Values == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if val := r.Values.GetAll(name); val != nil {
|
||||
aux := make([]any, 0, len(val))
|
||||
for _, v := range val {
|
||||
aux = append(aux, v.Value)
|
||||
}
|
||||
|
||||
return aux, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CountValues returns how many values per field are there
|
||||
func (r *Record) CountValues() (pos map[string]uint) {
|
||||
var (
|
||||
mod = r.GetModule()
|
||||
)
|
||||
|
||||
pos = map[string]uint{
|
||||
"ID": 1,
|
||||
"moduleID": 1,
|
||||
"namespaceID": 1,
|
||||
"meta": 1,
|
||||
"revision": 1,
|
||||
"createdAt": 1,
|
||||
"createdBy": 1,
|
||||
"updatedAt": 1,
|
||||
"updatedBy": 1,
|
||||
"deletedAt": 1,
|
||||
"deletedBy": 1,
|
||||
"ownedBy": 1,
|
||||
}
|
||||
|
||||
// if mod == nil {
|
||||
// // count record values
|
||||
// // only when module is known
|
||||
// return
|
||||
// }
|
||||
|
||||
if mod != nil {
|
||||
for _, f := range mod.Fields {
|
||||
pos[f.Name] = 0
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range r.Values {
|
||||
pos[val.Name]++
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Record) setValue(name string, pos uint, value any) (err error) {
|
||||
if reflect2.IsNil(value) {
|
||||
r.Values, _ = r.Values.Filter(func(rv *RecordValue) (bool, error) {
|
||||
if rv.Name == name && rv.Place == pos {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rv := &RecordValue{Name: name, Place: pos}
|
||||
|
||||
if cv, ok := value.(*RecordValue); ok {
|
||||
rv = cv
|
||||
} else {
|
||||
var auxv string
|
||||
switch aux := value.(type) {
|
||||
case *time.Time:
|
||||
auxv = aux.Format(time.RFC3339)
|
||||
|
||||
case time.Time:
|
||||
auxv = aux.Format(time.RFC3339)
|
||||
|
||||
default:
|
||||
auxv, err = cast.ToStringE(aux)
|
||||
|
||||
if r.module != nil {
|
||||
f := r.module.Fields.FindByName(name)
|
||||
if f != nil {
|
||||
switch f.Kind {
|
||||
case "DateTime":
|
||||
// @note temporary solution to make timestamps consistent; we should handle
|
||||
// timezones (or the lack of) more properly
|
||||
parseAndFormat := func(value interface{}, format string) string {
|
||||
auxt, err := cast.ToTimeE(value)
|
||||
if err != nil || auxt.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return auxt.Format(format)
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.IsDateOnly():
|
||||
auxv = parseAndFormat(auxv, DateOnlyLayout)
|
||||
|
||||
case f.IsTimeOnly():
|
||||
auxt, err := time.Parse(TimeOnlyLayout, auxv)
|
||||
if err != nil || auxt.IsZero() {
|
||||
auxv = ""
|
||||
}
|
||||
|
||||
default:
|
||||
auxv = parseAndFormat(auxv, time.RFC3339)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rv.Value = auxv
|
||||
}
|
||||
|
||||
// Try to utilize the module when possible
|
||||
// It can be omitted for some cases for easier test cases
|
||||
if r.module != nil {
|
||||
f := r.module.Fields.FindByName(name)
|
||||
if f != nil {
|
||||
switch f.Kind {
|
||||
case "Record", "User", "File":
|
||||
rv.Ref = cast.ToUint64(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Values = r.Values.Set(rv)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r Record) Dict() map[string]interface{} {
|
||||
dict := map[string]interface{}{
|
||||
"ID": r.ID,
|
||||
"recordID": r.ID,
|
||||
"moduleID": r.ModuleID,
|
||||
"revision": r.Revision,
|
||||
"meta": r.Meta,
|
||||
"namespaceID": r.NamespaceID,
|
||||
"ownedBy": r.OwnedBy,
|
||||
"createdAt": r.CreatedAt,
|
||||
"createdBy": r.CreatedBy,
|
||||
"updatedAt": r.UpdatedAt,
|
||||
"updatedBy": r.UpdatedBy,
|
||||
"deletedAt": r.DeletedAt,
|
||||
"deletedBy": r.DeletedBy,
|
||||
}
|
||||
|
||||
if r.module != nil {
|
||||
dict["values"] = r.Values.Dict(r.module.Fields)
|
||||
}
|
||||
|
||||
return dict
|
||||
}
|
||||
|
||||
// UnmarshalJSON for custom record deserialization
|
||||
//
|
||||
// Due to https://github.com/golang/go/issues/21092, we should manually reset the given record value set.
|
||||
// If this is skipped there is a chance of data corruption; ie. wrong value is removed/edited
|
||||
func (r *Record) UnmarshalJSON(data []byte) error {
|
||||
// Reset value set
|
||||
r.Values = nil
|
||||
|
||||
// Deserialize to r (*Record) via auxRecord auxiliary record type alias
|
||||
//
|
||||
// This prevents inf. loop where json.Unmarshal directly on Record type
|
||||
// calls this function
|
||||
type auxRecord Record
|
||||
return json.Unmarshal(data, &struct{ *auxRecord }{auxRecord: (*auxRecord)(r)})
|
||||
}
|
||||
|
||||
func (m RecordSummary) MarshalJSON() ([]byte, error) {
|
||||
out := map[string]any{
|
||||
"name": m.Name,
|
||||
}
|
||||
|
||||
switch m.Name {
|
||||
case "min":
|
||||
out["value"] = cast.ToString(m.Min)
|
||||
case "max":
|
||||
out["value"] = cast.ToString(m.Max)
|
||||
case "avg":
|
||||
out["value"] = cast.ToString(m.Avg)
|
||||
case "sum":
|
||||
out["value"] = cast.ToString(m.Sum)
|
||||
case "emptyCount":
|
||||
out["value"] = cast.ToString(m.EmptyCount)
|
||||
case "notEmptyCount":
|
||||
out["value"] = cast.ToString(m.NotEmptyCount)
|
||||
case "count":
|
||||
out["value"] = cast.ToString(m.Count)
|
||||
case "uniqueCount":
|
||||
out["value"] = cast.ToString(m.UniqueCount)
|
||||
case "earliest":
|
||||
out["value"] = cast.ToString(m.Earliest)
|
||||
case "latest":
|
||||
out["value"] = cast.ToString(m.Latest)
|
||||
}
|
||||
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
// ToBulkOperations converts BulkRecordSet to a list of BulkRecordOperations
|
||||
func (set RecordBulkSet) ToBulkOperations(dftModule uint64, dftNamespace uint64) (oo []*RecordBulkOperation, err error) {
|
||||
for _, br := range set {
|
||||
// can't use for loop's index, since some records can already have an ID
|
||||
i := 0
|
||||
for _, rr := range br.Set {
|
||||
// No use in allowing cross-namespace record creation.
|
||||
rr.NamespaceID = dftNamespace
|
||||
|
||||
// default module
|
||||
if rr.ModuleID == 0 {
|
||||
rr.ModuleID = dftModule
|
||||
}
|
||||
b := &RecordBulkOperation{
|
||||
Record: rr,
|
||||
Operation: OperationTypeUpdate,
|
||||
LinkBy: br.RefField,
|
||||
}
|
||||
|
||||
if rr.ID == 0 {
|
||||
b.ID = fmt.Sprintf("%s:%d", br.IDPrefix, i)
|
||||
i++
|
||||
} else {
|
||||
b.ID = strconv.FormatUint(rr.ID, 10)
|
||||
}
|
||||
|
||||
// If no RecordID is defined, we should create it
|
||||
if rr.ID == 0 {
|
||||
b.Operation = OperationTypeCreate
|
||||
}
|
||||
|
||||
if rr.DeletedAt != nil {
|
||||
b.Operation = OperationTypeDelete
|
||||
}
|
||||
|
||||
oo = append(oo, b)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetValuesByName filters values for records by names
|
||||
func (set RecordSet) GetValuesByName(names ...string) (out map[uint64]RecordValueSet) {
|
||||
nameMap := make(map[string]bool)
|
||||
for _, n := range names {
|
||||
if len(n) > 0 {
|
||||
nameMap[n] = true
|
||||
}
|
||||
}
|
||||
|
||||
out = make(map[uint64]RecordValueSet)
|
||||
err := set.Walk(func(rec *Record) error {
|
||||
_ = rec.Values.Walk(func(val *RecordValue) error {
|
||||
if val != nil && nameMap[val.Name] {
|
||||
val.RecordID = rec.ID
|
||||
|
||||
out[val.RecordID] = append(out[val.RecordID], val)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
346
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/record_detect_duplicates.go
generated
vendored
Normal file
346
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/record_detect_duplicates.go
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/str"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
deDup struct {
|
||||
ls localeService
|
||||
}
|
||||
|
||||
localeService interface {
|
||||
T(ctx context.Context, ns, key string, rr ...string) string
|
||||
}
|
||||
|
||||
DeDupRule struct {
|
||||
Name DeDupRuleName `json:"name"`
|
||||
Strict bool `json:"strict"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ConstraintSet DeDupRuleConstraintSet `json:"constraints"`
|
||||
}
|
||||
|
||||
DeDupRuleConstraint struct {
|
||||
Attribute string `json:"attribute"`
|
||||
Modifier DeDupValueModifier `json:"modifier"`
|
||||
MultiValue DeDupMultiValueConstraint `json:"multiValue"`
|
||||
}
|
||||
|
||||
DeDupRuleConstraintSet []*DeDupRuleConstraint
|
||||
|
||||
// DeDupRuleName represent the identifier for duplicate detection rule
|
||||
DeDupRuleName string
|
||||
|
||||
// DeDupValueModifier represent the algorithm used to check value string
|
||||
DeDupValueModifier string
|
||||
|
||||
// DeDupMultiValueConstraint for matching multi values accordingly
|
||||
DeDupMultiValueConstraint string
|
||||
|
||||
// DeDupIssueKind based on strict mode rule or duplication config
|
||||
DeDupIssueKind string
|
||||
)
|
||||
|
||||
const (
|
||||
ignoreCase DeDupValueModifier = "ignore-case"
|
||||
caseSensitive DeDupValueModifier = "case-sensitive"
|
||||
fuzzyMatch DeDupValueModifier = "fuzzy-match"
|
||||
soundsLike DeDupValueModifier = "sounds-like"
|
||||
|
||||
oneOf DeDupMultiValueConstraint = "one-of"
|
||||
equal DeDupMultiValueConstraint = "equal"
|
||||
|
||||
deDupWarning DeDupIssueKind = "duplication_warning"
|
||||
deDupError DeDupIssueKind = "duplication_error"
|
||||
)
|
||||
|
||||
func DeDup() *deDup {
|
||||
return &deDup{
|
||||
ls: locale.Global(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d deDup) CheckDuplication(ctx context.Context, rules DeDupRuleSet, rec Record, rr RecordSet) (out *RecordValueErrorSet, err error) {
|
||||
out = &RecordValueErrorSet{}
|
||||
err = rules.Walk(func(rule *DeDupRule) error {
|
||||
if rule.HasAttributes() {
|
||||
values := rr.GetValuesByName(distinct(rule.Attributes())...)
|
||||
|
||||
for valID, value := range values {
|
||||
if valID == rec.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
set := rule.validateValue(ctx, d.ls, rec, value)
|
||||
|
||||
if !set.IsValid() {
|
||||
out.Push(set.Set...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if out.IsValid() {
|
||||
out = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rule DeDupIssueKind) String() string {
|
||||
return string(rule)
|
||||
}
|
||||
|
||||
func (rule DeDupRule) HasAttributes() bool {
|
||||
return len(rule.ConstraintSet) > 0 && len(rule.Attributes()) > 0
|
||||
}
|
||||
|
||||
func (rule DeDupRule) Attributes() (out []string) {
|
||||
for _, c := range rule.ConstraintSet {
|
||||
out = append(out, c.Attribute)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rule DeDupRule) IsStrict() bool {
|
||||
return rule.Strict
|
||||
}
|
||||
|
||||
func (rule DeDupRule) IssueKind() string {
|
||||
out := deDupWarning
|
||||
if rule.Strict {
|
||||
out = deDupError
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (rule DeDupRule) IssueMessage() (out string) {
|
||||
return "record-field.errors.duplicateValue"
|
||||
}
|
||||
|
||||
func (rule DeDupRule) IssueMultivalueMessage() (out string) {
|
||||
return "record-field.errors.duplicateValueInSet"
|
||||
}
|
||||
|
||||
func (rule DeDupRule) String() string {
|
||||
return fmt.Sprintf("%s duplicate detection on `%s` field", rule.Name, strings.Join(rule.Attributes(), ", "))
|
||||
}
|
||||
|
||||
// validateValue will check duplicate detection based on rules name
|
||||
func (rule DeDupRule) validateValue(ctx context.Context, ls localeService, rec Record, vv RecordValueSet) (out *RecordValueErrorSet) {
|
||||
return rule.checkDuplication(ctx, ls, rec, vv)
|
||||
}
|
||||
|
||||
func (rule DeDupRule) checkDuplication(ctx context.Context, ls localeService, rec Record, vv RecordValueSet) (out *RecordValueErrorSet) {
|
||||
var (
|
||||
recVal = rec.Values
|
||||
)
|
||||
|
||||
out = &RecordValueErrorSet{}
|
||||
|
||||
for _, c := range rule.ConstraintSet {
|
||||
rvv := recVal.FilterByName(c.Attribute)
|
||||
if rvv.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
valErr = &RecordValueErrorSet{}
|
||||
)
|
||||
|
||||
existingVv := vv.FilterByName(c.Attribute)
|
||||
moduleField := rec.module.Fields.FindByName(c.Attribute)
|
||||
|
||||
if moduleField.Multi && c.IsAllEqual() {
|
||||
multiValErr := rule.multiValueAllEqual(ctx, ls, c, rvv, existingVv)
|
||||
out.Push(multiValErr)
|
||||
} else {
|
||||
rvvValueMap := make(map[string]*RecordValue)
|
||||
_ = rvv.Walk(func(rv *RecordValue) error {
|
||||
if len(rv.Value) > 0 {
|
||||
rvvValueMap[rv.Value] = rv
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
_ = vv.Walk(func(v *RecordValue) error {
|
||||
if v.RecordID == rec.ID {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rv, exists := rvvValueMap[v.Value]; exists && matchValue(c.Modifier, rv.Value, v.Value) {
|
||||
valErr.Push(RecordValueError{
|
||||
Kind: rule.IssueKind(),
|
||||
Message: ls.T(ctx, "compose", rule.IssueMessage()),
|
||||
Meta: map[string]interface{}{
|
||||
"field": v.Name,
|
||||
"value": v.Value,
|
||||
"dupValueField": rv.Name,
|
||||
"recordID": cast.ToString(v.RecordID),
|
||||
"rule": rule.String(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 1. multiValue is empty, then all value needs to be a match then return error/warning
|
||||
// 2. multiValue is oneOf, then one or more value needs to be a match then return error/warning
|
||||
// 3. multiValue is equal, then all value needs to be a match then return error/warning
|
||||
if (!valErr.IsValid() && (!c.HasMultiValue() || c.IsAllEqual()) && valErr.Len() == rvv.Len()) || (c.IsOneOf() && valErr.Len() > 0) {
|
||||
out.Push(valErr.Set...)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (rule DeDupRule) multiValueAllEqual(ctx context.Context, ls localeService, c *DeDupRuleConstraint, rvv RecordValueSet, existingVv RecordValueSet) (out RecordValueError) {
|
||||
var valErr = RecordValueError{}
|
||||
|
||||
if rvv.Len() != existingVv.Len() {
|
||||
return valErr
|
||||
}
|
||||
|
||||
rvvmap := make(map[string]int)
|
||||
existingVvmap := make(map[string]int)
|
||||
|
||||
dupValues := recordValueFrequencyMap(rvvmap, c.Modifier, rvv)
|
||||
_ = recordValueFrequencyMap(existingVvmap, c.Modifier, existingVv)
|
||||
|
||||
if matchRecordValueFrequencyMap(rvvmap, existingVvmap) {
|
||||
valErr = RecordValueError{
|
||||
Kind: rule.IssueKind(),
|
||||
Message: ls.T(ctx, "compose", rule.IssueMultivalueMessage()),
|
||||
Meta: map[string]interface{}{
|
||||
"field": c.Attribute,
|
||||
"value": strings.Join(dupValues, ", "),
|
||||
"dupValueField": c.Attribute,
|
||||
"rule": rule.String(),
|
||||
},
|
||||
}
|
||||
|
||||
return valErr
|
||||
}
|
||||
|
||||
return valErr
|
||||
}
|
||||
|
||||
func (dr DeDupRuleSet) Validate() (err error) {
|
||||
return dr.Walk(func(rule *DeDupRule) (err error) {
|
||||
if !rule.HasAttributes() {
|
||||
err = fmt.Errorf("deduplication not valid (no constraints)")
|
||||
return
|
||||
}
|
||||
|
||||
for _, a := range rule.Attributes() {
|
||||
if a == "" {
|
||||
err = fmt.Errorf("deduplication not valid (invalid field)")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (c DeDupRuleConstraint) HasMultiValue() bool {
|
||||
switch c.MultiValue {
|
||||
case oneOf, equal:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c DeDupRuleConstraint) IsAllEqual() bool {
|
||||
return c.MultiValue == equal
|
||||
}
|
||||
|
||||
func (c DeDupRuleConstraint) IsOneOf() bool {
|
||||
return c.MultiValue == oneOf
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) SetMetaID(id uint64) {
|
||||
if v.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range v.Set {
|
||||
if val.Meta != nil {
|
||||
if _, ok := val.Meta["id"]; !ok {
|
||||
val.Meta["id"] = cast.ToString(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) HasStrictErrors() bool {
|
||||
return v.HasKind(deDupError.String())
|
||||
}
|
||||
|
||||
// distinct only list the different (distinct) values
|
||||
func distinct(input []string) (out []string) {
|
||||
keys := make(map[string]bool)
|
||||
for _, val := range input {
|
||||
if _, ok := keys[val]; !ok {
|
||||
keys[val] = true
|
||||
out = append(out, val)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// matchValue will check if the input matches with target string as per the modifier
|
||||
func matchValue(modifier DeDupValueModifier, input string, target string) bool {
|
||||
switch modifier {
|
||||
case ignoreCase:
|
||||
return str.Match(input, target, str.CaseInSensitiveMatch)
|
||||
case caseSensitive:
|
||||
return str.Match(input, target, str.CaseSensitiveMatch)
|
||||
case fuzzyMatch:
|
||||
return str.Match(input, target, str.LevenshteinDistance)
|
||||
case soundsLike:
|
||||
return str.Match(input, target, str.Soundex)
|
||||
default:
|
||||
// ignoreCase as default, if not specified
|
||||
return str.Match(input, target, str.CaseInSensitiveMatch)
|
||||
}
|
||||
}
|
||||
|
||||
func recordValueFrequencyMap(rvFreqMap map[string]int, c DeDupValueModifier, vv RecordValueSet) (values []string) {
|
||||
for _, v := range vv {
|
||||
values = append(values, v.Value)
|
||||
|
||||
if c == ignoreCase {
|
||||
v.Value = strings.ToLower(v.Value)
|
||||
}
|
||||
|
||||
rvFreqMap[v.Value]++
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
func matchRecordValueFrequencyMap(a, b map[string]int) (ok bool) {
|
||||
for k, keyCount := range a {
|
||||
_, ok = b[k]
|
||||
if !ok || keyCount != b[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
444
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/record_value.go
generated
vendored
Normal file
444
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/record_value.go
generated
vendored
Normal file
@@ -0,0 +1,444 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
// RecordValue is a stored row in the `record_value` table
|
||||
RecordValue struct {
|
||||
RecordID uint64 `json:"-"`
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Ref uint64 `json:"-"`
|
||||
Place uint `json:"place,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
|
||||
Updated bool `json:"-"`
|
||||
OldValue string `json:"-"`
|
||||
}
|
||||
|
||||
RecordValueFilter struct {
|
||||
RecordID []uint64
|
||||
Deleted filter.State `json:"deleted"`
|
||||
Limit uint
|
||||
}
|
||||
)
|
||||
|
||||
func (v RecordValue) IsUpdated() bool {
|
||||
return v.Updated
|
||||
}
|
||||
|
||||
func (v RecordValue) IsDeleted() bool {
|
||||
return v.DeletedAt != nil
|
||||
}
|
||||
|
||||
func (v RecordValue) Clone() *RecordValue {
|
||||
return &RecordValue{
|
||||
RecordID: v.RecordID,
|
||||
Name: v.Name,
|
||||
Value: v.Value,
|
||||
Ref: v.Ref,
|
||||
Place: v.Place,
|
||||
DeletedAt: v.DeletedAt,
|
||||
Updated: v.Updated,
|
||||
OldValue: v.OldValue,
|
||||
}
|
||||
}
|
||||
|
||||
func (v RecordValue) Cast(f *ModuleField) (interface{}, error) {
|
||||
if f == nil {
|
||||
// safe fallback to string
|
||||
return v.Value, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.IsRef():
|
||||
if v.Ref == 0 && len(v.Value) > 0 {
|
||||
// Cover cases when we Ref is not set but Value is
|
||||
// This happens when RVS is transferred as JSON
|
||||
v.Ref, _ = strconv.ParseUint(v.Value, 10, 64)
|
||||
}
|
||||
|
||||
return v.Ref, nil
|
||||
|
||||
case f.IsDateTime():
|
||||
if v.Value == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return cast.ToTimeE(v.Value)
|
||||
|
||||
case f.IsDateOnly():
|
||||
if v.Value == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return time.Parse(DateOnlyLayout, v.Value)
|
||||
|
||||
case f.IsTimeOnly():
|
||||
if v.Value == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return time.Parse(TimeOnlyLayout, v.Value)
|
||||
|
||||
case f.IsBoolean():
|
||||
return expr.CastToBoolean(v.Value)
|
||||
|
||||
case f.IsNumeric():
|
||||
// @todo !! This is temporary; precision and scale require a rework
|
||||
if f.Options.Precision() == 0 {
|
||||
return cast.ToInt64E(v.Value)
|
||||
}
|
||||
|
||||
return cast.ToFloat64E(v.Value)
|
||||
|
||||
default:
|
||||
return v.Value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (set RecordValueSet) Clone() (vv RecordValueSet) {
|
||||
vv = make(RecordValueSet, len(set))
|
||||
for i := range set {
|
||||
vv[i] = set[i].Clone()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (set RecordValueSet) FilterByName(name string) (vv RecordValueSet) {
|
||||
for i := range set {
|
||||
if set[i].Name == name {
|
||||
vv = append(vv, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (set RecordValueSet) FilterByRecordID(recordID uint64) (vv RecordValueSet) {
|
||||
// Make sure we never return nil
|
||||
vv = RecordValueSet{}
|
||||
|
||||
for i := range set {
|
||||
if set[i].RecordID == recordID {
|
||||
vv = append(vv, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Replace existing values, remove extra
|
||||
func (set RecordValueSet) Replace(name string, values ...string) (vv RecordValueSet) {
|
||||
for i := range set {
|
||||
if set[i].Name != name {
|
||||
// copy values from other fields
|
||||
vv = append(vv, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
for p, v := range values {
|
||||
vv = append(vv, &RecordValue{
|
||||
Name: name,
|
||||
Value: v,
|
||||
Place: uint(p),
|
||||
Updated: true,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Set updates existing value or creates a new one
|
||||
func (set RecordValueSet) Set(v *RecordValue) RecordValueSet {
|
||||
for i := range set {
|
||||
if set[i].Name != v.Name {
|
||||
continue
|
||||
}
|
||||
if set[i].Place != v.Place {
|
||||
continue
|
||||
}
|
||||
|
||||
// Update existing entry
|
||||
set[i] = v
|
||||
return set
|
||||
}
|
||||
|
||||
// Append new value
|
||||
return append(set, v)
|
||||
}
|
||||
|
||||
// Get value set?
|
||||
func (set RecordValueSet) Get(name string, place uint) *RecordValue {
|
||||
for i := range set {
|
||||
if set[i].Name != name {
|
||||
continue
|
||||
}
|
||||
if set[i].Place != place {
|
||||
continue
|
||||
}
|
||||
|
||||
return set[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (set RecordValueSet) GetAll(name string) (out []*RecordValue) {
|
||||
for i := range set {
|
||||
if set[i].Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, set[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Has value set?
|
||||
func (set RecordValueSet) Has(name string, place uint) bool {
|
||||
for i := range set {
|
||||
if set[i].Name != name {
|
||||
continue
|
||||
}
|
||||
if set[i].Place != place {
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (set RecordValueSet) SetRecordID(recordID uint64) {
|
||||
for i := range set {
|
||||
set[i].RecordID = recordID
|
||||
}
|
||||
}
|
||||
|
||||
func (set RecordValueSet) SetUpdatedFlag(updated bool) {
|
||||
for i := range set {
|
||||
set[i].Updated = updated
|
||||
}
|
||||
}
|
||||
|
||||
func (set RecordValueSet) GetUpdated() (out RecordValueSet) {
|
||||
out = make([]*RecordValue, 0, len(set))
|
||||
for i := range set {
|
||||
if !set[i].Updated {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, set[i])
|
||||
}
|
||||
|
||||
// Append new value
|
||||
return out
|
||||
}
|
||||
|
||||
func (set RecordValueSet) GetClean() (out RecordValueSet) {
|
||||
out = make([]*RecordValue, 0, len(set))
|
||||
for s := range set {
|
||||
if set[s].DeletedAt != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, set[s].Clone())
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Merge merges old value set with new one and expects unchanged values to be in the new set
|
||||
func (set RecordValueSet) Merge(mfs ModuleFieldSet, new RecordValueSet, canAccessField func(f *ModuleField) bool) (out RecordValueSet) {
|
||||
new, _ = new.Filter(func(v *RecordValue) (bool, error) {
|
||||
return mfs.HasName(v.Name), nil
|
||||
})
|
||||
|
||||
// Value merge process does not know anything about permissions so
|
||||
// in case when new values are missing but do exist in the old set and their update/read is denied
|
||||
// we need to copy them to ensure value merge process them correctly
|
||||
for _, f := range mfs {
|
||||
if len(new.FilterByName(f.Name)) == 0 && !canAccessField(f) {
|
||||
// copy all fields from old to new
|
||||
new = append(new, set.FilterByName(f.Name).GetClean()...)
|
||||
}
|
||||
}
|
||||
|
||||
return set.merge(new)
|
||||
}
|
||||
|
||||
// Raw merge of old and one and new values, skipping unchanged
|
||||
//
|
||||
// This satisfies current requirements where record values are always
|
||||
// manipulated as a whole (not partial)
|
||||
func (set RecordValueSet) merge(new RecordValueSet) (out RecordValueSet) {
|
||||
if len(set) == 0 {
|
||||
// Empty set, copy all new values and return them
|
||||
for i := range new {
|
||||
new[i].Updated = true
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
||||
|
||||
out = make([]*RecordValue, 0)
|
||||
for s := range set {
|
||||
// Mark all old as updated
|
||||
out = append(out, &RecordValue{
|
||||
Name: set[s].Name,
|
||||
Value: set[s].Value,
|
||||
Ref: set[s].Ref,
|
||||
Place: set[s].Place,
|
||||
DeletedAt: &time.Time{},
|
||||
Updated: true,
|
||||
OldValue: set[s].Value,
|
||||
})
|
||||
}
|
||||
|
||||
for n := range new {
|
||||
if ex := out.Get(new[n].Name, new[n].Place); ex != nil {
|
||||
// Reset deleted flag
|
||||
ex.DeletedAt = new[n].DeletedAt
|
||||
|
||||
if ex.OldValue == new[n].Value {
|
||||
// Value is the same
|
||||
ex.Updated = false
|
||||
} else if !ex.Updated {
|
||||
// Value changed and old one was not marked as updated before
|
||||
// See if values really changed and update old value on existing value
|
||||
ex.Updated = ex.Value != new[n].Value
|
||||
ex.OldValue = ex.Value
|
||||
}
|
||||
|
||||
ex.Value = new[n].Value
|
||||
ex.Ref = new[n].Ref
|
||||
} else {
|
||||
// Value not previously set, make new
|
||||
out = append(out, &RecordValue{
|
||||
Name: new[n].Name,
|
||||
Value: new[n].Value,
|
||||
Ref: new[n].Ref,
|
||||
Place: new[n].Place,
|
||||
Updated: true,
|
||||
|
||||
// verbose & explicit for clarity
|
||||
OldValue: "",
|
||||
DeletedAt: nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (set *RecordValueSet) Scan(src any) error { return sql.ParseJSON(src, set) }
|
||||
func (set RecordValueSet) Value() (driver.Value, error) { return json.Marshal(set) }
|
||||
|
||||
// Simple RVS as string output utility fn that
|
||||
// can help with debugging
|
||||
func (set RecordValueSet) String() (o string) {
|
||||
if set == nil {
|
||||
return "<RecordValueSet = nil>"
|
||||
}
|
||||
|
||||
is := func(in interface{}) string {
|
||||
switch in := in.(type) {
|
||||
case bool:
|
||||
if in {
|
||||
return "✔"
|
||||
}
|
||||
case *time.Time:
|
||||
if in != nil {
|
||||
return "✔"
|
||||
}
|
||||
}
|
||||
|
||||
return "x"
|
||||
}
|
||||
|
||||
o += "━━━━━━━━━━━┳━━━━┳━━━┳━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
o += "name ┃ ## ┃ u ┃ d ┃ value ┃ ref ┃ old value \n"
|
||||
o += "━━━━━━━━━━━╋━━━━╋━━━╋━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
const tpl = "%-10s ┃ %2d ┃ %s ┃ %s ┃ %-25s ┃ %-20d ┃ %-25s\n"
|
||||
for _, v := range set {
|
||||
if v == nil {
|
||||
o += "<--------> ┃ -- ┃ - ┃ - ┃ <------------------------> ┃ <------------------> ┃ <------------------> \n"
|
||||
continue
|
||||
}
|
||||
|
||||
o += fmt.Sprintf(
|
||||
tpl,
|
||||
v.Name,
|
||||
v.Place,
|
||||
is(v.Updated),
|
||||
is(v.DeletedAt),
|
||||
v.Value,
|
||||
v.Ref,
|
||||
v.OldValue,
|
||||
)
|
||||
}
|
||||
o += "━━━━━━━━━━━┻━━━━┻━━━┻━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Returns structured representation of values casted to the appropriate types
|
||||
func (set RecordValueSet) Dict(fields ModuleFieldSet) map[string]interface{} {
|
||||
var (
|
||||
rval = make(map[string]interface{})
|
||||
)
|
||||
|
||||
if len(fields) == 0 {
|
||||
return rval
|
||||
}
|
||||
|
||||
_ = fields.Walk(func(f *ModuleField) error {
|
||||
// make sure all fields are set at least to nil
|
||||
rval[f.Name] = nil
|
||||
|
||||
if f.Multi {
|
||||
var (
|
||||
rv = set.FilterByName(f.Name)
|
||||
vv = make([]interface{}, len(rv))
|
||||
)
|
||||
for i, val := range rv {
|
||||
vv[i], _ = val.Cast(f)
|
||||
}
|
||||
rval[f.Name] = vv
|
||||
} else if v := set.Get(f.Name, 0); v != nil {
|
||||
rval[f.Name], _ = v.Cast(f)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return rval
|
||||
}
|
||||
|
||||
func (set RecordValueSet) Len() int { return len(set) }
|
||||
func (set RecordValueSet) Swap(i, j int) { set[i], set[j] = set[j], set[i] }
|
||||
func (set RecordValueSet) Less(i, j int) bool {
|
||||
n := strings.Compare(set[i].Name, set[j].Name)
|
||||
if n != 0 {
|
||||
return n < 0
|
||||
}
|
||||
|
||||
return set[i].Place < set[j].Place
|
||||
|
||||
}
|
||||
20
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/resources.gen.go
generated
vendored
Normal file
20
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/resources.gen.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
const (
|
||||
AttachmentResourceType = "corteza::compose:attachment"
|
||||
ChartResourceType = "corteza::compose:chart"
|
||||
ModuleResourceType = "corteza::compose:module"
|
||||
ModuleFieldResourceType = "corteza::compose:module-field"
|
||||
NamespaceResourceType = "corteza::compose:namespace"
|
||||
PageResourceType = "corteza::compose:page"
|
||||
PageLayoutResourceType = "corteza::compose:page-layout"
|
||||
RecordResourceType = "corteza::compose:record"
|
||||
RecordRevisionResourceType = "corteza::compose:record-revision"
|
||||
ComponentResourceType = "corteza::compose"
|
||||
)
|
||||
153
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/type_labels.gen.go
generated
vendored
Normal file
153
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/type_labels.gen.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package types
|
||||
|
||||
// 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:
|
||||
// compose/types/types.yaml
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (m *Chart) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Chart) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (Chart) LabelResourceKind() string {
|
||||
return "compose:chart"
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Chart) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (m *Module) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Module) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (Module) LabelResourceKind() string {
|
||||
return "compose:module"
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Module) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (m *ModuleField) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m ModuleField) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (ModuleField) LabelResourceKind() string {
|
||||
return "compose:module:field"
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m ModuleField) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (m *Namespace) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Namespace) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (Namespace) LabelResourceKind() string {
|
||||
return "compose:namespace"
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Namespace) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (m *Page) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Page) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (Page) LabelResourceKind() string {
|
||||
return "compose:page"
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m Page) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (m *PageLayout) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m PageLayout) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (PageLayout) LabelResourceKind() string {
|
||||
return "compose:page-layout"
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m PageLayout) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
640
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/type_set.gen.go
generated
vendored
Normal file
640
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/type_set.gen.go
generated
vendored
Normal file
@@ -0,0 +1,640 @@
|
||||
package types
|
||||
|
||||
// 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:
|
||||
// compose/types/types.yaml
|
||||
|
||||
type (
|
||||
|
||||
// AttachmentSet slice of Attachment
|
||||
//
|
||||
// This type is auto-generated.
|
||||
AttachmentSet []*Attachment
|
||||
|
||||
// ChartSet slice of Chart
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ChartSet []*Chart
|
||||
|
||||
// DeDupRuleSet slice of DeDupRule
|
||||
//
|
||||
// This type is auto-generated.
|
||||
DeDupRuleSet []*DeDupRule
|
||||
|
||||
// IconSet slice of Icon
|
||||
//
|
||||
// This type is auto-generated.
|
||||
IconSet []*Icon
|
||||
|
||||
// ModuleSet slice of Module
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ModuleSet []*Module
|
||||
|
||||
// ModuleFieldSet slice of ModuleField
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ModuleFieldSet []*ModuleField
|
||||
|
||||
// NamespaceSet slice of Namespace
|
||||
//
|
||||
// This type is auto-generated.
|
||||
NamespaceSet []*Namespace
|
||||
|
||||
// PageSet slice of Page
|
||||
//
|
||||
// This type is auto-generated.
|
||||
PageSet []*Page
|
||||
|
||||
// PageLayoutSet slice of PageLayout
|
||||
//
|
||||
// This type is auto-generated.
|
||||
PageLayoutSet []*PageLayout
|
||||
|
||||
// PrivacyModuleSet slice of PrivacyModule
|
||||
//
|
||||
// This type is auto-generated.
|
||||
PrivacyModuleSet []*PrivacyModule
|
||||
|
||||
// RecordSet slice of Record
|
||||
//
|
||||
// This type is auto-generated.
|
||||
RecordSet []*Record
|
||||
|
||||
// RecordValueSet slice of RecordValue
|
||||
//
|
||||
// This type is auto-generated.
|
||||
RecordValueSet []*RecordValue
|
||||
)
|
||||
|
||||
// Walk iterates through every slice item and calls w(Attachment) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set AttachmentSet) Walk(w func(*Attachment) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Attachment) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set AttachmentSet) Filter(f func(*Attachment) (bool, error)) (out AttachmentSet, err error) {
|
||||
var ok bool
|
||||
out = AttachmentSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set AttachmentSet) FindByID(ID uint64) *Attachment {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set AttachmentSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Chart) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ChartSet) Walk(w func(*Chart) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Chart) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ChartSet) Filter(f func(*Chart) (bool, error)) (out ChartSet, err error) {
|
||||
var ok bool
|
||||
out = ChartSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ChartSet) FindByID(ID uint64) *Chart {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ChartSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(DeDupRule) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set DeDupRuleSet) Walk(w func(*DeDupRule) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(DeDupRule) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set DeDupRuleSet) Filter(f func(*DeDupRule) (bool, error)) (out DeDupRuleSet, err error) {
|
||||
var ok bool
|
||||
out = DeDupRuleSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Icon) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set IconSet) Walk(w func(*Icon) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Icon) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set IconSet) Filter(f func(*Icon) (bool, error)) (out IconSet, err error) {
|
||||
var ok bool
|
||||
out = IconSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Module) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleSet) Walk(w func(*Module) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Module) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleSet) Filter(f func(*Module) (bool, error)) (out ModuleSet, err error) {
|
||||
var ok bool
|
||||
out = ModuleSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleSet) FindByID(ID uint64) *Module {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(ModuleField) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleFieldSet) Walk(w func(*ModuleField) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(ModuleField) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleFieldSet) Filter(f func(*ModuleField) (bool, error)) (out ModuleFieldSet, err error) {
|
||||
var ok bool
|
||||
out = ModuleFieldSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleFieldSet) FindByID(ID uint64) *ModuleField {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleFieldSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Namespace) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NamespaceSet) Walk(w func(*Namespace) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Namespace) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NamespaceSet) Filter(f func(*Namespace) (bool, error)) (out NamespaceSet, err error) {
|
||||
var ok bool
|
||||
out = NamespaceSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NamespaceSet) FindByID(ID uint64) *Namespace {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NamespaceSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Page) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageSet) Walk(w func(*Page) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Page) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageSet) Filter(f func(*Page) (bool, error)) (out PageSet, err error) {
|
||||
var ok bool
|
||||
out = PageSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageSet) FindByID(ID uint64) *Page {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(PageLayout) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageLayoutSet) Walk(w func(*PageLayout) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(PageLayout) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageLayoutSet) Filter(f func(*PageLayout) (bool, error)) (out PageLayoutSet, err error) {
|
||||
var ok bool
|
||||
out = PageLayoutSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageLayoutSet) FindByID(ID uint64) *PageLayout {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PageLayoutSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(PrivacyModule) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PrivacyModuleSet) Walk(w func(*PrivacyModule) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(PrivacyModule) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set PrivacyModuleSet) Filter(f func(*PrivacyModule) (bool, error)) (out PrivacyModuleSet, err error) {
|
||||
var ok bool
|
||||
out = PrivacyModuleSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Record) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set RecordSet) Walk(w func(*Record) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Record) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set RecordSet) Filter(f func(*Record) (bool, error)) (out RecordSet, err error) {
|
||||
var ok bool
|
||||
out = RecordSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set RecordSet) FindByID(ID uint64) *Record {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set RecordSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(RecordValue) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set RecordValueSet) Walk(w func(*RecordValue) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(RecordValue) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set RecordValueSet) Filter(f func(*RecordValue) (bool, error)) (out RecordValueSet, err error) {
|
||||
var ok bool
|
||||
out = RecordValueSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
23
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/types.yaml
generated
vendored
Normal file
23
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/types.yaml
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
types:
|
||||
Namespace:
|
||||
labelResourceType: compose:namespace
|
||||
Attachment: {}
|
||||
Module:
|
||||
labelResourceType: compose:module
|
||||
ModuleField:
|
||||
labelResourceType: compose:module:field
|
||||
Page:
|
||||
labelResourceType: compose:page
|
||||
Icon:
|
||||
noIdField: true
|
||||
PageLayout:
|
||||
labelResourceType: compose:page-layout
|
||||
Chart:
|
||||
labelResourceType: compose:chart
|
||||
Record: {}
|
||||
RecordValue:
|
||||
noIdField: true
|
||||
PrivacyModule:
|
||||
noIdField: true
|
||||
DeDupRule:
|
||||
noIdField: true
|
||||
104
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/validated.go
generated
vendored
Normal file
104
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/validated.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
RecordValueError struct {
|
||||
Kind string `json:"kind"`
|
||||
Message string `json:"message"`
|
||||
Meta map[string]interface{} `json:"meta"`
|
||||
}
|
||||
|
||||
RecordValueErrorSet struct {
|
||||
Set []RecordValueError `json:"set"`
|
||||
}
|
||||
)
|
||||
|
||||
// safe to show details of this error
|
||||
func (RecordValueErrorSet) Safe() bool { return true }
|
||||
|
||||
func (v *RecordValueErrorSet) Push(err ...RecordValueError) {
|
||||
v.Set = append(v.Set, err...)
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) IsValid() bool {
|
||||
return v == nil || len(v.Set) == 0
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) Len() int {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return len(v.Set)
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) Error() string {
|
||||
var no = 0
|
||||
if v != nil {
|
||||
no = len(v.Set)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d issue(s) found", no)
|
||||
}
|
||||
|
||||
func (v RecordValueErrorSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Message string `json:"message"`
|
||||
Set []RecordValueError `json:"set,omitempty"`
|
||||
}{
|
||||
Message: v.Error(),
|
||||
Set: v.Set,
|
||||
})
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) HasKind(kind string) bool {
|
||||
if v == nil || v.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, e := range v.Set {
|
||||
if e.Kind == kind {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *RecordValueErrorSet) Merge(errs ...*RecordValueErrorSet) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, e := range errs {
|
||||
if e == nil || e.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
v.Push(e.Set...)
|
||||
}
|
||||
}
|
||||
|
||||
// IsRecordValueErrorSet tests if given error is RecordValueErrorSet (or it wraps it) and it has errors
|
||||
// If not is not (or !IsValid), it return nil!
|
||||
func IsRecordValueErrorSet(err error) *RecordValueErrorSet {
|
||||
for {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rve, ok := err.(*RecordValueErrorSet); ok {
|
||||
if rve.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return rve
|
||||
}
|
||||
|
||||
err = errors.Unwrap(err)
|
||||
}
|
||||
}
|
||||
24
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/api/debug.go
generated
vendored
Normal file
24
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/api/debug.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Debug context
|
||||
type ctxKeyDebug struct{}
|
||||
|
||||
// Packs remote address to context
|
||||
func DebugToContext(production bool) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
next.ServeHTTP(w, req.WithContext(context.WithValue(req.Context(), ctxKeyDebug{}, !production)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DebugFromContext returns remote IP address from context
|
||||
func DebugFromContext(ctx context.Context) bool {
|
||||
debug, ok := ctx.Value(ctxKeyDebug{}).(bool)
|
||||
return ok && debug
|
||||
}
|
||||
29
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/api/ipaddr.go
generated
vendored
Normal file
29
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/api/ipaddr.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Key to use when setting the request ID.
|
||||
type ctxKeyRemoteAddr int
|
||||
|
||||
// RemoteAddrKey is the key that holds th unique request ID in a request context.
|
||||
const remoteAddrKey ctxKeyRemoteAddr = 0
|
||||
|
||||
// Packs remote address to context
|
||||
func RemoteAddrToContext(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
next.ServeHTTP(w, req.WithContext(context.WithValue(req.Context(), remoteAddrKey, req.RemoteAddr)))
|
||||
})
|
||||
}
|
||||
|
||||
// RemoteAddrFromContext returns remote IP address from context
|
||||
func RemoteAddrFromContext(ctx context.Context) string {
|
||||
v := ctx.Value(remoteAddrKey)
|
||||
if str, ok := v.(string); ok {
|
||||
return str
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
170
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/api/response.go
generated
vendored
Normal file
170
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/api/response.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
package api
|
||||
|
||||
// This code is modified version from
|
||||
// https://github.com/titpetric/factory/tree/master/resputil
|
||||
//
|
||||
// Parts of the code are rewritten to allow greater flexibility
|
||||
// but the general logic stays the same for now
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
successWrap struct {
|
||||
Success struct {
|
||||
Message string `json:"message"`
|
||||
} `json:"success"`
|
||||
}
|
||||
|
||||
CallFrame struct {
|
||||
Function string `json:"function"`
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
}
|
||||
|
||||
ErrorPayload struct {
|
||||
Message string `json:"message"`
|
||||
Context map[string]interface{} `json:"context,omitempty"`
|
||||
Callstack []*CallFrame `json:"callstack,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
// Success returns a structured success message for API responses
|
||||
func Success(success ...string) *successWrap {
|
||||
response := &successWrap{}
|
||||
response.Success.Message = "OK"
|
||||
if len(success) > 0 {
|
||||
response.Success.Message = success[0]
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// OK returns the default Success message
|
||||
func OK() *successWrap {
|
||||
return Success()
|
||||
}
|
||||
|
||||
// Writes response, according to type
|
||||
//
|
||||
// Primarily this function encodes given payload (directly or indirectly) as compact JSON
|
||||
//
|
||||
// In some specific scenarios, when:
|
||||
// - debug mode is enabled,
|
||||
// - no explicit accept header with /json mime-type is sent
|
||||
// and,
|
||||
//
|
||||
// if payload is an error:
|
||||
// it outputs formatted error with extended info
|
||||
//
|
||||
// if payload is non-error:
|
||||
// it outputs formatted and indented JSON
|
||||
//
|
||||
func encode(w http.ResponseWriter, r *http.Request, payload interface{}) {
|
||||
var (
|
||||
err error
|
||||
enc = json.NewEncoder(w)
|
||||
)
|
||||
|
||||
switch c := payload.(type) {
|
||||
|
||||
case error:
|
||||
err = c
|
||||
|
||||
case *successWrap:
|
||||
// main key is "success"
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if err = enc.Encode(c); err != nil {
|
||||
err = fmt.Errorf("failed to encode response: %w", err)
|
||||
}
|
||||
|
||||
default:
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
// main key is "response"
|
||||
aux := struct {
|
||||
Response interface{} `json:"response"`
|
||||
}{c}
|
||||
if err = enc.Encode(aux); err != nil {
|
||||
err = fmt.Errorf("failed to encode response: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err, is := err.(*errors.Error); is {
|
||||
// trim out the base stack we don't care about...
|
||||
_ = err.Apply(errors.StackTrimAtFn("http.HandlerFunc.ServeHTTP"))
|
||||
}
|
||||
|
||||
errors.ServeHTTP(w, r, err, !DebugFromContext(r.Context()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Send handles first non-nil (and non-empty) payload and encodes it or it's results (when fn)
|
||||
//
|
||||
// See encode() for details
|
||||
func Send(w http.ResponseWriter, r *http.Request, rr ...interface{}) {
|
||||
for _, rsp := range rr {
|
||||
switch c := rsp.(type) {
|
||||
case nil:
|
||||
// this will match a nil error
|
||||
continue
|
||||
|
||||
case *successWrap:
|
||||
encode(w, r, c)
|
||||
|
||||
case func(http.ResponseWriter, *http.Request):
|
||||
c(w, r)
|
||||
|
||||
case func() ([]byte, error):
|
||||
result, err := c()
|
||||
Send(w, r, err, result)
|
||||
|
||||
case func() (interface{}, error):
|
||||
result, err := c()
|
||||
Send(w, r, err, result)
|
||||
|
||||
case func() error:
|
||||
err := c()
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
encode(w, r, err)
|
||||
|
||||
case error:
|
||||
encode(w, r, c)
|
||||
|
||||
case []byte:
|
||||
if len(c) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := w.Write(c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
|
||||
case string:
|
||||
if c == "" {
|
||||
continue
|
||||
}
|
||||
encode(w, r, c)
|
||||
|
||||
case bool:
|
||||
if !c {
|
||||
continue
|
||||
}
|
||||
encode(w, r, c)
|
||||
|
||||
default:
|
||||
encode(w, r, c)
|
||||
}
|
||||
|
||||
// Exit on the first output...
|
||||
return
|
||||
}
|
||||
|
||||
encode(w, r, false)
|
||||
}
|
||||
13
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/boolean.go
generated
vendored
Normal file
13
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/boolean.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package cast2
|
||||
|
||||
import "github.com/spf13/cast"
|
||||
|
||||
func Bool(in any, out *bool) error {
|
||||
aux, err := cast.ToBoolE(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = aux
|
||||
return nil
|
||||
}
|
||||
33
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/integer.go
generated
vendored
Normal file
33
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/integer.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package cast2
|
||||
|
||||
import "github.com/spf13/cast"
|
||||
|
||||
func Uint64(in any, out *uint64) error {
|
||||
aux, err := cast.ToUint64E(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
func Uint(in any, out *uint) error {
|
||||
aux, err := cast.ToUintE(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
func Int(in any, out *int) error {
|
||||
aux, err := cast.ToIntE(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = aux
|
||||
return nil
|
||||
}
|
||||
32
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/map.go
generated
vendored
Normal file
32
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/map.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package cast2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/modern-go/reflect2"
|
||||
)
|
||||
|
||||
// Meta casts a value to a map[string]any
|
||||
func Meta(in any, out *map[string]any) (err error) {
|
||||
if reflect2.IsNil(in) {
|
||||
*out = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
switch aux := in.(type) {
|
||||
case []byte:
|
||||
err = json.Unmarshal(aux, out)
|
||||
case string:
|
||||
err = json.Unmarshal([]byte(aux), out)
|
||||
case map[string]any:
|
||||
*out = aux
|
||||
default:
|
||||
err = fmt.Errorf("unsupported type: %T", in)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
return fmt.Errorf("can not cast to Meta: %w", err)
|
||||
}
|
||||
12
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/slice.go
generated
vendored
Normal file
12
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/slice.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package cast2
|
||||
|
||||
// Anys converts any kinds of values to a []any slice
|
||||
func Anys[C any](in ...C) (out []any) {
|
||||
out = make([]any, len(in))
|
||||
|
||||
for i := range in {
|
||||
out[i] = in[i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
13
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/string.go
generated
vendored
Normal file
13
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/string.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package cast2
|
||||
|
||||
import "github.com/spf13/cast"
|
||||
|
||||
func String(in any, out *string) error {
|
||||
aux, err := cast.ToStringE(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = aux
|
||||
return nil
|
||||
}
|
||||
40
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/time.go
generated
vendored
Normal file
40
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cast2/time.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package cast2
|
||||
|
||||
import (
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/spf13/cast"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Time(in any, out *time.Time) error {
|
||||
if reflect2.IsNil(in) {
|
||||
if out != nil {
|
||||
*out = time.Time{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
aux, err := cast.ToTimeE(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
func TimePtr(in any, out **time.Time) error {
|
||||
if reflect2.IsNil(in) {
|
||||
*out = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
aux, err := cast.ToTimeE(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = &aux
|
||||
return nil
|
||||
}
|
||||
95
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/commands.go
generated
vendored
Normal file
95
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/commands.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCommand = &cobra.Command{
|
||||
Use: "corteza-server",
|
||||
Aliases: []string{"corteza", "server"},
|
||||
TraverseChildren: true,
|
||||
}
|
||||
|
||||
serveApiCommand = &cobra.Command{
|
||||
Use: "serve-api",
|
||||
Aliases: []string{"serve"},
|
||||
|
||||
Short: "Start HTTP server with REST API",
|
||||
}
|
||||
|
||||
upgradeCommand = &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: "Upgrade tasks",
|
||||
}
|
||||
|
||||
provisionCommand = &cobra.Command{
|
||||
Use: "provision",
|
||||
Short: "Provision tasks",
|
||||
}
|
||||
|
||||
versionCommand = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Version",
|
||||
}
|
||||
|
||||
// List of commands that do not
|
||||
// run initialization (db connection, etc...)
|
||||
light = map[string]bool{
|
||||
"help": true,
|
||||
versionCommand.Use: true,
|
||||
}
|
||||
)
|
||||
|
||||
// RootCommand creates root command with a simple persistent-pre-run callback
|
||||
//
|
||||
// Callback is called when not executed with help subcommand
|
||||
func RootCommand(ppRunEfn func() error) *cobra.Command {
|
||||
rootCommand.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
if light[cmd.Name()] {
|
||||
// Do not run hooks on parts on help command
|
||||
return nil
|
||||
}
|
||||
|
||||
if ppRunEfn != nil {
|
||||
return ppRunEfn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return rootCommand
|
||||
}
|
||||
|
||||
func ServeCommand(runEfn func() error) *cobra.Command {
|
||||
serveApiCommand.RunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
return runEfn()
|
||||
}
|
||||
|
||||
return serveApiCommand
|
||||
}
|
||||
|
||||
func UpgradeCommand(dbfn func() error) *cobra.Command {
|
||||
upgradeCommand.RunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
return dbfn()
|
||||
}
|
||||
|
||||
return upgradeCommand
|
||||
}
|
||||
|
||||
func ProvisionCommand(cffn func() error) *cobra.Command {
|
||||
provisionCommand.RunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
return cffn()
|
||||
}
|
||||
|
||||
return provisionCommand
|
||||
}
|
||||
|
||||
func VersionCommand() *cobra.Command {
|
||||
versionCommand.Run = func(cmd *cobra.Command, args []string) {
|
||||
cmd.Println(version.Version)
|
||||
}
|
||||
|
||||
return versionCommand
|
||||
}
|
||||
16
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/context.go
generated
vendored
Normal file
16
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/context.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ctxwrap "github.com/SentimensRG/ctx"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
)
|
||||
|
||||
// Context is small wrapper that returns sig-term bound context
|
||||
//
|
||||
// This can be used as (proper) background context that properly terminates
|
||||
// all subroutines.
|
||||
func Context() context.Context {
|
||||
return ctxwrap.AsContext(sigctx.New())
|
||||
}
|
||||
57
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/env.go
generated
vendored
Normal file
57
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/env.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// LoadEnv loads env-variables (KEY=VAL) from provided files and paths (by searching for .env file in the given path)
|
||||
//
|
||||
// Please note that loaded values
|
||||
// DO NOT OVERRIDE the existing
|
||||
// environmental variables
|
||||
func LoadEnv(pp ...string) error {
|
||||
// preparse the input and try to figure out if .env should be appended
|
||||
var checked = make([]string, 0, len(pp))
|
||||
for _, p := range pp {
|
||||
if s, err := os.Stat(p); err != nil {
|
||||
return err
|
||||
} else if s.IsDir() {
|
||||
chk := path.Join(p, ".env")
|
||||
if _, err = os.Stat(chk); err == nil {
|
||||
// make sure only .env files
|
||||
checked = append(checked, chk)
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
checked = append(checked, p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(checked) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return godotenv.Load(checked...)
|
||||
}
|
||||
|
||||
// EnvCommand outputs all loaded env variables
|
||||
func EnvCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "env",
|
||||
Long: "Outputs list (sorted by key) of of all environmental variables. Can be used for diagnosis and debugging",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
kv := os.Environ()
|
||||
sort.Strings(kv)
|
||||
for _, l := range kv {
|
||||
cmd.Println(l)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
28
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/helpers.go
generated
vendored
Normal file
28
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/cli/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func HandleError(err error) {
|
||||
if err = unwrapGeneric(err); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func unwrapGeneric(err error) error {
|
||||
for {
|
||||
g, ok := err.(interface{ IsGeneric() bool })
|
||||
if ok && g != nil && g.IsGeneric() {
|
||||
err = errors.Unwrap(err)
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
359
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/aggregator.go
generated
vendored
Normal file
359
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/aggregator.go
generated
vendored
Normal file
@@ -0,0 +1,359 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/ql"
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
// aggregator performs value aggregation primarily used for the aggregate step
|
||||
// @todo consider moving into a separate package
|
||||
//
|
||||
// The aggregator performs the requested value expressions as well as
|
||||
// aggregation operations over the evaluated values.
|
||||
//
|
||||
// The aggregator computes the values on the fly (for the ones it can).
|
||||
aggregator struct {
|
||||
// aggregates holds output aggregation values
|
||||
// @note we'll use float64 for all values, but it might make more sense to split it up
|
||||
// in the future. For now, it'll be ok.
|
||||
aggregates []float64
|
||||
|
||||
// counts holds the number of values for each aggregate including multi value fields.
|
||||
// Counts are currently only used for average.
|
||||
counts []int
|
||||
|
||||
// def provides a list of aggregates.
|
||||
//
|
||||
// Each index corresponds to the aggregates and counts slices
|
||||
def []aggregateDef
|
||||
// scanned indicates whether the aggregator has been scanned since
|
||||
// we need to block writes after the first scan.
|
||||
scanned bool
|
||||
}
|
||||
|
||||
// aggregateDef is a wrapper for the provided value aggregation
|
||||
aggregateDef struct {
|
||||
outIdent string
|
||||
|
||||
aggOp string
|
||||
|
||||
inIdent string
|
||||
eval evaluator
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// aggregateFunctionIndex specifies all of the registered aggregate functions
|
||||
//
|
||||
// @todo consider making this expandable via some registry/plugin/...
|
||||
aggregateFunctionIndex = map[string]bool{
|
||||
"count": true,
|
||||
"sum": true,
|
||||
"min": true,
|
||||
"max": true,
|
||||
"avg": true,
|
||||
}
|
||||
)
|
||||
|
||||
// Aggregator initializes a new aggregator for the given set of mappings
|
||||
//
|
||||
// The aggregator is not routine safe; consider defining multiple aggregators
|
||||
// and then combining them together.
|
||||
func Aggregator() *aggregator {
|
||||
return &aggregator{
|
||||
aggregates: make([]float64, 0, 16),
|
||||
counts: make([]int, 0, 16),
|
||||
}
|
||||
}
|
||||
|
||||
// AddAggregateE adds a new aggregate from a raw expression
|
||||
func (a *aggregator) AddAggregateE(ident, expr string) (err error) {
|
||||
// @note the converter is reused so we can safely do this
|
||||
n, err := newConverterGval().Parse(expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return a.AddAggregate(ident, n)
|
||||
}
|
||||
|
||||
// AddAggregate adds a new aggregate from an already parsed expression
|
||||
func (a *aggregator) AddAggregate(ident string, expr *ql.ASTNode) (err error) {
|
||||
def := aggregateDef{
|
||||
outIdent: ident,
|
||||
}
|
||||
|
||||
inIdent, expr, err := unpackMappingSource(expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Take it from the source
|
||||
if inIdent != "" {
|
||||
def.inIdent = inIdent
|
||||
}
|
||||
|
||||
// Take it from the expression
|
||||
// - agg. op.
|
||||
def.aggOp, expr, err = unpackExpressionNode(expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Prepare a runner in case we're not simply copying values
|
||||
if inIdent == "" {
|
||||
// - make evaluator
|
||||
if expr != nil {
|
||||
def.eval, err = newRunnerGvalParsed(expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.aggregates = append(a.aggregates, 0)
|
||||
a.counts = append(a.counts, 0)
|
||||
a.def = append(a.def, def)
|
||||
return
|
||||
}
|
||||
|
||||
// Aggregate aggregates the given value
|
||||
func (a *aggregator) Aggregate(ctx context.Context, v ValueGetter) (err error) {
|
||||
if a.scanned {
|
||||
// If we attempt to add data to the aggregator, the previous data might
|
||||
// no longer be valid.
|
||||
return fmt.Errorf("cannot call Aggregate on an already scanned aggregator")
|
||||
}
|
||||
|
||||
// @todo consider throwing all of the aggregates into a routine.
|
||||
// I think (for now) performance gains will be negligible.
|
||||
for i, attr := range a.def {
|
||||
err = a.aggregate(ctx, attr, i, v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan scans the aggregated values into the setter
|
||||
func (a *aggregator) Scan(s ValueSetter) (err error) {
|
||||
// On first scan, complete partial aggregates
|
||||
if !a.scanned {
|
||||
a.completePartials()
|
||||
}
|
||||
|
||||
a.scanned = true
|
||||
|
||||
// Set the values
|
||||
for i, attr := range a.def {
|
||||
// @note each aggregated value can be at most one so no need for multi-value
|
||||
// suport here.
|
||||
err = s.SetValue(attr.outIdent, 0, a.aggregates[i])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// aggregate applies the provided value into the requested aggregate
|
||||
func (a *aggregator) aggregate(ctx context.Context, attr aggregateDef, i int, v ValueGetter) (err error) {
|
||||
switch attr.aggOp {
|
||||
case "count":
|
||||
return a.count(ctx, attr, i, v)
|
||||
|
||||
case "sum":
|
||||
return a.sum(ctx, attr, i, v)
|
||||
|
||||
case "min":
|
||||
return a.min(ctx, attr, i, v)
|
||||
|
||||
case "max":
|
||||
return a.max(ctx, attr, i, v)
|
||||
|
||||
case "avg":
|
||||
return a.avg(ctx, attr, i, v)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported aggregate function: %s", attr.aggOp)
|
||||
}
|
||||
|
||||
// walkValues traverses the available values for the specified attribute
|
||||
func (a *aggregator) walkValues(ctx context.Context, r ValueGetter, cc map[string]uint, attr aggregateDef, run func(v any, isNil bool)) (err error) {
|
||||
var out any
|
||||
if attr.inIdent == "" {
|
||||
if attr.eval != nil {
|
||||
out, err = attr.eval.Eval(ctx, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
out = r
|
||||
}
|
||||
|
||||
run(out, reflect2.IsNil(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := uint(0); i < cc[attr.inIdent]; i++ {
|
||||
v, err := r.GetValue(attr.inIdent, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
run(v, reflect2.IsNil(v))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Aggregate methods
|
||||
|
||||
func (a *aggregator) count(ctx context.Context, attr aggregateDef, i int, v ValueGetter) (err error) {
|
||||
err = a.walkValues(ctx, v, v.CountValues(), attr, func(v any, isNil bool) {
|
||||
if isNil {
|
||||
return
|
||||
}
|
||||
|
||||
a.aggregates[i]++
|
||||
a.counts[i]++
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aggregator) sum(ctx context.Context, attr aggregateDef, i int, v ValueGetter) (err error) {
|
||||
err = a.walkValues(ctx, v, v.CountValues(), attr, func(v any, isNil bool) {
|
||||
if isNil {
|
||||
return
|
||||
}
|
||||
a.aggregates[i] += cast.ToFloat64(v)
|
||||
a.counts[i]++
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aggregator) min(ctx context.Context, attr aggregateDef, i int, v ValueGetter) (err error) {
|
||||
err = a.walkValues(ctx, v, v.CountValues(), attr, func(v any, isNil bool) {
|
||||
if isNil {
|
||||
return
|
||||
}
|
||||
|
||||
if a.counts[i] == 0 {
|
||||
a.aggregates[i] = cast.ToFloat64(v)
|
||||
} else {
|
||||
a.aggregates[i] = math.Min(a.aggregates[i], cast.ToFloat64(v))
|
||||
}
|
||||
a.counts[i]++
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aggregator) max(ctx context.Context, attr aggregateDef, i int, v ValueGetter) (err error) {
|
||||
err = a.walkValues(ctx, v, v.CountValues(), attr, func(v any, isNil bool) {
|
||||
if isNil {
|
||||
return
|
||||
}
|
||||
|
||||
if a.counts[i] == 0 {
|
||||
a.aggregates[i] = cast.ToFloat64(v)
|
||||
} else {
|
||||
a.aggregates[i] = math.Max(a.aggregates[i], cast.ToFloat64(v))
|
||||
}
|
||||
a.counts[i]++
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aggregator) avg(ctx context.Context, attr aggregateDef, i int, v ValueGetter) (err error) {
|
||||
err = a.walkValues(ctx, v, v.CountValues(), attr, func(v any, isNil bool) {
|
||||
if isNil {
|
||||
return
|
||||
}
|
||||
|
||||
a.aggregates[i] += cast.ToFloat64(v)
|
||||
|
||||
a.counts[i]++
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aggregator) completePartials() {
|
||||
a.completeAverage()
|
||||
}
|
||||
|
||||
func (a *aggregator) completeAverage() {
|
||||
for i, attr := range a.def {
|
||||
if attr.aggOp == "avg" {
|
||||
if a.counts[i] == 0 {
|
||||
return
|
||||
}
|
||||
a.aggregates[i] = a.aggregates[i] / float64(a.counts[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
func unpackMappingSource(n *ql.ASTNode) (ident string, expr *ql.ASTNode, err error) {
|
||||
// Check if first arg of agg. fnc. is an attr.
|
||||
if len(n.Args) == 1 && n.Args[0].Symbol != "" {
|
||||
return n.Args[0].Symbol, n, nil
|
||||
}
|
||||
|
||||
expr = n
|
||||
return
|
||||
}
|
||||
|
||||
func unpackExpressionNode(n *ql.ASTNode) (aggOp string, expr *ql.ASTNode, err error) {
|
||||
if n.Ref != "" {
|
||||
aggOp = strings.ToLower(n.Ref)
|
||||
}
|
||||
if !aggregateFunctionIndex[aggOp] {
|
||||
err = fmt.Errorf("root expression must be an aggregate function")
|
||||
return
|
||||
}
|
||||
|
||||
if len(n.Args) > 0 {
|
||||
expr = n.Args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// reset is a benchmarking utility to reset the aggregator
|
||||
//
|
||||
// Don't use it in production code.
|
||||
func (a *aggregator) reset() {
|
||||
for i := 0; i < len(a.aggregates); i++ {
|
||||
a.aggregates[i] = 0
|
||||
a.counts[i] = 0
|
||||
}
|
||||
a.scanned = false
|
||||
}
|
||||
370
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/alteration.go
generated
vendored
Normal file
370
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/alteration.go
generated
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
Alteration struct {
|
||||
ID uint64
|
||||
BatchID uint64
|
||||
DependsOn uint64
|
||||
Resource string
|
||||
ResourceType string
|
||||
ConnectionID uint64
|
||||
|
||||
AttributeAdd *AttributeAdd
|
||||
AttributeDelete *AttributeDelete
|
||||
AttributeReType *AttributeReType
|
||||
AttributeReEncode *AttributeReEncode
|
||||
ModelAdd *ModelAdd
|
||||
ModelDelete *ModelDelete
|
||||
}
|
||||
|
||||
AlterationSet []*Alteration
|
||||
|
||||
AttributeAdd struct {
|
||||
Attr *Attribute `json:"attr"`
|
||||
}
|
||||
|
||||
AttributeDelete struct {
|
||||
Attr *Attribute `json:"attr"`
|
||||
}
|
||||
|
||||
AttributeReType struct {
|
||||
Attr *Attribute `json:"attr"`
|
||||
To Type `json:"to"`
|
||||
}
|
||||
|
||||
AttributeReEncode struct {
|
||||
Attr *Attribute `json:"attr"`
|
||||
To Codec `json:"to"`
|
||||
}
|
||||
|
||||
ModelAdd struct {
|
||||
Model *Model `json:"model"`
|
||||
}
|
||||
|
||||
ModelDelete struct {
|
||||
Model *Model `json:"model"`
|
||||
}
|
||||
|
||||
// auxAttributeType is a helper struct used for marshaling/unmarshaling
|
||||
//
|
||||
// This is required since the Type inside the Attribute is an interface and we
|
||||
// need to help the encoding/json a bit.
|
||||
auxAttributeReType struct {
|
||||
Attr *Attribute `json:"attr"`
|
||||
To *auxAttributeType `json:"to"`
|
||||
}
|
||||
|
||||
// auxAttributeReEncode is a helper struct used for marshaling/unmarshaling
|
||||
//
|
||||
// This is required since the Codec inside the Attribute is an interface and we
|
||||
// need to help the encoding/json a bit.
|
||||
auxAttributeReEncode struct {
|
||||
Attr *Attribute `json:"attr"`
|
||||
To *auxStoreCodec `json:"to"`
|
||||
}
|
||||
)
|
||||
|
||||
// Merge merges the two alteration slices
|
||||
func (aa AlterationSet) Merge(bb AlterationSet) (cc AlterationSet) {
|
||||
// @todo the Merge function currently just merges the two slices together
|
||||
// and removes any duplicates that would occur due to the merge.
|
||||
// We should also handle overlapping/transitive alterations to reduce
|
||||
// the amount of needles processing.
|
||||
//
|
||||
// A quick list of overlapping alterations:
|
||||
// * attribute A added and then renamed from A to A'
|
||||
// * attribute A renamed to A' and then renamed to A''
|
||||
// * attribute A deleted and then created
|
||||
|
||||
aux := append(aa, bb...)
|
||||
seen := make(map[int]bool, len(aux)/2)
|
||||
|
||||
for i, a := range aux {
|
||||
if seen[i] {
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for j := i + 1; j < len(aux); j++ {
|
||||
if seen[j] {
|
||||
continue
|
||||
}
|
||||
|
||||
if a.compare(*aux[j]) {
|
||||
if !found {
|
||||
cc = append(cc, aux[j])
|
||||
}
|
||||
|
||||
seen[j] = true
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
cc = append(cc, a)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a AttributeReType) MarshalJSON() ([]byte, error) {
|
||||
aux := auxAttributeReType{
|
||||
Attr: a.Attr,
|
||||
To: &auxAttributeType{},
|
||||
}
|
||||
|
||||
switch t := a.To.(type) {
|
||||
case *TypeID:
|
||||
aux.To.Type = "ID"
|
||||
aux.To.ID = t
|
||||
|
||||
case *TypeRef:
|
||||
aux.To.Type = "Ref"
|
||||
aux.To.Ref = t
|
||||
|
||||
case *TypeTimestamp:
|
||||
aux.To.Type = "Timestamp"
|
||||
aux.To.Timestamp = t
|
||||
|
||||
case *TypeTime:
|
||||
aux.To.Type = "Time"
|
||||
aux.To.Time = t
|
||||
|
||||
case *TypeDate:
|
||||
aux.To.Type = "Date"
|
||||
aux.To.Date = t
|
||||
|
||||
case *TypeNumber:
|
||||
aux.To.Type = "Number"
|
||||
aux.To.Number = t
|
||||
|
||||
case *TypeText:
|
||||
aux.To.Type = "Text"
|
||||
aux.To.Text = t
|
||||
|
||||
case *TypeBoolean:
|
||||
aux.To.Type = "Boolean"
|
||||
aux.To.Boolean = t
|
||||
|
||||
case *TypeEnum:
|
||||
aux.To.Type = "Enum"
|
||||
aux.To.Enum = t
|
||||
|
||||
case *TypeGeometry:
|
||||
aux.To.Type = "Geometry"
|
||||
aux.To.Geometry = t
|
||||
|
||||
case *TypeJSON:
|
||||
aux.To.Type = "JSON"
|
||||
aux.To.JSON = t
|
||||
|
||||
case *TypeBlob:
|
||||
aux.To.Type = "Blob"
|
||||
aux.To.Blob = t
|
||||
|
||||
case *TypeUUID:
|
||||
aux.To.Type = "UUID"
|
||||
aux.To.UUID = t
|
||||
}
|
||||
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
func (a *AttributeReType) UnmarshalJSON(data []byte) (err error) {
|
||||
aux := &auxAttributeReType{}
|
||||
err = json.Unmarshal(data, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
*a = AttributeReType{}
|
||||
}
|
||||
|
||||
a.Attr = aux.Attr
|
||||
|
||||
switch aux.To.Type {
|
||||
case "ID":
|
||||
a.To = aux.To.ID
|
||||
|
||||
case "Ref":
|
||||
a.To = aux.To.Ref
|
||||
|
||||
case "Timestamp":
|
||||
a.To = aux.To.Timestamp
|
||||
|
||||
case "Time":
|
||||
a.To = aux.To.Time
|
||||
|
||||
case "Date":
|
||||
a.To = aux.To.Date
|
||||
|
||||
case "Number":
|
||||
a.To = aux.To.Number
|
||||
|
||||
case "Text":
|
||||
a.To = aux.To.Text
|
||||
|
||||
case "Boolean":
|
||||
a.To = aux.To.Boolean
|
||||
|
||||
case "Enum":
|
||||
a.To = aux.To.Enum
|
||||
|
||||
case "Geometry":
|
||||
a.To = aux.To.Geometry
|
||||
|
||||
case "JSON":
|
||||
a.To = aux.To.JSON
|
||||
|
||||
case "Blob":
|
||||
a.To = aux.To.Blob
|
||||
|
||||
case "UUID":
|
||||
a.To = aux.To.UUID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a AttributeReEncode) MarshalJSON() ([]byte, error) {
|
||||
aux := auxAttributeReEncode{
|
||||
Attr: a.Attr,
|
||||
To: &auxStoreCodec{},
|
||||
}
|
||||
|
||||
switch t := a.To.(type) {
|
||||
case *CodecPlain:
|
||||
aux.To.Type = "CodecPlain"
|
||||
aux.To.CodecPlain = t
|
||||
case *CodecRecordValueSetJSON:
|
||||
aux.To.Type = "CodecRecordValueSetJSON"
|
||||
aux.To.CodecRecordValueSetJSON = t
|
||||
case *CodecAlias:
|
||||
aux.To.Type = "CodecAlias"
|
||||
aux.To.CodecAlias = t
|
||||
}
|
||||
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
func (a *AttributeReEncode) UnmarshalJSON(data []byte) (err error) {
|
||||
aux := &auxAttributeReEncode{}
|
||||
err = json.Unmarshal(data, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
*a = AttributeReEncode{}
|
||||
}
|
||||
|
||||
a.Attr = aux.Attr
|
||||
|
||||
switch aux.To.Type {
|
||||
case "CodecPlain":
|
||||
a.To = aux.To.CodecPlain
|
||||
|
||||
case "CodecRecordValueSetJSON":
|
||||
a.To = aux.To.CodecRecordValueSetJSON
|
||||
|
||||
case "CodecAlias":
|
||||
a.To = aux.To.CodecAlias
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a Alteration) compare(b Alteration) (cmp bool) {
|
||||
if a.AttributeAdd == nil && b.AttributeAdd != nil {
|
||||
return false
|
||||
}
|
||||
if a.AttributeDelete == nil && b.AttributeDelete != nil {
|
||||
return false
|
||||
}
|
||||
if a.AttributeReType == nil && b.AttributeReType != nil {
|
||||
return false
|
||||
}
|
||||
if a.AttributeReEncode == nil && b.AttributeReEncode != nil {
|
||||
return false
|
||||
}
|
||||
if a.ModelAdd == nil && b.ModelAdd != nil {
|
||||
return false
|
||||
}
|
||||
if a.ModelDelete == nil && b.ModelDelete != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch {
|
||||
case a.AttributeAdd != nil:
|
||||
return a.compareAttributeAdd(b)
|
||||
case a.AttributeDelete != nil:
|
||||
return a.compareAttributeDelete(b)
|
||||
case a.AttributeReType != nil:
|
||||
return a.compareAttributeReType(b)
|
||||
case a.AttributeReEncode != nil:
|
||||
return a.compareAttributeReEncode(b)
|
||||
case a.ModelAdd != nil:
|
||||
return a.compareModelAdd(b)
|
||||
case a.ModelDelete != nil:
|
||||
return a.compareModelDelete(b)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported alteration type %v", a))
|
||||
}
|
||||
|
||||
func (a Alteration) compareAttributeAdd(b Alteration) bool {
|
||||
if a.AttributeAdd == nil || b.AttributeAdd == nil {
|
||||
return a.AttributeAdd == b.AttributeAdd
|
||||
}
|
||||
return a.AttributeAdd.Attr.Compare(b.AttributeAdd.Attr)
|
||||
}
|
||||
|
||||
func (a Alteration) compareAttributeDelete(b Alteration) bool {
|
||||
if a.AttributeDelete == nil || b.AttributeDelete == nil {
|
||||
return a.AttributeDelete == b.AttributeDelete
|
||||
}
|
||||
return a.AttributeDelete.Attr.Compare(b.AttributeDelete.Attr)
|
||||
}
|
||||
|
||||
func (a Alteration) compareAttributeReType(b Alteration) bool {
|
||||
if !a.AttributeReType.Attr.Compare(b.AttributeReType.Attr) {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.AttributeReType == nil || b.AttributeReType == nil {
|
||||
return a.AttributeReType == b.AttributeReType
|
||||
}
|
||||
return a.AttributeReType.To.Type() == b.AttributeReType.To.Type()
|
||||
}
|
||||
|
||||
func (a Alteration) compareAttributeReEncode(b Alteration) bool {
|
||||
if !a.AttributeReEncode.Attr.Compare(b.AttributeReEncode.Attr) {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.AttributeReEncode == nil || b.AttributeReEncode == nil {
|
||||
return a.AttributeReEncode == b.AttributeReEncode
|
||||
}
|
||||
return a.AttributeReEncode.To.Type() == b.AttributeReEncode.To.Type()
|
||||
}
|
||||
|
||||
func (a Alteration) compareModelAdd(b Alteration) bool {
|
||||
if a.ModelAdd == nil || b.ModelAdd == nil {
|
||||
return a.ModelAdd == b.ModelAdd
|
||||
}
|
||||
return a.ModelAdd.Model.Compare(*b.ModelAdd.Model)
|
||||
}
|
||||
|
||||
func (a Alteration) compareModelDelete(b Alteration) bool {
|
||||
if a.ModelDelete == nil || b.ModelDelete == nil {
|
||||
return a.ModelDelete == b.ModelDelete
|
||||
}
|
||||
return a.ModelDelete.Model.Compare(*b.ModelDelete.Model)
|
||||
}
|
||||
69
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/attribute_codec.go
generated
vendored
Normal file
69
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/attribute_codec.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package dal
|
||||
|
||||
type (
|
||||
AttributeCodecType string
|
||||
|
||||
// Codec defines how values for a specific model attribute
|
||||
// are retrieved or stored
|
||||
//
|
||||
// If attribute does not have store strategy set
|
||||
// store driver should use attribute name to determinate
|
||||
// source/destination of the value (table column, json document property)
|
||||
Codec interface {
|
||||
Type() AttributeCodecType
|
||||
SingleValueOnly() bool
|
||||
}
|
||||
|
||||
CodecPlain struct{}
|
||||
|
||||
// CodecRecordValueSetJSON defines that values are encoded/decoded into
|
||||
// a JSON simple document { [_: string]: Array<unknown> }
|
||||
//
|
||||
// This only solves
|
||||
// Attribute{Ident: "foo", Store: StoreCodecRecordValueJSON{ Ident: "bar" }
|
||||
// => "bar"->'foo'->0
|
||||
CodecRecordValueSetJSON struct {
|
||||
Ident string
|
||||
}
|
||||
|
||||
// handling complex JSON documents
|
||||
//StoreCodecJSON struct {
|
||||
// Ident string
|
||||
// Path []any
|
||||
//}
|
||||
//
|
||||
// { "@value": ... "@type": .... }
|
||||
// StoreCodecJSONLD struct { Ident string; Path []any }
|
||||
|
||||
// StoreCodecXML
|
||||
//StoreCodecXML struct {}
|
||||
|
||||
// CodecAlias defines case when value is not stored
|
||||
// under the same column (in case of an SQL table)
|
||||
//
|
||||
// Value of CodecAlias.Ident is used as base
|
||||
// and value of Attribute.Ident holding CodecAlias is used
|
||||
// as an alias!
|
||||
//
|
||||
// Attribute{Ident: "foo", Store: CodecAlias{ Ident: "bar" }
|
||||
// => "bar" as "foo"
|
||||
CodecAlias struct {
|
||||
Ident string
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
AttributeCodecPlain AttributeCodecType = "corteza::dal:attribute-codec:plain"
|
||||
AttributeCodecRecordValueSetJSON AttributeCodecType = "corteza::dal:attribute-codec:record-value-set-json"
|
||||
AttributeCodecAlias AttributeCodecType = "corteza::dal:attribute-codec:alias"
|
||||
)
|
||||
|
||||
func (*CodecPlain) Type() AttributeCodecType { return AttributeCodecPlain }
|
||||
func (*CodecRecordValueSetJSON) Type() AttributeCodecType {
|
||||
return AttributeCodecRecordValueSetJSON
|
||||
}
|
||||
func (*CodecAlias) Type() AttributeCodecType { return AttributeCodecAlias }
|
||||
|
||||
func (*CodecPlain) SingleValueOnly() bool { return true }
|
||||
func (*CodecRecordValueSetJSON) SingleValueOnly() bool { return false }
|
||||
func (*CodecAlias) SingleValueOnly() bool { return true }
|
||||
203
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/attribute_types.go
generated
vendored
Normal file
203
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/attribute_types.go
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
package dal
|
||||
|
||||
type (
|
||||
AttributeType string
|
||||
|
||||
// temp
|
||||
Type interface {
|
||||
Type() AttributeType
|
||||
IsNullable() bool
|
||||
}
|
||||
|
||||
// TypeID handles ID (uint64) coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
//
|
||||
// It is always Corteza ID
|
||||
TypeID struct {
|
||||
GeneratedByStore bool
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue uint64
|
||||
}
|
||||
|
||||
// TypeRef handles ID (uint64) coding + reference info
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeRef struct {
|
||||
// defaults to ID
|
||||
RefAttribute string
|
||||
RefModel *ModelRef
|
||||
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue uint64
|
||||
}
|
||||
|
||||
// TypeTimestamp handles timestamp coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeTimestamp struct {
|
||||
Timezone bool
|
||||
Precision int
|
||||
Nullable bool
|
||||
|
||||
DefaultCurrentTimestamp bool
|
||||
}
|
||||
|
||||
// TypeTime handles time coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeTime struct {
|
||||
Timezone bool
|
||||
TimezonePrecision bool
|
||||
Precision int
|
||||
Nullable bool
|
||||
|
||||
DefaultCurrentTimestamp bool
|
||||
}
|
||||
|
||||
// TypeDate handles date coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, jsonb...)
|
||||
TypeDate struct {
|
||||
//
|
||||
Nullable bool
|
||||
|
||||
DefaultCurrentTimestamp bool
|
||||
}
|
||||
|
||||
// TypeNumber handles number coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, jsonb...)
|
||||
TypeNumber struct {
|
||||
Precision int
|
||||
Scale int
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue float64
|
||||
Meta map[string]any
|
||||
}
|
||||
|
||||
// TypeText handles string coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, jsonb...)
|
||||
TypeText struct {
|
||||
Length uint
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue string
|
||||
}
|
||||
|
||||
// TypeBoolean
|
||||
TypeBoolean struct {
|
||||
//
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue bool
|
||||
}
|
||||
|
||||
// TypeEnum
|
||||
TypeEnum struct {
|
||||
Values []string
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue string
|
||||
}
|
||||
|
||||
// TypeGeometry
|
||||
TypeGeometry struct {
|
||||
//
|
||||
Nullable bool
|
||||
}
|
||||
|
||||
// TypeJSON handles coding of arbitrary data into JSON structure
|
||||
// NOT TO BE CONFUSED with encodedField
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeJSON struct {
|
||||
//
|
||||
Nullable bool
|
||||
|
||||
HasDefault bool
|
||||
DefaultValue any
|
||||
}
|
||||
|
||||
// TypeBlob store/return data as
|
||||
TypeBlob struct {
|
||||
//
|
||||
Nullable bool
|
||||
}
|
||||
|
||||
TypeUUID struct {
|
||||
Nullable bool
|
||||
}
|
||||
|
||||
TypeNumberStoreNativeType string
|
||||
)
|
||||
|
||||
const (
|
||||
AttributeTypeID AttributeType = "corteza::dal:attribute-type:id"
|
||||
AttributeTypeRef AttributeType = "corteza::dal:attribute-type:ref"
|
||||
AttributeTypeTimestamp AttributeType = "corteza::dal:attribute-type:timestamp"
|
||||
AttributeTypeTime AttributeType = "corteza::dal:attribute-type:time"
|
||||
AttributeTypeDate AttributeType = "corteza::dal:attribute-type:date"
|
||||
AttributeTypeNumber AttributeType = "corteza::dal:attribute-type:number"
|
||||
AttributeTypeText AttributeType = "corteza::dal:attribute-type:text"
|
||||
AttributeTypeBoolean AttributeType = "corteza::dal:attribute-type:boolean"
|
||||
AttributeTypeEnum AttributeType = "corteza::dal:attribute-type:enum"
|
||||
AttributeTypeGeometry AttributeType = "corteza::dal:attribute-type:geometry"
|
||||
AttributeTypejson AttributeType = "corteza::dal:attribute-type:json"
|
||||
AttributeTypeBlob AttributeType = "corteza::dal:attribute-type:blob"
|
||||
AttributeTypeUUID AttributeType = "corteza::dal:attribute-type:uuid"
|
||||
)
|
||||
|
||||
func (t TypeID) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeRef) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeTimestamp) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeTime) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeDate) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeNumber) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeText) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeBoolean) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeEnum) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeGeometry) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeJSON) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeBlob) IsNullable() bool { return t.Nullable }
|
||||
func (t TypeUUID) IsNullable() bool { return t.Nullable }
|
||||
|
||||
func (t TypeID) Type() AttributeType { return AttributeTypeID }
|
||||
func (t TypeRef) Type() AttributeType { return AttributeTypeRef }
|
||||
func (t TypeTimestamp) Type() AttributeType { return AttributeTypeTimestamp }
|
||||
func (t TypeTime) Type() AttributeType { return AttributeTypeTime }
|
||||
func (t TypeDate) Type() AttributeType { return AttributeTypeDate }
|
||||
func (t TypeNumber) Type() AttributeType { return AttributeTypeNumber }
|
||||
func (t TypeText) Type() AttributeType { return AttributeTypeText }
|
||||
func (t TypeBoolean) Type() AttributeType { return AttributeTypeBoolean }
|
||||
func (t TypeEnum) Type() AttributeType { return AttributeTypeEnum }
|
||||
func (t TypeGeometry) Type() AttributeType { return AttributeTypeGeometry }
|
||||
func (t TypeJSON) Type() AttributeType { return AttributeTypejson }
|
||||
func (t TypeBlob) Type() AttributeType { return AttributeTypeBlob }
|
||||
func (t TypeUUID) Type() AttributeType { return AttributeTypeUUID }
|
||||
36
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/buffer.go
generated
vendored
Normal file
36
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
// OrderedBuffer provides the buffered data in the specified order
|
||||
OrderedBuffer interface {
|
||||
Buffer
|
||||
|
||||
// InOrder makes the buffer provide the stored data in the specified order
|
||||
InOrder(ss ...*filter.SortExpr) (err error)
|
||||
}
|
||||
|
||||
// Buffer provides a place where you can buffer the data provided by DAL
|
||||
// @note Buffers are currently primarily used for testing
|
||||
Buffer interface {
|
||||
// Seek moves the index pointer to the specified location
|
||||
// After the Seek call, a Next() call is required
|
||||
Seek(context.Context, int) error
|
||||
|
||||
// Len returns the number of elements in the buffer
|
||||
Len() int
|
||||
|
||||
Iterator
|
||||
Adder
|
||||
}
|
||||
|
||||
Adder interface {
|
||||
// Add adds a new ValueGetter to the buffer
|
||||
Add(context.Context, ValueGetter) (err error)
|
||||
}
|
||||
)
|
||||
42
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/conn.go
generated
vendored
Normal file
42
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/conn.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package dal
|
||||
|
||||
import "regexp"
|
||||
|
||||
type (
|
||||
ConnectionWrap struct {
|
||||
ID uint64
|
||||
|
||||
connection Connection
|
||||
params ConnectionParams
|
||||
Config ConnectionConfig
|
||||
}
|
||||
|
||||
ConnectionConfig struct {
|
||||
SensitivityLevelID uint64
|
||||
Label string
|
||||
|
||||
// When model does not specify the ident (table name for example), fallback to this
|
||||
ModelIdent string
|
||||
|
||||
// when a new model is added on a connection, it's ident
|
||||
// is verified against this regexp
|
||||
//
|
||||
// ident is considered valid if it matches one of the expressions
|
||||
// or if the list of checks is empty
|
||||
ModelIdentCheck []*regexp.Regexp
|
||||
}
|
||||
)
|
||||
|
||||
func checkIdent(ident string, rr ...*regexp.Regexp) bool {
|
||||
if len(rr) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, r := range rr {
|
||||
if r.MatchString(ident) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
299
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_aggregate.go
generated
vendored
Normal file
299
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_aggregate.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/ql"
|
||||
)
|
||||
|
||||
type (
|
||||
// Aggregate produces a series of aggregated rows from the provided sources based
|
||||
// on the specified group.
|
||||
Aggregate struct {
|
||||
Ident string
|
||||
RelSource string
|
||||
Filter filter.Filter
|
||||
filter internalFilter
|
||||
|
||||
Group []AggregateAttr
|
||||
OutAttributes []AggregateAttr
|
||||
|
||||
SourceAttributes []AttributeMapping
|
||||
|
||||
rel PipelineStep
|
||||
plan aggregatePlan
|
||||
analysis map[string]OpAnalysis
|
||||
}
|
||||
|
||||
// aggregatePlan outlines how the optimizer determined the dataset should be
|
||||
// aggregated
|
||||
aggregatePlan struct {
|
||||
// partialScan indicates we can partially pull data from the two sources
|
||||
// as the data is provided in the correct order.
|
||||
partialScan bool
|
||||
}
|
||||
|
||||
groupKey []any
|
||||
aggregateGroup struct {
|
||||
key groupKey
|
||||
agg *aggregator
|
||||
}
|
||||
|
||||
// AggregateAttr is a simple wrapper to outline aggregated attribute definitions
|
||||
AggregateAttr struct {
|
||||
MultiValue bool
|
||||
Key bool
|
||||
|
||||
// @todo change; temporary for compose service
|
||||
RawExpr string
|
||||
|
||||
Identifier string
|
||||
Label string
|
||||
Expression *ql.ASTNode
|
||||
Type Type
|
||||
Store Codec
|
||||
}
|
||||
)
|
||||
|
||||
func (def *Aggregate) Identifier() string {
|
||||
return def.Ident
|
||||
}
|
||||
|
||||
func (def *Aggregate) Sources() []string {
|
||||
return []string{def.RelSource}
|
||||
}
|
||||
|
||||
func (def *Aggregate) Attributes() [][]AttributeMapping {
|
||||
aa := append(def.Group, def.OutAttributes...)
|
||||
out := make([]AttributeMapping, 0, len(aa))
|
||||
|
||||
for _, a := range aa {
|
||||
out = append(out, a.toSimpleAttr())
|
||||
}
|
||||
|
||||
return [][]AttributeMapping{out}
|
||||
}
|
||||
|
||||
func (def *Aggregate) Analyze(ctx context.Context) (err error) {
|
||||
// @todo proper analysis; for now we'll leave this as defaults
|
||||
def.analysis = map[string]OpAnalysis{
|
||||
OpAnalysisIterate: {
|
||||
ScanCost: CostUnknown,
|
||||
SearchCost: CostUnknown,
|
||||
FilterCost: CostUnknown,
|
||||
SortCost: CostUnknown,
|
||||
OutputSize: SizeUnknown,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Aggregate) Analysis() map[string]OpAnalysis {
|
||||
return def.analysis
|
||||
}
|
||||
|
||||
func (def *Aggregate) Optimize(reqFilter internalFilter) (rspFilter internalFilter, err error) {
|
||||
err = fmt.Errorf("not implemented")
|
||||
return
|
||||
}
|
||||
|
||||
// iterator initializes an iterator based on the provided pipeline step definition
|
||||
func (def *Aggregate) iterator(ctx context.Context, src Iterator) (out Iterator, err error) {
|
||||
exec, err := def.init(ctx, src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return exec, exec.init(ctx)
|
||||
}
|
||||
|
||||
// dryrun performs step execution without interacting with the data
|
||||
// @todo consider rewording this
|
||||
func (def *Aggregate) dryrun(ctx context.Context) (err error) {
|
||||
_, err = def.init(ctx, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Aggregate) init(ctx context.Context, src Iterator) (exec *aggregate, err error) {
|
||||
exec = &aggregate{
|
||||
source: src,
|
||||
}
|
||||
|
||||
// Convert the provided filter into an internal filter
|
||||
if def.Filter != nil {
|
||||
def.filter, err = toInternalFilter(def.Filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Collect attributes from the underlaying step in case own are not provided
|
||||
if len(def.SourceAttributes) == 0 {
|
||||
def.SourceAttributes = collectAttributes(def.rel)
|
||||
}
|
||||
|
||||
// Index source attributes for group/aggregate definition validation
|
||||
srcAttrs := indexAttrs(def.SourceAttributes...)
|
||||
pp := newQlParser(func(ident ql.Ident) (_ ql.Ident, err error) {
|
||||
if _, ok := srcAttrs[ident.Value]; !ok {
|
||||
return ident, fmt.Errorf("unknown attribute %s", ident.Value)
|
||||
}
|
||||
return ident, nil
|
||||
})
|
||||
|
||||
prepAttr := func(attr AggregateAttr) (_ AggregateAttr, err error) {
|
||||
if attr.RawExpr != "" {
|
||||
// Parse (it already validates) raw expressions
|
||||
attr.Expression, err = pp.Parse(attr.RawExpr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Manually validate already parsed expressions
|
||||
err = attr.Expression.Traverse(func(a *ql.ASTNode) (bool, *ql.ASTNode, error) {
|
||||
if a.Symbol == "" {
|
||||
return true, a, nil
|
||||
}
|
||||
if _, ok := srcAttrs[a.Symbol]; !ok {
|
||||
return false, nil, fmt.Errorf("unknown attribute %s", a.Symbol)
|
||||
}
|
||||
|
||||
return true, a, nil
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return def.determineAttrType(attr, def.SourceAttributes)
|
||||
}
|
||||
|
||||
// Convert & validate group definitions
|
||||
// - groups
|
||||
outAttrs := make(map[string]bool, len(def.Group)+len(def.OutAttributes))
|
||||
for i, attr := range def.Group {
|
||||
attr, err = prepAttr(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
def.Group[i] = attr
|
||||
idtf := attr.Identifier
|
||||
|
||||
exec.groupDefs = append(exec.groupDefs, attr)
|
||||
outAttrs[idtf] = true
|
||||
}
|
||||
// - aggregates
|
||||
for i, attr := range def.OutAttributes {
|
||||
// @todo change when needed; currently, all aggregates are numbers
|
||||
attr.Type = &TypeNumber{}
|
||||
attr, err = prepAttr(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
def.OutAttributes[i] = attr
|
||||
idtf := attr.Identifier
|
||||
|
||||
exec.aggregateDefs = append(exec.aggregateDefs, attr)
|
||||
outAttrs[idtf] = true
|
||||
}
|
||||
|
||||
// Generic validation
|
||||
if len(def.Group) == 0 {
|
||||
err = fmt.Errorf("no group attributes specified")
|
||||
return
|
||||
}
|
||||
|
||||
if len(def.OutAttributes) == 0 {
|
||||
err = fmt.Errorf("no output attributes specified")
|
||||
return
|
||||
}
|
||||
|
||||
if len(def.SourceAttributes) == 0 {
|
||||
err = fmt.Errorf("no source attributes specified")
|
||||
return
|
||||
}
|
||||
|
||||
// order
|
||||
for _, s := range def.filter.OrderBy() {
|
||||
if _, ok := outAttrs[s.Column]; !ok {
|
||||
err = fmt.Errorf("order by attribute %s does not exist", s.Column)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Finishup
|
||||
exec.filter = def.filter
|
||||
exec.def = *def
|
||||
return
|
||||
}
|
||||
|
||||
// determineAttrType determines the type of the AggregateAttr based on it's definition
|
||||
// and source attributes
|
||||
func (def *Aggregate) determineAttrType(base AggregateAttr, ss []AttributeMapping) (out AggregateAttr, err error) {
|
||||
out = base
|
||||
if out.Type != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var root *ql.ASTNode
|
||||
var t Type
|
||||
|
||||
// If we have a symbol, then we'll use it to determine the type.
|
||||
// All current operations should return the same output type as the input one.
|
||||
//
|
||||
// In case of a function, use the output type of the root most function which has
|
||||
// a known type.
|
||||
//
|
||||
// Note, some refs (group and add for example) may not know their types so we need
|
||||
// to dig deeper.
|
||||
base.Expression.Traverse(func(a *ql.ASTNode) (bool, *ql.ASTNode, error) {
|
||||
if a.Symbol != "" {
|
||||
root = a
|
||||
return false, a, nil
|
||||
}
|
||||
|
||||
if a.Ref != "" {
|
||||
tmp := refToGvalExp[strings.ToLower(a.Ref)]
|
||||
if tmp == nil || tmp.OutType == nil || tmp.OutTypeUnknown {
|
||||
return true, a, nil
|
||||
}
|
||||
|
||||
if tmp.OutType != nil {
|
||||
t = tmp.OutType
|
||||
return false, a, nil
|
||||
}
|
||||
}
|
||||
return true, a, nil
|
||||
})
|
||||
|
||||
if root != nil {
|
||||
for _, s := range ss {
|
||||
if s.Identifier() == root.Symbol {
|
||||
t = s.Properties().Type
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.Type = t
|
||||
return
|
||||
}
|
||||
|
||||
func (a AggregateAttr) toSimpleAttr() SimpleAttr {
|
||||
return SimpleAttr{
|
||||
Ident: a.Identifier,
|
||||
Props: MapProperties{
|
||||
Label: a.Label,
|
||||
IsPrimary: a.Key,
|
||||
Type: a.Type,
|
||||
},
|
||||
|
||||
// @todo won't matter for now
|
||||
Expr: "",
|
||||
Src: "",
|
||||
}
|
||||
}
|
||||
362
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_datasource.go
generated
vendored
Normal file
362
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_datasource.go
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/ql"
|
||||
)
|
||||
|
||||
type (
|
||||
// Datasource is a simple passthrough step for underlaying datasources.
|
||||
// It exists primarily to make operations consistent.
|
||||
Datasource struct {
|
||||
Ident string
|
||||
Filter filter.Filter
|
||||
ModelRef ModelRef
|
||||
filter internalFilter
|
||||
|
||||
OutAttributes []AttributeMapping
|
||||
|
||||
analysis map[string]OpAnalysis
|
||||
connection *ConnectionWrap
|
||||
model *Model
|
||||
|
||||
// clobbered lists all of the steps that are offloaded into the datasource.
|
||||
// The list is provided in order; the first step is the first step which should execute
|
||||
// in the report pipeline.
|
||||
//
|
||||
// @todo change to generic step; doing this because I can for now
|
||||
clobbered []*Aggregate
|
||||
|
||||
// provided in the init step so we can omit some code in the exec step
|
||||
// @todo consider removing this
|
||||
auxIter Iterator
|
||||
}
|
||||
)
|
||||
|
||||
func (def *Datasource) Identifier() string {
|
||||
return def.Ident
|
||||
}
|
||||
|
||||
func (def *Datasource) Sources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (def *Datasource) Attributes() [][]AttributeMapping {
|
||||
if len(def.clobbered) > 0 {
|
||||
return def.offloadedAttributes()
|
||||
}
|
||||
|
||||
return def.ownAttributes()
|
||||
}
|
||||
|
||||
func (def *Datasource) Analyze(ctx context.Context) (err error) {
|
||||
a, err := def.connection.connection.Analyze(ctx, def.model)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
def.analysis = map[string]OpAnalysis{
|
||||
OpAnalysisIterate: {
|
||||
ScanCost: CostUnknown,
|
||||
SearchCost: CostUnknown,
|
||||
FilterCost: CostUnknown,
|
||||
SortCost: CostUnknown,
|
||||
OutputSize: SizeUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
if _, ok := a[OpAnalysisAggregate]; ok {
|
||||
def.analysis[OpAnalysisAggregate] = a[OpAnalysisAggregate]
|
||||
}
|
||||
if _, ok := a[OpAnalysisJoin]; ok {
|
||||
def.analysis[OpAnalysisJoin] = a[OpAnalysisJoin]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Datasource) Analysis() map[string]OpAnalysis {
|
||||
return def.analysis
|
||||
}
|
||||
|
||||
func (def *Datasource) Optimize(req internalFilter) (res internalFilter, err error) {
|
||||
return internalFilter{}, fmt.Errorf("optimization not implemented")
|
||||
}
|
||||
|
||||
func (def *Datasource) init(ctx context.Context) (err error) {
|
||||
if def.model == nil {
|
||||
return fmt.Errorf("cannot initialize datasource: model not set")
|
||||
}
|
||||
if def.connection == nil {
|
||||
return fmt.Errorf("cannot initialize datasource: connection not set")
|
||||
}
|
||||
|
||||
if def.Filter != nil {
|
||||
def.filter, err = toInternalFilter(def.Filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(def.OutAttributes) == 0 {
|
||||
def.OutAttributes = def.outAttrsFromModel(def.model)
|
||||
}
|
||||
|
||||
pp := make([]string, 0, len(def.OutAttributes)/2+1)
|
||||
for _, a := range def.OutAttributes {
|
||||
if a.Properties().IsPrimary {
|
||||
pp = append(pp, a.Identifier())
|
||||
}
|
||||
}
|
||||
|
||||
// Assure and attempt to correct the provided sort to conform with the data set and the
|
||||
// paging cursor (if any)
|
||||
def.filter, err = assureSort(def.filter, pp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Firstly validate the base
|
||||
err = def.validate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(def.clobbered) > 0 {
|
||||
// @todo currently, we can only do one; change this when we tweak the DB
|
||||
// offloading code.
|
||||
ag := def.clobbered[0]
|
||||
|
||||
// Invoke the aggregation's init to perform the validation and preparation logic
|
||||
var wa *aggregate
|
||||
wa, err = ag.init(ctx, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Preprocess the filters to conform to connection API
|
||||
f, having, err := def.getAggregationFilters(def.filter, wa.filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
def.auxIter, err = def.connection.connection.Aggregate(ctx, def.model, f, wa.groupDefs, wa.aggregateDefs, having)
|
||||
return err
|
||||
}
|
||||
|
||||
def.auxIter, err = def.connection.connection.Search(ctx, def.model, def.filter)
|
||||
return err
|
||||
}
|
||||
|
||||
func (def *Datasource) exec(ctx context.Context) (out Iterator, err error) {
|
||||
if def.auxIter == nil {
|
||||
return nil, fmt.Errorf("datasource not initialized")
|
||||
}
|
||||
|
||||
return def.auxIter, nil
|
||||
}
|
||||
|
||||
func (def *Datasource) validate() (err error) {
|
||||
err = func() (err error) {
|
||||
if len(def.OutAttributes) == 0 {
|
||||
return fmt.Errorf("no attributes specified")
|
||||
}
|
||||
|
||||
return
|
||||
}()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid definition: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Datasource) outAttrsFromModel(model *Model) (attrs []AttributeMapping) {
|
||||
for _, attr := range model.Attributes {
|
||||
if attr.Type == nil {
|
||||
panic(fmt.Sprintf("impossible state: attribute %s has no type", attr.Ident))
|
||||
}
|
||||
|
||||
attrs = append(attrs, SimpleAttr{
|
||||
Ident: attr.Ident,
|
||||
Src: attr.Ident,
|
||||
Props: MapProperties{
|
||||
IsPrimary: attr.PrimaryKey,
|
||||
IsSystem: attr.System,
|
||||
IsFilterable: attr.Filterable,
|
||||
IsSortable: attr.Sortable,
|
||||
Nullable: attr.Type.IsNullable(),
|
||||
Type: attr.Type,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Datasource) shouldClobberAggregation() bool {
|
||||
costs, ok := def.analysis[OpAnalysisAggregate]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// @todo more to it; check and compare costs; for now we know that all rdbms
|
||||
// offloads will be faster.
|
||||
|
||||
_ = costs
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (def *Datasource) ownAttributes() [][]AttributeMapping {
|
||||
return [][]AttributeMapping{def.OutAttributes}
|
||||
}
|
||||
|
||||
func (def *Datasource) offloadedAttributes() [][]AttributeMapping {
|
||||
// The last offloaded step's attributes is what this step's iterator returns
|
||||
return def.clobbered[len(def.clobbered)-1].Attributes()
|
||||
}
|
||||
|
||||
func (def *Datasource) clobber(s PipelineStep) (ok bool) {
|
||||
switch cs := s.(type) {
|
||||
case *Aggregate:
|
||||
if !def.shouldClobberAggregation() {
|
||||
return false
|
||||
}
|
||||
|
||||
def.clobbered = append(def.clobbered, cs)
|
||||
return true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getAggregationFilters transforms the base and the aggregation filters to conform
|
||||
// to the store/dal's model.Aggregate API
|
||||
//
|
||||
// - The full filter.Filter return parameter is applied to the base dataset
|
||||
// - The QL node return parameter is applied only to the aggregated dataset
|
||||
//
|
||||
// @todo should we change the store's API to accept 2x filter.Filter? I think
|
||||
// that would allow the underlaying driver to decide how to handle them
|
||||
// instead of relying on what SQL does.
|
||||
func (def *Datasource) getAggregationFilters(base, agg internalFilter) (filter internalFilter, having *ql.ASTNode, err error) {
|
||||
var typedV expr.TypedValue
|
||||
filter = base
|
||||
|
||||
// Move the aggregation's order and limit to the base filter because those
|
||||
// are applied to the final output (per SQL)
|
||||
//
|
||||
// Leave base filtering parameters (filter, constraints, ...) as those are apploed
|
||||
// to the dataset before aggregation (which is correct).
|
||||
filter.orderBy = agg.orderBy
|
||||
filter.limit = agg.limit
|
||||
|
||||
// Convert the rest of the filtering parameters defined on the aggregation's filter
|
||||
// to the QL node.
|
||||
// ALl of that is applied after the aggregation which is correct.
|
||||
var nConstraints *ql.ASTNode
|
||||
if len(agg.constraints) > 0 {
|
||||
nConstraints = &ql.ASTNode{
|
||||
Ref: "and",
|
||||
}
|
||||
for k, c := range agg.constraints {
|
||||
arg := &ql.ASTNode{
|
||||
Ref: "or",
|
||||
}
|
||||
|
||||
for _, v := range c {
|
||||
typedV, err = expr.Typify(v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
arg.Args = append(arg.Args, &ql.ASTNode{
|
||||
Symbol: k,
|
||||
Value: ql.WrapValue(typedV),
|
||||
})
|
||||
}
|
||||
|
||||
nConstraints.Args = append(nConstraints.Args, &ql.ASTNode{
|
||||
Ref: "group",
|
||||
Args: ql.ASTNodeSet{arg},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var nStateConstraints *ql.ASTNode
|
||||
if len(agg.stateConstraints) > 0 {
|
||||
nStateConstraints = &ql.ASTNode{
|
||||
Ref: "and",
|
||||
}
|
||||
for k, c := range agg.stateConstraints {
|
||||
typedV, err = expr.Typify(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nStateConstraints.Args = append(nStateConstraints.Args, &ql.ASTNode{
|
||||
Symbol: k,
|
||||
Value: ql.WrapValue(typedV),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var nMetaConstraints *ql.ASTNode
|
||||
if len(agg.stateConstraints) > 0 {
|
||||
nMetaConstraints = &ql.ASTNode{
|
||||
Ref: "and",
|
||||
}
|
||||
for k, c := range agg.stateConstraints {
|
||||
typedV, err = expr.Typify(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nMetaConstraints.Args = append(nMetaConstraints.Args, &ql.ASTNode{
|
||||
Symbol: k,
|
||||
Value: ql.WrapValue(typedV),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
nExpression := agg.expParsed
|
||||
|
||||
var nCursor *ql.ASTNode
|
||||
if agg.cursor != nil {
|
||||
nCursor, err = agg.cursor.ToAST(nil, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
having = &ql.ASTNode{
|
||||
Ref: "and",
|
||||
}
|
||||
|
||||
if nConstraints != nil {
|
||||
having.Args = append(having.Args, nConstraints)
|
||||
}
|
||||
if nStateConstraints != nil {
|
||||
having.Args = append(having.Args, nStateConstraints)
|
||||
}
|
||||
if nMetaConstraints != nil {
|
||||
having.Args = append(having.Args, nMetaConstraints)
|
||||
}
|
||||
if nExpression != nil {
|
||||
having.Args = append(having.Args, nExpression)
|
||||
}
|
||||
if nCursor != nil {
|
||||
having.Args = append(having.Args, nCursor)
|
||||
}
|
||||
|
||||
// In case everything is empty, no need for the having part
|
||||
if len(having.Ref) == 0 {
|
||||
having = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
218
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_join.go
generated
vendored
Normal file
218
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_join.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
// Join produces a series of joined rows from the provided sources based
|
||||
// on the JoinPredicate.
|
||||
//
|
||||
// The join step produces an SQL left join-like output where all right rows
|
||||
// have a corresponding left row.
|
||||
Join struct {
|
||||
Ident string
|
||||
RelLeft string
|
||||
RelRight string
|
||||
// @todo allow multiple join predicates; for now (for easier indexing)
|
||||
// only allow one (this is the same as we had before)
|
||||
On JoinPredicate
|
||||
Filter filter.Filter
|
||||
filter internalFilter
|
||||
|
||||
OutAttributes []AttributeMapping
|
||||
LeftAttributes []AttributeMapping
|
||||
RightAttributes []AttributeMapping
|
||||
|
||||
relLeft PipelineStep
|
||||
relRight PipelineStep
|
||||
plan joinPlan
|
||||
analysis map[string]OpAnalysis
|
||||
}
|
||||
|
||||
// JoinPredicate determines the attributes the two datasets should get joined on
|
||||
JoinPredicate struct {
|
||||
Left string
|
||||
Right string
|
||||
}
|
||||
|
||||
// joinPlan outlines how the optimizer determined the two datasets should be
|
||||
// joined on.
|
||||
joinPlan struct {
|
||||
// @todo add strategy when we have different strategies implemented
|
||||
// strategy string
|
||||
|
||||
// partialScan indicates we can partially pull data from the two sources
|
||||
// as the data is provided in the correct order.
|
||||
partialScan bool
|
||||
}
|
||||
)
|
||||
|
||||
func (def *Join) Identifier() string {
|
||||
return def.Ident
|
||||
}
|
||||
|
||||
func (def *Join) Sources() []string {
|
||||
return []string{def.RelLeft, def.RelRight}
|
||||
}
|
||||
|
||||
func (def *Join) Attributes() [][]AttributeMapping {
|
||||
return [][]AttributeMapping{def.OutAttributes}
|
||||
}
|
||||
|
||||
func (def *Join) Analyze(ctx context.Context) (err error) {
|
||||
// @todo proper analysis; for now we'll leave this as defaults
|
||||
def.analysis = map[string]OpAnalysis{
|
||||
OpAnalysisIterate: {
|
||||
ScanCost: CostUnknown,
|
||||
SearchCost: CostUnknown,
|
||||
FilterCost: CostUnknown,
|
||||
SortCost: CostUnknown,
|
||||
OutputSize: SizeUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Join) Analysis() map[string]OpAnalysis {
|
||||
return def.analysis
|
||||
}
|
||||
|
||||
func (def *Join) Optimize(req internalFilter) (res internalFilter, err error) {
|
||||
err = fmt.Errorf("not implemented")
|
||||
return
|
||||
}
|
||||
|
||||
// iterator initializes an iterator based on the provided pipeline step definition
|
||||
func (def *Join) iterator(ctx context.Context, left, right Iterator) (out Iterator, err error) {
|
||||
exec, err := def.init(ctx, left, right)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get pred. type
|
||||
// @todo should validate that both pred. types are the same/compatible
|
||||
var pt Type
|
||||
for _, a := range def.LeftAttributes {
|
||||
if a.Identifier() == def.On.Left {
|
||||
pt = a.Properties().Type
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return exec, exec.init(ctx, pt)
|
||||
}
|
||||
|
||||
// dryrun performs step execution without interacting with the data
|
||||
// @todo consider rewording this
|
||||
func (def *Join) dryrun(ctx context.Context) (err error) {
|
||||
_, err = def.init(ctx, nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Join) init(ctx context.Context, left, right Iterator) (exec *joinLeft, err error) {
|
||||
exec = &joinLeft{
|
||||
leftSource: left,
|
||||
rightSource: right,
|
||||
}
|
||||
|
||||
// Convert the provided filter into an internal filter
|
||||
if def.Filter != nil {
|
||||
def.filter, err = toInternalFilter(def.Filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Collect attributes from the underlaying step in case own are not provided
|
||||
if len(def.LeftAttributes) == 0 {
|
||||
def.LeftAttributes = collectAttributes(def.relLeft)
|
||||
}
|
||||
if len(def.RightAttributes) == 0 {
|
||||
def.RightAttributes = collectAttributes(def.relRight)
|
||||
}
|
||||
|
||||
// When the output attributes are not provided, we determine them based on the input
|
||||
// attributes.
|
||||
//
|
||||
// The join step prefixes each attribute with the identifier of the source they came from.
|
||||
// This avoids name collisions.
|
||||
// @todo consider improving this to only prefix when there is a name collision
|
||||
// and allow the nested attr. to be more permissive; for example, instead of
|
||||
// requiring a.b.c only b.c or c would be enough.
|
||||
if len(def.OutAttributes) == 0 {
|
||||
ins := func(srcIdent string, aa []AttributeMapping) {
|
||||
for _, a := range aa {
|
||||
p := a.Properties()
|
||||
def.OutAttributes = append(def.OutAttributes, SimpleAttr{
|
||||
Ident: fmt.Sprintf("%s%s%s", srcIdent, attributeNestingSeparator, a.Identifier()),
|
||||
Expr: a.Expression(),
|
||||
Src: a.Identifier(),
|
||||
Props: p,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def.OutAttributes = make([]AttributeMapping, 0, len(def.LeftAttributes)+len(def.RightAttributes))
|
||||
ins(def.RelLeft, def.LeftAttributes)
|
||||
ins(def.RelRight, def.RightAttributes)
|
||||
}
|
||||
|
||||
// Assure and attempt to correct the provided sort to conform with the data set and the
|
||||
// paging cursor (if any)
|
||||
def.filter, err = assureSort(def.filter, exec.collectPrimaryAttributes(def.OutAttributes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Index attrs for validations
|
||||
leftSrcAttrs := indexAttrs(def.LeftAttributes...)
|
||||
rightSrcAttrs := indexAttrs(def.RightAttributes...)
|
||||
outAttrs := indexAttrs(def.OutAttributes...)
|
||||
|
||||
for _, a := range def.OutAttributes {
|
||||
if !leftSrcAttrs[a.Source()] && !rightSrcAttrs[a.Source()] {
|
||||
return nil, fmt.Errorf("unknown attribute %s", a.Source())
|
||||
}
|
||||
}
|
||||
|
||||
// Generic validation
|
||||
if !leftSrcAttrs[def.On.Left] {
|
||||
return nil, fmt.Errorf("unknown join predicate attribute %s", def.On.Left)
|
||||
}
|
||||
if !rightSrcAttrs[def.On.Right] {
|
||||
return nil, fmt.Errorf("unknown join predicate attribute %s", def.On.Right)
|
||||
}
|
||||
|
||||
if len(def.OutAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no attributes specified")
|
||||
}
|
||||
if len(def.LeftAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no left attributes specified")
|
||||
}
|
||||
if len(def.RightAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no right attributes specified")
|
||||
}
|
||||
|
||||
if def.On.Left == "" {
|
||||
return nil, fmt.Errorf("no left attribute in the join predicate specified")
|
||||
}
|
||||
if def.On.Right == "" {
|
||||
return nil, fmt.Errorf("no right attribute in the join predicate specified")
|
||||
}
|
||||
|
||||
// order
|
||||
for _, s := range def.filter.OrderBy() {
|
||||
if _, ok := outAttrs[s.Column]; !ok {
|
||||
return nil, fmt.Errorf("order attribute %s does not exist", s.Column)
|
||||
}
|
||||
}
|
||||
|
||||
exec.filter = def.filter
|
||||
exec.def = *def
|
||||
return
|
||||
}
|
||||
289
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_link.go
generated
vendored
Normal file
289
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/def_link.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
// Link produces a series of left and corresponding right rows based on the
|
||||
// provided sources and the LinkPredicate.
|
||||
//
|
||||
// The Link step produces an SQL left join-like output where left and right
|
||||
// rows are served separately and the left rows are not duplicated.
|
||||
Link struct {
|
||||
Ident string
|
||||
RelLeft string
|
||||
RelRight string
|
||||
// @todo allow multiple link predicates; for now (for easier indexing)
|
||||
// only allow one (this is the same as we had before)
|
||||
On LinkPredicate
|
||||
// @todo consider splitting filter into left and right filter
|
||||
filter internalFilter
|
||||
Filter filter.Filter
|
||||
|
||||
OutLeftAttributes []AttributeMapping
|
||||
OutRightAttributes []AttributeMapping
|
||||
LeftAttributes []AttributeMapping
|
||||
RightAttributes []AttributeMapping
|
||||
|
||||
relLeft PipelineStep
|
||||
relRight PipelineStep
|
||||
|
||||
plan linkPlan
|
||||
analysis map[string]OpAnalysis
|
||||
}
|
||||
|
||||
// LinkPredicate determines the attributes the two datasets should get joined on
|
||||
LinkPredicate struct {
|
||||
Left string
|
||||
Right string
|
||||
}
|
||||
|
||||
// linkPlan outlines how the optimizer determined the two datasets should be
|
||||
// joined on.
|
||||
linkPlan struct {
|
||||
// @todo add strategy when we have different strategies implemented
|
||||
// strategy string
|
||||
|
||||
// partialScan indicates we can partially pull data from the two sources
|
||||
// as the data is provided in the correct order.
|
||||
partialScan bool
|
||||
}
|
||||
|
||||
rowLink struct {
|
||||
a, b ValueGetter
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
LinkRefIdent = "$sys.ref"
|
||||
)
|
||||
|
||||
func (def *Link) Identifier() string {
|
||||
return def.Ident
|
||||
}
|
||||
|
||||
func (def *Link) Sources() []string {
|
||||
return []string{def.RelLeft, def.RelRight}
|
||||
}
|
||||
|
||||
func (def *Link) Attributes() [][]AttributeMapping {
|
||||
return [][]AttributeMapping{def.OutLeftAttributes, def.OutRightAttributes}
|
||||
}
|
||||
|
||||
func (def *Link) Analyze(ctx context.Context) (err error) {
|
||||
// @todo proper analysis; for now we'll leave this as defaults
|
||||
def.analysis = map[string]OpAnalysis{
|
||||
OpAnalysisIterate: {
|
||||
ScanCost: CostUnknown,
|
||||
SearchCost: CostUnknown,
|
||||
FilterCost: CostUnknown,
|
||||
SortCost: CostUnknown,
|
||||
OutputSize: SizeUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Link) Analysis() map[string]OpAnalysis {
|
||||
return def.analysis
|
||||
}
|
||||
|
||||
func (def *Link) Optimize(req internalFilter) (res internalFilter, err error) {
|
||||
err = fmt.Errorf("not implemented")
|
||||
return
|
||||
}
|
||||
|
||||
// iterator initializes an iterator based on the provided pipeline step definition
|
||||
func (def *Link) iterator(ctx context.Context, left, right Iterator) (out Iterator, err error) {
|
||||
x, err := def.init(ctx, left, right)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get pred. type
|
||||
// @todo should validate that both pred. types are the same/compatible
|
||||
var pt Type
|
||||
for _, a := range def.LeftAttributes {
|
||||
if a.Identifier() == def.On.Left {
|
||||
pt = a.Properties().Type
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Collect order by attributes which appear in the right source
|
||||
rightAttrs := indexAttrs(def.RightAttributes...)
|
||||
rightOrderAttrs := make([]string, 0, len(def.filter.OrderBy()))
|
||||
for _, o := range def.filter.OrderBy() {
|
||||
if rightAttrs[o.Column] {
|
||||
rightOrderAttrs = append(rightOrderAttrs, o.Column)
|
||||
}
|
||||
}
|
||||
|
||||
return x, x.init(ctx, pt, rightOrderAttrs)
|
||||
}
|
||||
|
||||
// dryrun performs step execution without interacting with the data
|
||||
// @todo consider rewording this
|
||||
func (def *Link) dryrun(ctx context.Context) (err error) {
|
||||
_, err = def.init(ctx, nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (def *Link) init(ctx context.Context, left, right Iterator) (exec *linkLeft, err error) {
|
||||
exec = &linkLeft{
|
||||
leftSource: left,
|
||||
rightSource: right,
|
||||
}
|
||||
|
||||
// Convert the provided filter into an internal filter
|
||||
if def.Filter != nil {
|
||||
def.filter, err = toInternalFilter(def.Filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Collect attributes from the underlaying step in case own are not provided
|
||||
if len(def.LeftAttributes) == 0 {
|
||||
def.LeftAttributes = collectAttributes(def.relLeft)
|
||||
}
|
||||
if len(def.RightAttributes) == 0 {
|
||||
def.RightAttributes = collectAttributes(def.relRight)
|
||||
}
|
||||
|
||||
// @todo this isn't quite ok -- the ident of left/right must become the src of the out.
|
||||
// An edge-case but it should be covered.
|
||||
if len(def.OutLeftAttributes) == 0 {
|
||||
def.OutLeftAttributes = def.LeftAttributes
|
||||
}
|
||||
if len(def.OutRightAttributes) == 0 {
|
||||
def.OutRightAttributes = def.RightAttributes
|
||||
}
|
||||
|
||||
// Index source attributes for group/aggregate definition validation
|
||||
leftSrcAttrs := indexAttrs(def.LeftAttributes...)
|
||||
rightSrcAttrs := indexAttrs(def.RightAttributes...)
|
||||
outAttrs := indexAttrs(def.OutLeftAttributes...)
|
||||
indexAttrsInto(outAttrs, def.OutRightAttributes...)
|
||||
|
||||
// Check if source attributes are present
|
||||
for _, a := range append(def.OutLeftAttributes, def.OutRightAttributes...) {
|
||||
if !leftSrcAttrs[a.Source()] && !rightSrcAttrs[a.Source()] {
|
||||
return nil, fmt.Errorf("unknown attribute %s", a.Source())
|
||||
}
|
||||
}
|
||||
|
||||
// Check link predicates
|
||||
if !leftSrcAttrs[def.On.Left] {
|
||||
return nil, fmt.Errorf("left link predicate %s does not exist", def.On.Left)
|
||||
}
|
||||
if !rightSrcAttrs[def.On.Right] {
|
||||
return nil, fmt.Errorf("right link predicate %s does not exist", def.On.Right)
|
||||
}
|
||||
|
||||
// General validation
|
||||
if len(def.OutLeftAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no left output attributes specified")
|
||||
}
|
||||
if len(def.OutRightAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no right output attributes specified")
|
||||
}
|
||||
if len(def.LeftAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no left attributes specified")
|
||||
}
|
||||
if len(def.RightAttributes) == 0 {
|
||||
return nil, fmt.Errorf("no right attributes specified")
|
||||
}
|
||||
|
||||
if def.On.Left == "" {
|
||||
return nil, fmt.Errorf("no left attribute in the link predicate specified")
|
||||
}
|
||||
if def.On.Right == "" {
|
||||
return nil, fmt.Errorf("no right attribute in the link predicate specified")
|
||||
}
|
||||
|
||||
for _, s := range def.filter.OrderBy() {
|
||||
if _, ok := outAttrs[s.Column]; !ok {
|
||||
return nil, fmt.Errorf("order attribute %s does not exist", s.Column)
|
||||
}
|
||||
}
|
||||
|
||||
exec.filter = def.filter
|
||||
exec.def = *def
|
||||
return
|
||||
}
|
||||
|
||||
func (r *rowLink) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
// @todo this way of determining multivalue is not entirely ok but we can get away with it
|
||||
// since we have special functions to work with those.
|
||||
// We need to somehow inform this bit as to what fields are multi value.
|
||||
row, counts := r.getGetter(k)
|
||||
if row == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cc := counts[k]
|
||||
if cc == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if cc == 1 {
|
||||
return row.GetValue(k, 0)
|
||||
}
|
||||
|
||||
out := make([]any, cc)
|
||||
for i := uint(0); i < cc; i++ {
|
||||
out[i], _ = row.GetValue(k, i)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *rowLink) GetValue(name string, pos uint) (v any, err error) {
|
||||
row, a := r.getGetter(name)
|
||||
if row == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cc, ok := a[name]; ok {
|
||||
if pos >= cc {
|
||||
return nil, nil
|
||||
}
|
||||
return row.GetValue(name, pos)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *rowLink) CountValues() (out map[string]uint) {
|
||||
out = make(map[string]uint)
|
||||
|
||||
for k, c := range r.a.CountValues() {
|
||||
out[k] = c
|
||||
}
|
||||
for k, c := range r.b.CountValues() {
|
||||
out[k] = c
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getGetter returns the ValueGetter based on if the value is in a or b
|
||||
// If neither, it returns nil
|
||||
func (r *rowLink) getGetter(k string) (row ValueGetter, cc map[string]uint) {
|
||||
cc = r.a.CountValues()
|
||||
if _, ok := cc[k]; ok {
|
||||
return r.a, cc
|
||||
}
|
||||
|
||||
cc = r.b.CountValues()
|
||||
if _, ok := cc[k]; ok {
|
||||
return r.b, cc
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
189
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/diff.go
generated
vendored
Normal file
189
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/diff.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
package dal
|
||||
|
||||
type (
|
||||
modelDiffType string
|
||||
// ModelDiff defines one identified missmatch between two models
|
||||
ModelDiff struct {
|
||||
Type modelDiffType
|
||||
// Original will be nil when a new attribute is being added
|
||||
Original *Attribute
|
||||
// Asserted will be nil wen an existing attribute is being removed
|
||||
Asserted *Attribute
|
||||
}
|
||||
|
||||
ModelDiffSet []*ModelDiff
|
||||
)
|
||||
|
||||
const (
|
||||
AttributeMissing modelDiffType = "attributeMissing"
|
||||
AttributeTypeMissmatch modelDiffType = "typeMissmatch"
|
||||
AttributeSensitivityMismatch modelDiffType = "sensitivityMismatch"
|
||||
AttributeCodecMismatch modelDiffType = "codecMismatch"
|
||||
)
|
||||
|
||||
// Diff calculates the diff between models a and b where a is used as base
|
||||
func (a *Model) Diff(b *Model) (out ModelDiffSet) {
|
||||
if a == nil {
|
||||
a = &Model{}
|
||||
}
|
||||
if b == nil {
|
||||
b = &Model{}
|
||||
}
|
||||
|
||||
bIndex := make(map[string]struct {
|
||||
found bool
|
||||
attr *Attribute
|
||||
})
|
||||
for _, _attr := range b.Attributes {
|
||||
attr := _attr
|
||||
bIndex[attr.Ident] = struct {
|
||||
found bool
|
||||
attr *Attribute
|
||||
}{
|
||||
attr: attr,
|
||||
}
|
||||
}
|
||||
|
||||
aIndex := make(map[string]struct {
|
||||
found bool
|
||||
attr *Attribute
|
||||
})
|
||||
for _, _attr := range a.Attributes {
|
||||
attr := _attr
|
||||
aIndex[attr.Ident] = struct {
|
||||
found bool
|
||||
attr *Attribute
|
||||
}{
|
||||
attr: attr,
|
||||
}
|
||||
}
|
||||
|
||||
// Deleted and update ones
|
||||
for _, _attrA := range a.Attributes {
|
||||
attrA := _attrA
|
||||
// store is an interface to something that could be a pointer.
|
||||
// we need to copy it to make sure we don't get a nil pointer
|
||||
// make sure not to modify this since it would modify the original
|
||||
attrA.Store = _attrA.Store
|
||||
|
||||
// Missmatches
|
||||
attrBAux, ok := bIndex[attrA.Ident]
|
||||
if !ok {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeMissing,
|
||||
Original: attrA,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Typecheck
|
||||
if attrA.Type.Type() != attrBAux.attr.Type.Type() {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeTypeMissmatch,
|
||||
Original: attrA,
|
||||
Asserted: attrBAux.attr,
|
||||
})
|
||||
}
|
||||
|
||||
// Other stuff
|
||||
// @todo improve; for now it'll do
|
||||
if attrA.SensitivityLevelID != attrBAux.attr.SensitivityLevelID {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeSensitivityMismatch,
|
||||
Original: attrA,
|
||||
Asserted: attrBAux.attr,
|
||||
})
|
||||
}
|
||||
if attrA.Store.Type() != attrBAux.attr.Store.Type() {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeCodecMismatch,
|
||||
Original: attrA,
|
||||
Asserted: attrBAux.attr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// New
|
||||
for _, _attrB := range b.Attributes {
|
||||
attrB := _attrB
|
||||
|
||||
// Missmatches
|
||||
_, ok := aIndex[attrB.Ident]
|
||||
if !ok {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeMissing,
|
||||
Original: nil,
|
||||
Asserted: attrB,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dd ModelDiffSet) Alterations() (out []*Alteration) {
|
||||
add := func(a *Alteration) {
|
||||
out = append(out, a)
|
||||
}
|
||||
|
||||
for _, d := range dd {
|
||||
switch d.Type {
|
||||
case AttributeMissing:
|
||||
if d.Asserted == nil {
|
||||
// @todo if this was the last attribute we can consider dropping this column
|
||||
if d.Original.Store.Type() == AttributeCodecRecordValueSetJSON {
|
||||
break
|
||||
}
|
||||
|
||||
add(&Alteration{
|
||||
AttributeDelete: &AttributeDelete{
|
||||
Attr: d.Original,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
if d.Asserted.Store.Type() == AttributeCodecRecordValueSetJSON {
|
||||
add(&Alteration{
|
||||
AttributeAdd: &AttributeAdd{
|
||||
Attr: &Attribute{
|
||||
Ident: d.Asserted.StoreIdent(),
|
||||
Type: &TypeJSON{Nullable: false},
|
||||
Store: &CodecPlain{},
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
add(&Alteration{
|
||||
AttributeAdd: &AttributeAdd{
|
||||
Attr: d.Asserted,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case AttributeTypeMissmatch:
|
||||
// @todo we might have to do some validation earlier on
|
||||
if d.Original.Store.Type() == AttributeCodecRecordValueSetJSON {
|
||||
break
|
||||
}
|
||||
|
||||
add(&Alteration{
|
||||
AttributeReType: &AttributeReType{
|
||||
Attr: d.Asserted,
|
||||
To: d.Asserted.Type,
|
||||
},
|
||||
})
|
||||
|
||||
case AttributeCodecMismatch:
|
||||
add(&Alteration{
|
||||
AttributeReEncode: &AttributeReEncode{
|
||||
Attr: d.Asserted,
|
||||
To: d.Asserted.Store,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
257
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/driver.go
generated
vendored
Normal file
257
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/driver.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/ql"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
PKValues map[string]any
|
||||
|
||||
ConnectionParams struct {
|
||||
Type string `json:"type"`
|
||||
Params map[string]any `json:"params"`
|
||||
}
|
||||
|
||||
Connection interface {
|
||||
// Meta
|
||||
|
||||
// Models returns all the models the underlying connection already supports
|
||||
//
|
||||
// This is useful when adding support for new models since we can find out what
|
||||
// can work out of the box.
|
||||
Models(context.Context) (ModelSet, error)
|
||||
|
||||
// Operations returns all of the operations the given store supports
|
||||
Operations() OperationSet
|
||||
|
||||
// Can returns true if this store can handle the given operations
|
||||
Can(operations ...Operation) bool
|
||||
|
||||
// DML stuff
|
||||
|
||||
// Create stores the given data into the underlying database
|
||||
Create(ctx context.Context, m *Model, rr ...ValueGetter) error
|
||||
|
||||
// Update updates the given value in the underlying connection
|
||||
Update(ctx context.Context, m *Model, r ValueGetter) error
|
||||
|
||||
// Lookup returns one bit of data
|
||||
Lookup(context.Context, *Model, ValueGetter, ValueSetter) error
|
||||
|
||||
// Search returns an iterator which can be used to access all if the bits
|
||||
Search(context.Context, *Model, filter.Filter) (Iterator, error)
|
||||
|
||||
// Count returns the total number of rows matching the filter
|
||||
Count(context.Context, *Model, filter.Filter) (uint, error)
|
||||
|
||||
// Analyze returns the operation analysis the connection can perform for the model
|
||||
Analyze(ctx context.Context, m *Model) (map[string]OpAnalysis, error)
|
||||
|
||||
// Aggregate returns the iterator with aggregated data from the base model
|
||||
Aggregate(ctx context.Context, m *Model, f filter.Filter, groupBy []AggregateAttr, aggrExpr []AggregateAttr, having *ql.ASTNode) (i Iterator, _ error)
|
||||
|
||||
// Delete deletes the given value
|
||||
Delete(ctx context.Context, m *Model, pkv ValueGetter) error
|
||||
|
||||
// Truncate deletes all the data for the given model
|
||||
Truncate(ctx context.Context, m *Model) error
|
||||
|
||||
// DDL stuff
|
||||
|
||||
// // returns all attribute types that driver supports
|
||||
// AttributeTypes() []data.AttributeType
|
||||
|
||||
// CreateModel adds support for the given models to the underlying database
|
||||
//
|
||||
// The operation returns an error if any of the models already exists.
|
||||
CreateModel(context.Context, ...*Model) error
|
||||
|
||||
// DeleteModel removes support for the given model from the underlying database
|
||||
DeleteModel(context.Context, ...*Model) error
|
||||
|
||||
// UpdateModel requests for metadata changes to the existing model
|
||||
//
|
||||
// Only metadata (such as idents) are affected; attributes can not be changed here
|
||||
UpdateModel(ctx context.Context, old *Model, new *Model) error
|
||||
|
||||
// AssertSchemaAlterations returns a new set of Alterations based on what the underlying
|
||||
// schema already provides -- it discards alterations for column additions that already exist, etc.
|
||||
AssertSchemaAlterations(ctx context.Context, sch *Model, aa ...*Alteration) ([]*Alteration, error)
|
||||
|
||||
// ApplyAlteration applies the given alterations to the underlying schema
|
||||
ApplyAlteration(ctx context.Context, sch *Model, aa ...*Alteration) []error
|
||||
}
|
||||
|
||||
ConnectionCloser interface {
|
||||
// Close closes the store connection allowing the driver to perform potential
|
||||
// cleanup operations
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
// Store provides an interface which CRS uses to interact with the underlying database
|
||||
|
||||
ValueGetter interface {
|
||||
CountValues() map[string]uint
|
||||
GetValue(string, uint) (any, error)
|
||||
}
|
||||
|
||||
ValueSetter interface {
|
||||
SetValue(string, uint, any) error
|
||||
}
|
||||
|
||||
ConnectorFn func(ctx context.Context, dsn string) (Connection, error)
|
||||
|
||||
DriverConnectionParam struct {
|
||||
Key string `json:"key"`
|
||||
ValueType string `json:"valueType"`
|
||||
MultiValue bool `json:"multiValue"`
|
||||
}
|
||||
|
||||
DriverConnectionConfig struct {
|
||||
Type string `json:"type"`
|
||||
Params []DriverConnectionParam `json:"params"`
|
||||
}
|
||||
|
||||
Driver struct {
|
||||
Type string `json:"type"`
|
||||
Connection DriverConnectionConfig `json:"connection"`
|
||||
Operations OperationSet `json:"operations"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
registeredConnectors = make(map[string]ConnectorFn)
|
||||
registeredDrivers = make(map[string]Driver)
|
||||
)
|
||||
|
||||
func (pkv PKValues) CountValues() map[string]uint {
|
||||
c := make(map[string]uint)
|
||||
for k := range pkv {
|
||||
c[k] = 1
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (pkv PKValues) GetValue(key string, _ uint) (any, error) {
|
||||
if val, has := pkv[key]; has {
|
||||
return val, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing")
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterConnector registers a new connector for the given DSN schema
|
||||
//
|
||||
// In case of a duplicate schema the latter overwrites the prior
|
||||
func RegisterConnector(fn ConnectorFn, tt ...string) {
|
||||
for _, t := range tt {
|
||||
registeredConnectors[t] = fn
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterDriver(d Driver) {
|
||||
registeredDrivers[d.Type] = d
|
||||
}
|
||||
|
||||
// connect opens a new StoreConnection for the given CRS
|
||||
func connect(ctx context.Context, log *zap.Logger, isDevelopment bool, cp ConnectionParams) (Connection, error) {
|
||||
if cp.Type != "corteza::dal:connection:dsn" {
|
||||
return nil, fmt.Errorf("cannot open connection: only DSN connections supported (got: %q)", cp.Type)
|
||||
}
|
||||
if cp.Params == nil {
|
||||
return nil, fmt.Errorf("cannot open connection: connection parameters not defined")
|
||||
}
|
||||
if _, ok := cp.Params["dsn"]; !ok {
|
||||
return nil, fmt.Errorf("cannot open connection: DSN not provided")
|
||||
}
|
||||
|
||||
dsn := cp.Params["dsn"].(string)
|
||||
|
||||
if isDevelopment {
|
||||
if strings.Contains(dsn, "{version}") {
|
||||
log.Warn("You're using DB_DSN with {version}, It is still in EXPERIMENTAL phase")
|
||||
log.Warn("Should be used only for development mode")
|
||||
log.Warn("You may experience instability")
|
||||
}
|
||||
expr := regexp.MustCompile(`[.\-]+`)
|
||||
version := expr.ReplaceAllString(os.Getenv("BUILD_VERSION"), "_")
|
||||
dsn = strings.Replace(dsn, "{version}", version, 1)
|
||||
}
|
||||
|
||||
var storeType = strings.SplitN(dsn, "://", 2)[0]
|
||||
if storeType == "" {
|
||||
// Backward compatibility
|
||||
storeType = "mysql"
|
||||
}
|
||||
|
||||
if conn, ok := registeredConnectors[storeType]; ok {
|
||||
return conn(ctx, dsn)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unknown store type used: %q (check your database configuration)", storeType)
|
||||
}
|
||||
}
|
||||
|
||||
func NewDSNDriverConnectionConfig() DriverConnectionConfig {
|
||||
return DriverConnectionConfig{
|
||||
Type: "corteza::dal:connection:dsn",
|
||||
Params: []DriverConnectionParam{{
|
||||
Key: "dsn",
|
||||
ValueType: "string",
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
//func NewHTTPDriverConnectionConfig() DriverConnectionConfig {
|
||||
// panic("not implemented NewHTTPDriverConnectionConfig")
|
||||
// return DriverConnectionConfig{
|
||||
// Type: "corteza::dal:connection:http",
|
||||
// Params: []DriverConnectionParam{{}},
|
||||
// }
|
||||
//}
|
||||
//func NewFederatedNodeDriverConnectionConfig() DriverConnectionConfig {
|
||||
// panic("not implemented NewFederatedNodeDriverConnectionConfig")
|
||||
// return DriverConnectionConfig{
|
||||
// Type: "corteza::dal:connection:federated-node",
|
||||
// Params: []DriverConnectionParam{{}},
|
||||
// }
|
||||
//}
|
||||
|
||||
//func NewDSNConnection(dsn string) ConnectionParams {
|
||||
// return ConnectionParams{
|
||||
// Type: "corteza::dal:connection:dsn",
|
||||
// Params: map[string]any{
|
||||
// "dsn": dsn,
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
|
||||
//func NewHTTPConnection(url string, headers, query map[string][]string) ConnectionParams {
|
||||
// return ConnectionParams{
|
||||
// Type: "corteza::dal:connection:http",
|
||||
// Params: map[string]any{
|
||||
// "url": url,
|
||||
// "headers": headers,
|
||||
// "query": query,
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func NewFederatedNodeConnection(url string, pairToken, authToken string) ConnectionParams {
|
||||
// return ConnectionParams{
|
||||
// Type: "corteza::dal:connection:federation-node",
|
||||
// Params: map[string]any{
|
||||
// "baseURL": url,
|
||||
// "pairToken": pairToken,
|
||||
// "authToken": authToken,
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
166
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/errors.go
generated
vendored
Normal file
166
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/errors.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
)
|
||||
|
||||
// Generic errors
|
||||
|
||||
func errModelNotFound(modelID uint64) error {
|
||||
return errors.NotFound("model %d does not exist", modelID)
|
||||
}
|
||||
|
||||
func errConnectionNotFound(connectionID uint64) error {
|
||||
return errors.NotFound("connection %d does not exist", connectionID)
|
||||
}
|
||||
|
||||
// Sensitivity level errors
|
||||
// - remove
|
||||
func errSensitivityLevelRemoveNotFound(sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot remove sensitivity level %d: sensitivity level does not exist", sensitivityLevelID)
|
||||
}
|
||||
|
||||
// Connection errors
|
||||
// - create
|
||||
func errConnectionCreateMissingSensitivityLevel(connectionID, sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot create connection %d: sensitivity level does not exist %d", connectionID, sensitivityLevelID)
|
||||
}
|
||||
func errConnectionCreateConnectionFailed(connectionID uint64, err error) error {
|
||||
return fmt.Errorf("cannot create connection %d: connection failed: %v", connectionID, err)
|
||||
}
|
||||
func errConnectionDeleteNotFound(connectionID uint64) error {
|
||||
return fmt.Errorf("cannot delete connection %d: connection does not exist", connectionID)
|
||||
}
|
||||
func errConnectionDeleteCloserFailed(connectionID uint64, err error) error {
|
||||
return fmt.Errorf("cannot delete connection %d: connection's driver failed to close: %v", connectionID, err)
|
||||
}
|
||||
|
||||
// - update
|
||||
func errConnectionUpdateNotFound(connectionID uint64) error {
|
||||
return fmt.Errorf("cannot update connection %d: connection does not exist", connectionID)
|
||||
}
|
||||
func errConnectionUpdateMissingSensitivityLevel(connectionID, sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot update connection %d: sensitivity level %d does not exist", connectionID, sensitivityLevelID)
|
||||
}
|
||||
|
||||
// Model errors
|
||||
// - create
|
||||
func errModelCreateProblematicConnection(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: connection has issues", modelID, connectionID)
|
||||
}
|
||||
func errModelCreateMissingConnection(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: connection does not exist", modelID, connectionID)
|
||||
}
|
||||
func errModelCreateMissingSensitivityLevel(connectionID, modelID, sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: sensitivity level %d does not exist", modelID, connectionID, sensitivityLevelID)
|
||||
}
|
||||
func errModelCreateGreaterSensitivityLevel(connectionID, modelID, modelSensitivityLevelID, connSensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: sensitivity level %d exceeds connection supported sensitivity level %d", modelID, connectionID, modelSensitivityLevelID, connSensitivityLevelID)
|
||||
}
|
||||
func errModelCreateMissingAttributeSensitivityLevel(connectionID, modelID, sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: attribute sensitivity level %d does not exist", modelID, connectionID, sensitivityLevelID)
|
||||
}
|
||||
func errModelCreateGreaterAttributeSensitivityLevel(connectionID, modelID, attrSensitivityLevelID, modelSensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: attribute sensitivity level %d exceeds model supported sensitivity level %d", modelID, connectionID, attrSensitivityLevelID, modelSensitivityLevelID)
|
||||
}
|
||||
func errModelCreateConnectionModelUnsupported(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: model already exists for connection but is not compatible with provided definition", modelID, connectionID)
|
||||
}
|
||||
func errModelCreateInvalidIdent(connectionID, modelID uint64, ident string) error {
|
||||
return fmt.Errorf("cannot create model %d on connection %d: malformed model ident %s", modelID, connectionID, ident)
|
||||
}
|
||||
|
||||
// - update
|
||||
func errModelUpdateProblematicConnection(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: connection has issues", modelID, connectionID)
|
||||
}
|
||||
func errModelUpdateMissingConnection(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: connection does not exist", modelID, connectionID)
|
||||
}
|
||||
func errModelUpdateConnectionModelUnsupported(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: model already exists for connection but is not compatible with provided definition", modelID, connectionID)
|
||||
}
|
||||
func errModelUpdateMissingOldModel(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: model does not exist", modelID, connectionID)
|
||||
}
|
||||
func errModelUpdateDuplicate(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: model already exists", modelID, connectionID)
|
||||
}
|
||||
func errModelUpdateConnectionMissmatch(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: cannot change model connection", modelID, connectionID)
|
||||
}
|
||||
func errModelUpdateMissingSensitivityLevel(connectionID, modelID, sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: sensitivity level %d does not exist", modelID, connectionID, sensitivityLevelID)
|
||||
}
|
||||
func errModelUpdateGreaterSensitivityLevel(connectionID, modelID, modelSensitivityLevelID, connSensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot update model %d on connection %d: sensitivity level %d exceeds connection supported sensitivity level %d", modelID, connectionID, modelSensitivityLevelID, connSensitivityLevelID)
|
||||
}
|
||||
|
||||
// - alterations
|
||||
func errModelRequiresAlteration(connectionID, modelID, batchID uint64) error {
|
||||
return fmt.Errorf("model %d on connection %d requires schema alterations: alteration batchID %d", modelID, connectionID, batchID)
|
||||
}
|
||||
|
||||
// Attribute errors
|
||||
// - Update
|
||||
func errAttributeUpdateProblematicConnection(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update attribute for model %d on connection %d: connection has issues", modelID, connectionID)
|
||||
}
|
||||
func errAttributeUpdateMissingModel(connectionID, modelID uint64) error {
|
||||
return fmt.Errorf("cannot update attribute for model %d on connection %d: model does not exist", modelID, connectionID)
|
||||
}
|
||||
func errAttributeUpdateMissingSensitivityLevel(connectionID, modelID, sensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot update attribute for model %d on connection %d: sensitivity level %d does not exist", modelID, connectionID, sensitivityLevelID)
|
||||
}
|
||||
func errAttributeUpdateGreaterSensitivityLevel(connectionID, modelID, attrSensitivityLevelID, modelSensitivityLevelID uint64) error {
|
||||
return fmt.Errorf("cannot update attribute for model %d on connection %d: sensitivity level %d exceeds model supported sensitivity level %d", modelID, connectionID, attrSensitivityLevelID, modelSensitivityLevelID)
|
||||
}
|
||||
|
||||
// Record errors
|
||||
|
||||
func errRecordOpProblematicConnection(connectionID uint64) error {
|
||||
return fmt.Errorf("cannot perform record operation: connection %d has issues", connectionID)
|
||||
}
|
||||
func errRecordOpProblematicModel(modelID uint64) error {
|
||||
return fmt.Errorf("cannot perform record operation: model %d has issues", modelID)
|
||||
}
|
||||
|
||||
// func errModelHigherSensitivity(model, connection string) error {
|
||||
// return errors.New(
|
||||
// errors.KindSensitiveData,
|
||||
|
||||
// "model sensitivity surpasses connection sensitivity",
|
||||
|
||||
// errors.Meta("type", "invalid sensitivity"),
|
||||
|
||||
// // Translation namespace & key
|
||||
// errors.Meta(locale.ErrorMetaNamespace{}, "internal"),
|
||||
// errors.Meta(locale.ErrorMetaKey{}, "dal.sensitivity.model-exceeds-connection"),
|
||||
// errors.Meta("model", model),
|
||||
// errors.Meta("connection", connection),
|
||||
|
||||
// errors.StackSkip(1),
|
||||
// errors.StackTrimAtFn("http.HandlerFunc.ServeHTTP"),
|
||||
// )
|
||||
// }
|
||||
|
||||
// func errAttributeHigherSensitivity(model, attribute string) error {
|
||||
// return errors.New(
|
||||
// errors.KindSensitiveData,
|
||||
|
||||
// "attribute sensitivity surpasses model sensitivity",
|
||||
|
||||
// errors.Meta("type", "invalid sensitivity"),
|
||||
|
||||
// // Translation namespace & key
|
||||
// errors.Meta(locale.ErrorMetaNamespace{}, "internal"),
|
||||
// errors.Meta(locale.ErrorMetaKey{}, "dal.sensitivity.attribute-exceeds-model"),
|
||||
// errors.Meta("model", model),
|
||||
// errors.Meta("attribute", attribute),
|
||||
|
||||
// errors.StackSkip(1),
|
||||
// errors.StackTrimAtFn("http.HandlerFunc.ServeHTTP"),
|
||||
// )
|
||||
// }
|
||||
589
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/exec_aggregate.go
generated
vendored
Normal file
589
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/exec_aggregate.go
generated
vendored
Normal file
@@ -0,0 +1,589 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/ql"
|
||||
"github.com/tidwall/btree"
|
||||
)
|
||||
|
||||
type (
|
||||
aggregate struct {
|
||||
def Aggregate
|
||||
filter internalFilter
|
||||
|
||||
source Iterator
|
||||
err error
|
||||
scanRow *Row
|
||||
planned bool
|
||||
|
||||
groupDefs []AggregateAttr
|
||||
aggregateDefs []AggregateAttr
|
||||
|
||||
rowTester tester
|
||||
keyWalker keyWalker
|
||||
|
||||
// Keep track of the registered groupIndex so we can easier implement the iterator.
|
||||
// Simple index tracking won't be enough, because btree is self-balancing -- things change
|
||||
//
|
||||
// @todo worth considering using the inmembuffer and fork it around...
|
||||
groupIndex *btree.Generic[*aggregateGroup]
|
||||
groups []*aggregateGroup
|
||||
i int
|
||||
|
||||
ctr int
|
||||
}
|
||||
|
||||
keyWalker func(context.Context, ValueGetter, func(context.Context, groupKey, ValueGetter) error) error
|
||||
)
|
||||
|
||||
// init initializes the execution step's state
|
||||
func (xs *aggregate) init(ctx context.Context) (err error) {
|
||||
// Initialize state variables
|
||||
xs.scanRow = xs.initScanRow()
|
||||
xs.groupIndex = btree.NewGeneric[*aggregateGroup](xs.compareGroupKeys)
|
||||
xs.groups = make([]*aggregateGroup, 0, 128)
|
||||
xs.rowTester, err = prepareGenericRowTester(xs.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize the key maker
|
||||
kk := make([]*ql.ASTNode, 0, len(xs.groupDefs))
|
||||
for _, a := range xs.groupDefs {
|
||||
kk = append(kk, a.Expression)
|
||||
}
|
||||
|
||||
xs.keyWalker, err = aggregateGroupKeyWalker(kk...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Apply the decided aggregation plan
|
||||
return xs.applyPlan(ctx)
|
||||
}
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Iterator methods
|
||||
|
||||
func (xs *aggregate) Next(ctx context.Context) (more bool) {
|
||||
// Assure aggregation plan was taken into account
|
||||
xs.err = xs.applyPlan(ctx)
|
||||
if xs.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Go next...
|
||||
more, xs.err = xs.next(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// next prepares the next scannable row
|
||||
//
|
||||
// The method is adjusted based on the defined aggregation plan.
|
||||
// The method performs appropriate filtering/sorting (if needed).
|
||||
func (xs *aggregate) next(ctx context.Context) (more bool, err error) {
|
||||
var g *aggregateGroup
|
||||
for {
|
||||
if xs.limitExceeded() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Make sure it's cleared out and ready for fresh data
|
||||
xs.scanRow.Reset()
|
||||
|
||||
// Make sure we have data
|
||||
err = xs.prepareNext(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Next group
|
||||
g, err = xs.nextGroup(ctx)
|
||||
if err != nil || g == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Make sure the key is in there
|
||||
// @todo potential optimization where we don't scan entire composited keys
|
||||
err = xs.scanKey(g, xs.scanRow)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Scan the data
|
||||
err = g.agg.Scan(xs.scanRow)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if we want to keep it
|
||||
k, err := xs.keep(ctx, xs.scanRow)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !k {
|
||||
continue
|
||||
}
|
||||
|
||||
xs.ctr++
|
||||
break
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (xs *aggregate) limitExceeded() bool {
|
||||
return xs.filter.limit > 0 && xs.ctr >= int(xs.filter.limit)
|
||||
}
|
||||
|
||||
func (xs *aggregate) More(limit uint, v ValueGetter) (err error) {
|
||||
// Redo the cursor
|
||||
xs.filter.cursor, err = filter.PagingCursorFrom(xs.filter.OrderBy(), v, xs.collectPrimaryAttributes()...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Redo the row tester
|
||||
xs.rowTester, err = prepareGenericRowTester(xs.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Redo the state
|
||||
// @todo adjust based on aggregation plan; reuse buffered, etc.
|
||||
xs.scanRow.Reset()
|
||||
xs.groupIndex = btree.NewGeneric[*aggregateGroup](xs.compareGroupKeys)
|
||||
xs.groups = make([]*aggregateGroup, 0, 128)
|
||||
xs.planned = false
|
||||
xs.i = 0
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *aggregate) Err() error { return s.err }
|
||||
|
||||
func (s *aggregate) Scan(dst ValueSetter) (err error) {
|
||||
if s.i < 0 {
|
||||
return fmt.Errorf("@todo err not initialized; next first")
|
||||
}
|
||||
|
||||
var v any
|
||||
for name, cc := range s.scanRow.CountValues() {
|
||||
for i := uint(0); i < cc; i++ {
|
||||
// omitting err here since it won't happen
|
||||
v, _ = s.scanRow.GetValue(name, i)
|
||||
err = dst.SetValue(name, i, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *aggregate) Close() error {
|
||||
if s.source != nil {
|
||||
return s.source.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *aggregate) BackCursor(v ValueGetter) (*filter.PagingCursor, error) {
|
||||
c, err := filter.PagingCursorFrom(s.filter.OrderBy(), v, s.collectPrimaryAttributes()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.ROrder = true
|
||||
c.LThen = s.filter.OrderBy().Reversed()
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (s *aggregate) ForwardCursor(v ValueGetter) (*filter.PagingCursor, error) {
|
||||
c, err := filter.PagingCursorFrom(s.filter.OrderBy(), v, s.collectPrimaryAttributes()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Utilities
|
||||
|
||||
// applyPlan runs additional initialization code based on the defined aggregation plan
|
||||
func (xs *aggregate) applyPlan(ctx context.Context) (err error) {
|
||||
if xs.planned || xs.err != nil {
|
||||
return xs.err
|
||||
}
|
||||
|
||||
xs.planned = true
|
||||
switch {
|
||||
case !xs.def.plan.partialScan:
|
||||
return xs.pullEntireSource(ctx)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *aggregate) prepareNext(ctx context.Context) (err error) {
|
||||
// Pull next chunk from source if not entirely buffered
|
||||
if xs.def.plan.partialScan {
|
||||
err = xs.pullNextChunk(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// pullEntireSource pulls the entire source into aggregators
|
||||
//
|
||||
// The entire source should be pulled when underlaying datasources can't provide
|
||||
// more appropriately ordered data due to costs.
|
||||
func (xs *aggregate) pullEntireSource(ctx context.Context) (err error) {
|
||||
if xs.source == nil {
|
||||
return fmt.Errorf("unable to pull data: no source defined")
|
||||
}
|
||||
|
||||
// @todo consider pre-populating the hashmaps
|
||||
r := &Row{
|
||||
counters: make(map[string]uint),
|
||||
values: make(valueSet),
|
||||
}
|
||||
|
||||
// Drain the source
|
||||
for xs.source.Next(ctx) {
|
||||
err = xs.source.Scan(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the key for this row
|
||||
// @todo we probably can reuse the key or at least cache keys and avoid re-computation.
|
||||
// My fairly hacky attempt boosted performance by ~20%
|
||||
err = xs.keyWalker(ctx, r, xs.addToGroup)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.Reset()
|
||||
}
|
||||
xs.err = xs.source.Err()
|
||||
if xs.err != nil {
|
||||
return xs.err
|
||||
}
|
||||
|
||||
xs.sortGroups()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// pullNextChunk pulls the next chunk into the aggregators
|
||||
//
|
||||
// Source should be pulled in chunks when the underlaying datasource can provide
|
||||
// more appropriately ordered data.
|
||||
func (xs *aggregate) pullNextChunk(ctx context.Context) (err error) {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (s *aggregate) addToGroup(ctx context.Context, key groupKey, r ValueGetter) (err error) {
|
||||
if s.groupIndex == nil {
|
||||
s.groupIndex = btree.NewGeneric[*aggregateGroup](s.compareGroupKeys)
|
||||
}
|
||||
|
||||
// Try to get the existing one; if it doesn't exist, push a new one
|
||||
// @todo this causes a bit of a memory pressure; investigate
|
||||
g, ok := s.groupIndex.Get(&aggregateGroup{key: key})
|
||||
if !ok {
|
||||
g, err = s.wrapGroup(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.groupIndex.Set(g)
|
||||
|
||||
s.groups = append(s.groups, g)
|
||||
}
|
||||
|
||||
// if it exists/was added, add it to the aggregator
|
||||
err = g.agg.Aggregate(ctx, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Group key comparator
|
||||
// @todo can this be moved to the struct's Less method?
|
||||
func (t *aggregate) compareGroupKeys(a, b *aggregateGroup) (out bool) {
|
||||
for i := range a.key {
|
||||
va := a.key[i]
|
||||
vb := b.key[i]
|
||||
out = out || compareValues(va, vb) < 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *aggregate) wrapGroup(ctx context.Context, key groupKey) (g *aggregateGroup, err error) {
|
||||
agg := Aggregator()
|
||||
|
||||
for _, a := range s.aggregateDefs {
|
||||
err = agg.AddAggregate(a.Identifier, a.Expression)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
g = &aggregateGroup{
|
||||
key: key,
|
||||
agg: agg,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *aggregate) nextGroup(ctx context.Context) (_ *aggregateGroup, err error) {
|
||||
if xs.i >= len(xs.groups) {
|
||||
return
|
||||
}
|
||||
|
||||
xs.i++
|
||||
return xs.groups[xs.i-1], nil
|
||||
}
|
||||
|
||||
func (xs *aggregate) scanKey(g *aggregateGroup, dst *Row) (err error) {
|
||||
for i, attr := range xs.groupDefs {
|
||||
// @todo multi value support?
|
||||
// omitting err; internal row won't raise them
|
||||
dst.SetValue(attr.Identifier, 0, g.key[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *aggregate) keep(ctx context.Context, r *Row) (bool, error) {
|
||||
if s.rowTester == nil {
|
||||
return true, nil
|
||||
}
|
||||
return s.rowTester.Test(ctx, r)
|
||||
}
|
||||
|
||||
// Each group key is a PK candidate; all together form a composite key
|
||||
func (s *aggregate) collectPrimaryAttributes() (out []string) {
|
||||
out = make([]string, 0, 2)
|
||||
for _, m := range s.def.Group {
|
||||
out = append(out, m.Identifier)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *aggregate) sortGroups() {
|
||||
sort.SliceStable(s.groups, func(i, j int) bool {
|
||||
ga := s.groups[i]
|
||||
gb := s.groups[j]
|
||||
|
||||
var (
|
||||
va any
|
||||
vb any
|
||||
)
|
||||
|
||||
for _, o := range s.filter.OrderBy() {
|
||||
x := inKeys(s.def.Group, o.Column)
|
||||
if x > -1 {
|
||||
va = ga.key[x]
|
||||
} else {
|
||||
x := inKeys(s.def.OutAttributes, o.Column)
|
||||
va = ga.agg.aggregates[x]
|
||||
}
|
||||
|
||||
x = inKeys(s.def.Group, o.Column)
|
||||
if x > -1 {
|
||||
vb = gb.key[x]
|
||||
} else {
|
||||
x := inKeys(s.def.OutAttributes, o.Column)
|
||||
vb = gb.agg.aggregates[x]
|
||||
}
|
||||
|
||||
cmp := compareValues(va, vb)
|
||||
if cmp != 0 {
|
||||
if o.Descending {
|
||||
return cmp > 0
|
||||
}
|
||||
return cmp < 0
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func inKeys(kk []AggregateAttr, ident string) int {
|
||||
for i, k := range kk {
|
||||
if k.Identifier == ident {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (xs *aggregate) initScanRow() (out *Row) {
|
||||
// base
|
||||
out = &Row{
|
||||
counters: make(map[string]uint),
|
||||
values: make(valueSet),
|
||||
}
|
||||
|
||||
// pre-populate with known attrs
|
||||
for _, attr := range append(xs.def.Group, xs.def.OutAttributes...) {
|
||||
out.values[attr.Identifier] = make([]any, 0, 2)
|
||||
out.counters[attr.Identifier] = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// aggregateGroupKeyWalker prepares a function which runs the provided function over all group keys
|
||||
//
|
||||
// This is required to handle multi-value attributes when used in group keys.
|
||||
//
|
||||
// Algorithm TL;DR
|
||||
//
|
||||
// We're going backwards in the slice of idents we need to handle (going the other way around)
|
||||
// should also work but I chose to do it like so.
|
||||
//
|
||||
// For every ident, keep track of the number of elements and the index of the last unhandled ident.
|
||||
//
|
||||
// For every iteration, collect all of the values pointed to by the unused index.
|
||||
// After the key is processed, increment the counter for the current ident.
|
||||
// If the counter exceeds the number of elements, reset the counter for the current ident and move
|
||||
// the ident pointer backwards (repeat if that ident's counter also exceeds the limit).
|
||||
//
|
||||
// When we find an indent which still has some items to process (counter doesn't exceed limit),
|
||||
// reset the ident pointer to the end of the slice and repeat the whole thing.
|
||||
func aggregateGroupKeyWalker(kk ...*ql.ASTNode) (out keyWalker, err error) {
|
||||
// @todo option to copy constants and idents
|
||||
runners, err := makeExprRunners(kk...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// We'll sort the idents to keep the output consistent; the order doesn't matter
|
||||
// but it will simplify testing.
|
||||
idents, hasConstants := keysFromExpr(kk...)
|
||||
sort.Strings(idents)
|
||||
|
||||
out = func(ctx context.Context, vg ValueGetter, run func(context.Context, groupKey, ValueGetter) error) error {
|
||||
// Edgecase for when all of the expressions return constant values
|
||||
if len(idents) == 0 {
|
||||
// This should be impossible but better safe then sorry
|
||||
if !hasConstants {
|
||||
return nil
|
||||
}
|
||||
|
||||
k, err := makeGroupKey(ctx, runners, vg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return run(ctx, k, vg)
|
||||
}
|
||||
|
||||
// For every value combination of idents used in agg. key expressions
|
||||
// construct a key and run the runner.
|
||||
|
||||
limits := vg.CountValues()
|
||||
ptr := len(idents) - 1
|
||||
counts := make(map[string]uint, len(limits))
|
||||
for k := range limits {
|
||||
counts[k] = 0
|
||||
}
|
||||
|
||||
handle := func() (err error) {
|
||||
aux := make(map[string]any, len(limits))
|
||||
|
||||
for k, i := range counts {
|
||||
aux[k], err = vg.GetValue(k, i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
kk, err := makeGroupKey(ctx, runners, aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return run(ctx, kk, vg)
|
||||
}
|
||||
|
||||
outer:
|
||||
for ptr >= 0 {
|
||||
handle()
|
||||
|
||||
counts[idents[ptr]]++
|
||||
for {
|
||||
// There are still values to process so we can skip the rest
|
||||
if counts[idents[ptr]] < limits[idents[ptr]] {
|
||||
continue outer
|
||||
}
|
||||
|
||||
// Reset the counter for the current ptr since we'll move back.
|
||||
// The outer loop will have this reset all of the counters after the current ptr.
|
||||
counts[idents[ptr]] = 0
|
||||
|
||||
ptr--
|
||||
if ptr < 0 {
|
||||
break outer
|
||||
}
|
||||
|
||||
counts[idents[ptr]]++
|
||||
|
||||
if counts[idents[ptr]] >= limits[idents[ptr]] {
|
||||
continue
|
||||
}
|
||||
|
||||
// We need to reset to the end so the next ident gets all of the values
|
||||
// that appear after it.
|
||||
ptr = len(idents) - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func makeExprRunners(kk ...*ql.ASTNode) (out []*runnerGval, err error) {
|
||||
out = make([]*runnerGval, len(kk))
|
||||
|
||||
for i, k := range kk {
|
||||
out[i], err = newRunnerGvalParsed(k)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func makeGroupKey(ctx context.Context, runners []*runnerGval, vals any) (gk groupKey, err error) {
|
||||
gk = make(groupKey, len(runners))
|
||||
for i, r := range runners {
|
||||
v, err := r.Eval(ctx, vals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gk[i] = v
|
||||
}
|
||||
return gk, nil
|
||||
}
|
||||
417
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/exec_join_left.go
generated
vendored
Normal file
417
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/exec_join_left.go
generated
vendored
Normal file
@@ -0,0 +1,417 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/tidwall/btree"
|
||||
)
|
||||
|
||||
type (
|
||||
// Considerations for optimizations
|
||||
// - Rework how values are stored internally.
|
||||
// Potentially rework the pipeline input/output interfaces, how ValueGetter/Setter work.
|
||||
// - Skip initial left row scan/sort when sorting not requested.
|
||||
// - Rework row struct to use slices instead of hashmaps.
|
||||
// With how values are now the performance gain is not that impactful and the
|
||||
// rework complexity is a bit too high for now.
|
||||
// - Use `.More` to filter rows by key.
|
||||
// - When data is provided in a satisfactory order, use that to pull data in chunks.
|
||||
joinLeft struct {
|
||||
def Join
|
||||
filter internalFilter
|
||||
|
||||
leftSource Iterator
|
||||
rightSource Iterator
|
||||
err error
|
||||
scanRow ValueGetter
|
||||
planned bool
|
||||
filtered bool
|
||||
|
||||
rowTester tester
|
||||
|
||||
// Index to keep track of related rows
|
||||
relIndex *relIndex
|
||||
|
||||
// Output placeholder for sorted rows
|
||||
// @todo consider a generic slice for cases when sorting is not needed.
|
||||
// This will probably save up on memory/time since we don't even need
|
||||
// to pull everything.
|
||||
outSorted *btree.Generic[ValueGetter]
|
||||
i int
|
||||
}
|
||||
)
|
||||
|
||||
func (xs *joinLeft) init(ctx context.Context, joinPredType Type) (err error) {
|
||||
xs.relIndex, err = newRelIndex(joinPredType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
xs.rowTester, err = prepareGenericRowTester(xs.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// @note careful here if you throw routines into the mix; see the NoLocks flag.
|
||||
// Enabling locks does have a performance impact so you might be better off by
|
||||
// constructing multiple of these but then you'll also need to complicate
|
||||
// the .Next methods a bit.
|
||||
xs.outSorted = btree.NewGenericOptions[ValueGetter](makeRowComparator(xs.filter.OrderBy()...), btree.Options{NoLocks: true})
|
||||
|
||||
return xs.applyPlan(ctx)
|
||||
}
|
||||
|
||||
func (xs *joinLeft) Next(ctx context.Context) (more bool) {
|
||||
xs.err = xs.applyPlan(ctx)
|
||||
if xs.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
more, xs.err = xs.next(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *joinLeft) More(limit uint, v ValueGetter) (err error) {
|
||||
xs.filter.cursor, err = filter.PagingCursorFrom(xs.filter.OrderBy(), v, xs.collectPrimaryAttributes(xs.def.OutAttributes)...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Redo row tester
|
||||
xs.rowTester, err = prepareGenericRowTester(xs.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Redo the state
|
||||
// @todo adjust based on aggregation plan; reuse buffered, etc.
|
||||
xs.relIndex.Clear()
|
||||
xs.outSorted = btree.NewGenericOptions[ValueGetter](makeRowComparator(xs.filter.OrderBy()...), btree.Options{NoLocks: true})
|
||||
xs.scanRow = nil
|
||||
xs.planned = false
|
||||
xs.i = 0
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *joinLeft) Err() error { return xs.err }
|
||||
|
||||
func (xs *joinLeft) Scan(s ValueSetter) (err error) {
|
||||
for k, cc := range xs.scanRow.CountValues() {
|
||||
for i := uint(0); i < cc; i++ {
|
||||
// @note internal row won't raise errors so we can safely omit them
|
||||
v, _ := xs.scanRow.GetValue(k, i)
|
||||
err = s.SetValue(k, i, v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *joinLeft) Close() (err error) {
|
||||
if xs == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cc := []io.Closer{
|
||||
xs.leftSource,
|
||||
xs.rightSource,
|
||||
}
|
||||
|
||||
for _, c := range cc {
|
||||
if c != nil {
|
||||
err = c.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *joinLeft) BackCursor(v ValueGetter) (pc *filter.PagingCursor, err error) {
|
||||
pc, err = filter.PagingCursorFrom(xs.filter.OrderBy(), v, xs.collectPrimaryAttributes(xs.def.OutAttributes)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pc.ROrder = true
|
||||
pc.LThen = xs.filter.OrderBy().Reversed()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *joinLeft) ForwardCursor(v ValueGetter) (pc *filter.PagingCursor, err error) {
|
||||
pc, err = filter.PagingCursorFrom(xs.filter.OrderBy(), v, xs.collectPrimaryAttributes(xs.def.OutAttributes)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Utility methods
|
||||
|
||||
// next prepares the next scan row based on the defined join plan
|
||||
func (xs *joinLeft) next(ctx context.Context) (more bool, err error) {
|
||||
if xs.limitExceeded() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
more, err = xs.pullNext(ctx)
|
||||
if !more || err != nil {
|
||||
return more, err
|
||||
}
|
||||
|
||||
xs.scanRow, _ = xs.outSorted.GetAt(xs.i)
|
||||
xs.i++
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (xs *joinLeft) limitExceeded() bool {
|
||||
return xs.filter.limit > 0 && xs.i >= int(xs.filter.limit)
|
||||
}
|
||||
|
||||
// pullNext pulls additional data so we can produce more
|
||||
//
|
||||
// This step may be omitted based on the join plan.
|
||||
func (xs *joinLeft) pullNext(ctx context.Context) (more bool, err error) {
|
||||
// Pull next chunk from source if not entirely buffered
|
||||
if xs.def.plan.partialScan {
|
||||
// @todo this case is currently not implemented so we're erroring it out
|
||||
return false, fmt.Errorf("partialScan join plan strategy not implemented")
|
||||
}
|
||||
|
||||
// Check if buffer has more
|
||||
if xs.i >= xs.outSorted.Len() {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// applyPlan runs plan specific logic to prepare the state
|
||||
func (xs *joinLeft) applyPlan(ctx context.Context) (err error) {
|
||||
if xs.planned || xs.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
xs.planned = true
|
||||
switch {
|
||||
case !xs.def.plan.partialScan:
|
||||
return xs.pullEntireSource(ctx)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// pullEntireSource pulls both sources into memory and indexes them for later use
|
||||
func (xs *joinLeft) pullEntireSource(ctx context.Context) (err error) {
|
||||
// This bit does the filtering so just mark it of as such here
|
||||
xs.filtered = true
|
||||
|
||||
// First the right source
|
||||
err = xs.pullEntireRightSource(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Next the left source
|
||||
err = xs.pullEntireLeftSource(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// pullEntireRightSource pulls and indexes all of the right bits
|
||||
func (xs *joinLeft) pullEntireRightSource(ctx context.Context) (err error) {
|
||||
for xs.rightSource.Next(ctx) {
|
||||
r := &Row{
|
||||
counters: make(map[string]uint),
|
||||
values: make(valueSet),
|
||||
}
|
||||
|
||||
err = xs.rightSource.Scan(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = xs.indexRightRow(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return xs.rightSource.Err()
|
||||
}
|
||||
|
||||
// pullEntireLeftSource pulls left bits and attempts to do as much of the joining
|
||||
// work on this stage
|
||||
func (xs *joinLeft) pullEntireLeftSource(ctx context.Context) (err error) {
|
||||
for xs.leftSource.Next(ctx) {
|
||||
l := &Row{
|
||||
counters: make(map[string]uint),
|
||||
values: make(valueSet),
|
||||
}
|
||||
|
||||
err = xs.leftSource.Scan(l)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = xs.joinRight(ctx, l)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return xs.leftSource.Err()
|
||||
}
|
||||
|
||||
// joinRight finds related right rows for the given left row and matches them up
|
||||
//
|
||||
// @note for sorting, we use a b-tree as it's self sorting.
|
||||
// Benchmarking shows that using a slice is negligibly faster if faster at all.
|
||||
func (xs *joinLeft) joinRight(ctx context.Context, left *Row) (err error) {
|
||||
bb, ok, err := xs.getRelatedBuffers(left)
|
||||
if err != nil || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, b := range bb {
|
||||
for _, right := range b.rows {
|
||||
// Merge the two
|
||||
xs.mergeRows(xs.def.OutAttributes, right, left, right)
|
||||
|
||||
// Assert if we want to keep
|
||||
k, err := xs.keep(ctx, right)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !k {
|
||||
continue
|
||||
}
|
||||
|
||||
xs.outSorted.Set(right)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getRelatedBuffers returns all of the right rows corresponding to the given left row
|
||||
func (xs *joinLeft) getRelatedBuffers(l *Row) (out []*relIndexBuffer, ok bool, err error) {
|
||||
var aux *relIndexBuffer
|
||||
for c := uint(0); c < l.CountValues()[xs.def.On.Left]; c++ {
|
||||
// @note internal Row struct never errors
|
||||
v, _ := l.GetValue(xs.def.On.Left, c)
|
||||
aux, ok = xs.relIndex.Get(v)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
out = append(out, aux)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// indexRightRow pushes the provided row onto the rel index
|
||||
// @todo consider moving most of this logic to the relIndex struct.
|
||||
func (xs *joinLeft) indexRightRow(r *Row) (err error) {
|
||||
for i := uint(0); i < r.CountValues()[xs.def.On.Right]; i++ {
|
||||
v, err := r.GetValue(xs.def.On.Right, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xs.relIndex.Add(v, r)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// keep checks if the row should be kept or discarded
|
||||
func (xs *joinLeft) keep(ctx context.Context, r *Row) (bool, error) {
|
||||
if xs.rowTester == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return xs.rowTester.Test(ctx, r)
|
||||
}
|
||||
|
||||
// collectPrimaryAttributes returns all of the attributes of the composited key
|
||||
//
|
||||
// For joins, all primary attributes from both of the sides should be in here
|
||||
// since that is what always uniquely identifies a joined row.
|
||||
//
|
||||
// @todo consider applying PK candidates and filter out some of these. I don't
|
||||
// think it'll provide much of a performance boost but worth a shot later on.
|
||||
func (xs *joinLeft) collectPrimaryAttributes(mm []AttributeMapping) (out []string) {
|
||||
out = make([]string, 0, 2)
|
||||
for _, m := range mm {
|
||||
if m.Properties().IsPrimary {
|
||||
out = append(out, m.Identifier())
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// mergeRows merges the left and right rows based on the provided attrs
|
||||
func (xs *joinLeft) mergeRows(attrs []AttributeMapping, out, left, right *Row) {
|
||||
var (
|
||||
identSide int
|
||||
srcIdent string
|
||||
)
|
||||
|
||||
for _, attr := range attrs {
|
||||
srcIdent = attr.Identifier()
|
||||
identSide = xs.identSide(srcIdent)
|
||||
|
||||
if identSide == -1 {
|
||||
xs.mergeValuesFrom(attr.Source(), attr.Identifier(), out, left)
|
||||
} else if identSide == 1 {
|
||||
xs.mergeValuesFrom(attr.Source(), attr.Identifier(), out, right)
|
||||
} else {
|
||||
xs.mergeValuesFrom(attr.Source(), attr.Identifier(), out, left, right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (xs *joinLeft) mergeValuesFrom(inIdent, outIdent string, out *Row, sources ...*Row) {
|
||||
var (
|
||||
aux any
|
||||
)
|
||||
for _, src := range sources {
|
||||
for c := uint(0); c < src.CountValues()[inIdent]; c++ {
|
||||
aux, _ = src.GetValue(inIdent, c)
|
||||
out.SetValue(outIdent, c, aux)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// identSide returns -1 if the ident belongs to the left source, 1 if it belongs
|
||||
// to the right side, and 0 if it's either.
|
||||
//
|
||||
// @todo consider adding an additional flag to identify what side it is on.
|
||||
// For now, this should be fine.
|
||||
func (xs *joinLeft) identSide(ident string) int {
|
||||
pp := strings.Split(ident, attributeNestingSeparator)
|
||||
if len(pp) > 1 {
|
||||
if pp[0] == xs.def.RelLeft {
|
||||
return -1
|
||||
} else if pp[0] == xs.def.RelRight {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
505
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/exec_link_left.go
generated
vendored
Normal file
505
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/exec_link_left.go
generated
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
linkLeft struct {
|
||||
def Link
|
||||
filter internalFilter
|
||||
|
||||
leftSource Iterator
|
||||
rightSource Iterator
|
||||
err error
|
||||
scanRow *Row
|
||||
planned bool
|
||||
filtered bool
|
||||
|
||||
rightSortAttrs []string
|
||||
|
||||
rowTester tester
|
||||
|
||||
// Buffer to keep track of pulled left rows
|
||||
leftRows []*Row
|
||||
relIndex *relIndex
|
||||
keepLeft bool
|
||||
leftIndex int
|
||||
rightIndex int
|
||||
|
||||
// Some helper fields for temporary data
|
||||
leftRow *Row
|
||||
rightRow *Row
|
||||
relScanBuffer *relIndexBuffer
|
||||
}
|
||||
)
|
||||
|
||||
func (xs *linkLeft) init(ctx context.Context, linkPredType Type, rightSort []string) (err error) {
|
||||
// @note the index is keeping track of the right source attributes so we can
|
||||
// simplify the sorting logic.
|
||||
xs.relIndex, err = newRelIndex(linkPredType, rightSort...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
xs.rightSortAttrs = rightSort
|
||||
|
||||
xs.rowTester, err = prepareGenericRowTester(xs.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return xs.applyPlan(ctx)
|
||||
}
|
||||
|
||||
func (xs *linkLeft) Next(ctx context.Context) (more bool) {
|
||||
xs.err = xs.applyPlan(ctx)
|
||||
if xs.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
more, xs.err = xs.next(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *linkLeft) More(limit uint, v ValueGetter) (err error) {
|
||||
xs.filter.cursor, err = xs.ForwardCursor(v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Redo row tester
|
||||
xs.rowTester, err = prepareGenericRowTester(xs.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Redo the state
|
||||
// @todo adjust based on aggregation plan; reuse buffered, etc.
|
||||
xs.relIndex.Clear()
|
||||
xs.leftRows = make([]*Row, 0, 128)
|
||||
xs.scanRow = nil
|
||||
xs.planned = false
|
||||
xs.keepLeft = false
|
||||
xs.leftIndex = 0
|
||||
xs.rightIndex = 0
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *linkLeft) Err() error { return xs.err }
|
||||
|
||||
func (xs *linkLeft) Scan(s ValueSetter) (err error) {
|
||||
for k, cc := range xs.scanRow.CountValues() {
|
||||
for i := uint(0); i < cc; i++ {
|
||||
// @note internal row won't raise errors so we can safely omit them
|
||||
v, _ := xs.scanRow.GetValue(k, i)
|
||||
err = s.SetValue(k, i, v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *linkLeft) Close() (err error) {
|
||||
if xs == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cc := []io.Closer{
|
||||
xs.leftSource,
|
||||
xs.rightSource,
|
||||
}
|
||||
|
||||
for _, c := range cc {
|
||||
if c != nil {
|
||||
err = c.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *linkLeft) BackCursor(v ValueGetter) (pc *filter.PagingCursor, err error) {
|
||||
g := &rowLink{
|
||||
a: xs.leftRow,
|
||||
b: v,
|
||||
}
|
||||
pc, err = filter.PagingCursorFrom(xs.filter.OrderBy(), g, xs.collectPrimaryAttributes()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pc.ROrder = true
|
||||
pc.LThen = xs.filter.OrderBy().Reversed()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *linkLeft) ForwardCursor(v ValueGetter) (pc *filter.PagingCursor, err error) {
|
||||
g := &rowLink{
|
||||
a: xs.leftRow,
|
||||
b: v,
|
||||
}
|
||||
pc, err = filter.PagingCursorFrom(xs.filter.OrderBy(), g, xs.collectPrimaryAttributes()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Utility methods
|
||||
|
||||
// next prepares the next scan row based on the defined link plan
|
||||
func (xs *linkLeft) next(ctx context.Context) (more bool, err error) {
|
||||
more, err = xs.pullNext(ctx)
|
||||
if !more || err != nil {
|
||||
return more, err
|
||||
}
|
||||
|
||||
return xs.nextBuffered()
|
||||
}
|
||||
|
||||
// pullNext pulls additional data so we can produce more
|
||||
//
|
||||
// This step may be omitted based on the join plan.
|
||||
func (xs *linkLeft) pullNext(ctx context.Context) (more bool, err error) {
|
||||
// Pull next chunk from source if not entirely buffered
|
||||
if xs.def.plan.partialScan {
|
||||
return false, fmt.Errorf("partialScan join plan strategy not implemented")
|
||||
}
|
||||
|
||||
// Check if buffer has more
|
||||
// We need to check for gt because the right bits may still be relevant
|
||||
if xs.leftIndex > len(xs.leftRows) {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// nextBuffered prepares the next scan row from the buffers
|
||||
func (xs *linkLeft) nextBuffered() (more bool, err error) {
|
||||
// keepLeft indicates if we should keep the left row and move onto the next
|
||||
// in the right buffers.
|
||||
//
|
||||
// If we're keeping it, take the next row from the other side, else take the
|
||||
// next left one and reset right counters.
|
||||
|
||||
var (
|
||||
ok bool
|
||||
)
|
||||
|
||||
for {
|
||||
if !xs.keepLeft {
|
||||
// Go for the next left row
|
||||
if xs.leftIndex >= len(xs.leftRows) {
|
||||
return
|
||||
}
|
||||
xs.leftRow = xs.leftRows[xs.leftIndex]
|
||||
xs.leftIndex++
|
||||
|
||||
// Go for the next right buffer
|
||||
xs.relScanBuffer, ok, err = xs.getRelatedBuffer(xs.leftRow)
|
||||
if !ok || err != nil {
|
||||
return
|
||||
}
|
||||
// if len(rel) > 1 {
|
||||
// // @todo implement this; not entirely sure how it should be so I'll block it for now
|
||||
// // @todo move this check futher up
|
||||
// return false, fmt.Errorf("multi-value link predicates not supported")
|
||||
// }
|
||||
// xs.relScanBuffer = rel[0]
|
||||
xs.rightIndex = 0
|
||||
|
||||
xs.scanRow = xs.leftRow
|
||||
xs.keepLeft = true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Related buffer done
|
||||
if xs.rightIndex >= len(xs.relScanBuffer.rows) {
|
||||
xs.keepLeft = false
|
||||
continue
|
||||
}
|
||||
|
||||
xs.rightRow = xs.relScanBuffer.rows[xs.rightIndex]
|
||||
xs.rightIndex++
|
||||
xs.scanRow = xs.rightRow
|
||||
|
||||
// Sys defined value to indicate the source
|
||||
// @todo consider using some wrap struct instead
|
||||
xs.scanRow.SetValue(LinkRefIdent, 0, xs.def.RelRight)
|
||||
break
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// getRelatedBuffer returns all of the right rows corresponding to the given left row
|
||||
func (xs *linkLeft) getRelatedBuffer(l *Row) (out *relIndexBuffer, ok bool, err error) {
|
||||
|
||||
// @todo mv link predicate attrs
|
||||
v, _ := l.GetValue(xs.def.On.Left, 0)
|
||||
out, ok = xs.relIndex.Get(v)
|
||||
return
|
||||
}
|
||||
|
||||
// applyPlan runs plan specific logic to prepare the state
|
||||
func (xs *linkLeft) applyPlan(ctx context.Context) (err error) {
|
||||
if xs.planned || xs.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
xs.planned = true
|
||||
switch {
|
||||
case !xs.def.plan.partialScan:
|
||||
return xs.pullEntireSource(ctx)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// pullEntireSource pulls both sources into memory and indexes them for later use
|
||||
func (xs *linkLeft) pullEntireSource(ctx context.Context) (err error) {
|
||||
// This bit does the filtering so just mark it of as such here
|
||||
xs.filtered = true
|
||||
|
||||
// First the right source
|
||||
err = xs.pullEntireRightSource(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Next the left source
|
||||
err = xs.pullEntireLeftSource(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort if needed
|
||||
return xs.sortLeftRows()
|
||||
}
|
||||
|
||||
// pullEntireRightSource pulls and indexes all of the right bits
|
||||
func (xs *linkLeft) pullEntireRightSource(ctx context.Context) (err error) {
|
||||
// Drain the source
|
||||
for xs.rightSource.Next(ctx) {
|
||||
r := &Row{
|
||||
counters: make(map[string]uint),
|
||||
values: make(valueSet),
|
||||
}
|
||||
|
||||
err = xs.rightSource.Scan(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = xs.indexRightRow(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return xs.rightSource.Err()
|
||||
}
|
||||
|
||||
// pullEntireLeftSource pulls and indexes all of the left bits
|
||||
func (xs *linkLeft) pullEntireLeftSource(ctx context.Context) (err error) {
|
||||
var (
|
||||
rel *relIndexBuffer
|
||||
ok bool
|
||||
)
|
||||
|
||||
// Drain the source
|
||||
for xs.leftSource.Next(ctx) {
|
||||
l := &Row{
|
||||
counters: make(map[string]uint),
|
||||
values: make(valueSet),
|
||||
}
|
||||
|
||||
err = xs.leftSource.Scan(l)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rel, ok, err = xs.getRelatedBuffer(l)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
k, err := xs.keep(ctx, l, rel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !k {
|
||||
continue
|
||||
}
|
||||
|
||||
xs.leftRows = append(xs.leftRows, l)
|
||||
}
|
||||
return xs.leftSource.Err()
|
||||
}
|
||||
|
||||
// indexRightRow pushes the provided row onto the rel index
|
||||
// @todo consider moving most of this logic to the relIndex struct.
|
||||
func (xs *linkLeft) indexRightRow(r *Row) (err error) {
|
||||
// @todo mv link predicate attrs; should be prevented higher up for now
|
||||
v, err := r.GetValue(xs.def.On.Right, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xs.relIndex.Add(v, r)
|
||||
return
|
||||
}
|
||||
|
||||
// sortLeftRows sorts the left rows into the correct order
|
||||
//
|
||||
// Algorithm outline:
|
||||
// Compare the two left rows based on the defined sort order.
|
||||
// If the two left rows match up, or we're primarily sorting using right rows,
|
||||
// check the min/max stats values produced by the index buffer.
|
||||
//
|
||||
// If some min/max of a chunk is greater/lesser then the other, no row in the other
|
||||
// can appear before/after the prior.
|
||||
func (xs *linkLeft) sortLeftRows() (err error) {
|
||||
var (
|
||||
leftRelBufferA *relIndexBuffer
|
||||
leftRelBufferB *relIndexBuffer
|
||||
|
||||
a, b any
|
||||
)
|
||||
|
||||
// Use stable sort just so we don't needlesly messup the initial order if we decide
|
||||
// to preserve sorts produced by further steps.
|
||||
sort.SliceStable(xs.leftRows, func(i, j int) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, s := range xs.filter.OrderBy() {
|
||||
// Prepare the data
|
||||
leftRowA := xs.leftRows[i]
|
||||
leftRowB := xs.leftRows[j]
|
||||
|
||||
leftRowA = convertStringToIntForColumn(s.Column, leftRowA)
|
||||
leftRowB = convertStringToIntForColumn(s.Column, leftRowB)
|
||||
|
||||
leftRelBufferA, _, err = xs.getRelatedBuffer(leftRowA)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
leftRelBufferB, _, err = xs.getRelatedBuffer(leftRowB)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !xs.isRightSortAttr(s.Column) {
|
||||
// This bit here orders based on the left attributes
|
||||
less, skip := evalCmpResult(compareGetters(leftRowA, leftRowB, leftRowA.counters, leftRowB.counters, s.Column), s)
|
||||
if !skip {
|
||||
return less
|
||||
}
|
||||
} else {
|
||||
// This bit here orders based on the right attributes
|
||||
// Check the stats of the buffer; make sure to adjust based on direction
|
||||
|
||||
// Use chunk's values
|
||||
if !s.Descending {
|
||||
a = leftRelBufferA.min[s.Column]
|
||||
b = leftRelBufferB.min[s.Column]
|
||||
} else {
|
||||
a = leftRelBufferA.max[s.Column]
|
||||
b = leftRelBufferB.max[s.Column]
|
||||
}
|
||||
|
||||
less, skip := evalCmpResult(compareValues(a, b), s)
|
||||
if !skip {
|
||||
return less
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (xs *linkLeft) isRightSortAttr(ident string) bool {
|
||||
for _, i := range xs.rightSortAttrs {
|
||||
if i == ident {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// keep checks if the row should be kept or discarded
|
||||
//
|
||||
// Link's keep is a bit more complicated and it looks at the related buffer as well.
|
||||
func (xs *linkLeft) keep(ctx context.Context, left *Row, buffer *relIndexBuffer) (keep bool, err error) {
|
||||
// If no buffer, we won't keep -- left inner join like behavior
|
||||
if buffer == nil {
|
||||
return false, nil
|
||||
}
|
||||
// No tester include all ok rows
|
||||
if xs.rowTester == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ch := &rowLink{a: left}
|
||||
for _, ch.b = range buffer.rows {
|
||||
k, err := xs.rowTester.Test(ctx, ch)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !k {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (xs *linkLeft) collectPrimaryAttributes() (out []string) {
|
||||
out = make([]string, 0, 2)
|
||||
for _, m := range xs.def.OutLeftAttributes {
|
||||
if m.Properties().IsPrimary {
|
||||
out = append(out, m.Identifier())
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range xs.def.OutRightAttributes {
|
||||
if m.Properties().IsPrimary {
|
||||
out = append(out, m.Identifier())
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// convertStringToIntForColumn converts the string value that have numeric data in the specified column
|
||||
// and updates the Row value datatype to int, if the conversion is successful
|
||||
func convertStringToIntForColumn(column string, row *Row) *Row {
|
||||
if strValue, ok := row.values[column][0].(string); ok {
|
||||
if rowVal, err := strconv.Atoi(strValue); err == nil {
|
||||
row.values[column][0] = rowVal
|
||||
}
|
||||
}
|
||||
return row
|
||||
}
|
||||
189
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/filter.go
generated
vendored
Normal file
189
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/filter.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/ql"
|
||||
)
|
||||
|
||||
type (
|
||||
// internalFilter is a wrap to simplify interactions for cases where we might
|
||||
// want to interact with the received filters; i.e. when optimizing pipeline steps
|
||||
internalFilter struct {
|
||||
constraints map[string][]any
|
||||
stateConstraints map[string]filter.State
|
||||
metaConstraints map[string]any
|
||||
expression string
|
||||
expParsed *ql.ASTNode
|
||||
orderBy filter.SortExprSet
|
||||
limit uint
|
||||
cursor *filter.PagingCursor
|
||||
}
|
||||
|
||||
parsedFilter interface {
|
||||
ExpressionParsed() *ql.ASTNode
|
||||
}
|
||||
)
|
||||
|
||||
func (f internalFilter) Constraints() map[string][]any { return f.constraints }
|
||||
func (f internalFilter) StateConstraints() map[string]filter.State { return f.stateConstraints }
|
||||
func (f internalFilter) MetaConstraints() map[string]any { return f.metaConstraints }
|
||||
func (f internalFilter) Expression() string { return f.expression }
|
||||
func (f internalFilter) ExpressionParsed() *ql.ASTNode { return f.expParsed }
|
||||
func (f internalFilter) OrderBy() filter.SortExprSet { return f.orderBy }
|
||||
func (f internalFilter) Limit() uint { return f.limit }
|
||||
func (f internalFilter) Cursor() *filter.PagingCursor { return f.cursor }
|
||||
|
||||
// toInternalFilter converts filter.Filter to internalFilter for easier manipulation
|
||||
//
|
||||
// nil filters are returned as zeroed-out internalFilters.
|
||||
func toInternalFilter(f filter.Filter) (out internalFilter, err error) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Base
|
||||
out = internalFilter{
|
||||
constraints: f.Constraints(),
|
||||
stateConstraints: f.StateConstraints(),
|
||||
expression: f.Expression(),
|
||||
orderBy: f.OrderBy(),
|
||||
limit: f.Limit(),
|
||||
cursor: f.Cursor(),
|
||||
}
|
||||
|
||||
pf, ok := f.(parsedFilter)
|
||||
if ok {
|
||||
out.expParsed = pf.ExpressionParsed()
|
||||
|
||||
// In case the filter was already provided, we need to make sure the idents
|
||||
// are wrapped
|
||||
out.expParsed.Traverse(func(a *ql.ASTNode) (bool, *ql.ASTNode, error) {
|
||||
a.Symbol = wrapNestedGvalIdent(a.Symbol)
|
||||
return true, a, nil
|
||||
})
|
||||
}
|
||||
if out.expParsed != nil {
|
||||
// We can't mix the two so just clear it out to avoid confusion
|
||||
out.expression = ""
|
||||
}
|
||||
|
||||
// Parse expression for later use
|
||||
if out.expression != "" {
|
||||
out.expParsed, err = newQlParser().Parse(out.expression)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func FilterFromExpr(n *ql.ASTNode) internalFilter {
|
||||
// @todo consider adding string expr for consistency
|
||||
return internalFilter{
|
||||
expParsed: n,
|
||||
}
|
||||
}
|
||||
|
||||
// MergeFilters returns a new filter based on a overwritten by b
|
||||
func (a internalFilter) MergeFilters(b filter.Filter) (c internalFilter, err error) {
|
||||
// In case we got a generic b filter, convert it to the internal one for easier handling
|
||||
aux, ok := b.(internalFilter)
|
||||
if !ok {
|
||||
var err error
|
||||
aux, err = toInternalFilter(b)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
}
|
||||
|
||||
return a.mergeFilters(aux), nil
|
||||
}
|
||||
|
||||
func (a internalFilter) mergeFilters(b internalFilter) (c internalFilter) {
|
||||
|
||||
c = a
|
||||
|
||||
// expression
|
||||
if b.expParsed != nil {
|
||||
if c.expParsed == nil {
|
||||
c.expParsed = b.expParsed
|
||||
c.expression = b.expression
|
||||
} else {
|
||||
parsedA := a.expParsed
|
||||
rawA := a.expression
|
||||
if parsedA.Ref != "group" {
|
||||
parsedA = &ql.ASTNode{
|
||||
Ref: "group",
|
||||
Args: []*ql.ASTNode{
|
||||
parsedA,
|
||||
},
|
||||
}
|
||||
rawA = fmt.Sprintf("(%s)", rawA)
|
||||
}
|
||||
|
||||
parsedB := b.expParsed
|
||||
rawB := b.expression
|
||||
if parsedB.Ref != "group" {
|
||||
parsedB = &ql.ASTNode{
|
||||
Ref: "group",
|
||||
Args: []*ql.ASTNode{
|
||||
parsedB,
|
||||
},
|
||||
}
|
||||
rawB = fmt.Sprintf("(%s)", rawB)
|
||||
}
|
||||
|
||||
c.expParsed = &ql.ASTNode{
|
||||
Ref: "and",
|
||||
Args: ql.ASTNodeSet{parsedA, parsedB},
|
||||
}
|
||||
c.expression = fmt.Sprintf("%s && %s", rawA, rawB)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// constraints
|
||||
if b.constraints != nil {
|
||||
if c.constraints == nil {
|
||||
c.constraints = make(map[string][]any)
|
||||
}
|
||||
|
||||
for k, v := range b.constraints {
|
||||
c.constraints[k] = append(c.constraints[k], v...)
|
||||
}
|
||||
}
|
||||
|
||||
// state constraints
|
||||
if b.stateConstraints != nil {
|
||||
if c.stateConstraints == nil {
|
||||
c.stateConstraints = make(map[string]filter.State)
|
||||
}
|
||||
for k, v := range b.stateConstraints {
|
||||
c.stateConstraints[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// order by
|
||||
if b.orderBy != nil {
|
||||
c.orderBy = append(c.orderBy, b.orderBy...)
|
||||
}
|
||||
|
||||
// cursor
|
||||
// always use the latest paging cursor
|
||||
c.cursor = b.cursor
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// empty reports if the filter is completely empty or not
|
||||
func (f internalFilter) empty() bool {
|
||||
return f.constraints == nil &&
|
||||
f.stateConstraints == nil &&
|
||||
f.expression == "" &&
|
||||
f.orderBy == nil &&
|
||||
f.limit == 0 &&
|
||||
f.cursor == nil
|
||||
}
|
||||
212
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/inmem_buffer.go
generated
vendored
Normal file
212
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/inmem_buffer.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
// inmemBuffer isa simple DAL buffer which holds all of the data in memory
|
||||
//
|
||||
// This buffer should be used for small datasets
|
||||
inmemBuffer struct {
|
||||
rows []ValueGetter
|
||||
ctrs []map[string]uint
|
||||
|
||||
i int
|
||||
cap int
|
||||
closed bool
|
||||
|
||||
more bool
|
||||
err error
|
||||
|
||||
sort filter.SortExprSet
|
||||
}
|
||||
)
|
||||
|
||||
// InMemoryBuffer initializes a new DAL buffer where the data is kept in memory
|
||||
func InMemoryBuffer() *inmemBuffer {
|
||||
return &inmemBuffer{
|
||||
// @note we'll buffer the value counters along side the actual value counter
|
||||
// because constant hashmap initialization is quite memory intensive and introduces
|
||||
// a bit of a bottleneck.
|
||||
//
|
||||
// @todo investigate if this can be optimized at a lower level
|
||||
rows: make([]ValueGetter, 0, 100),
|
||||
ctrs: make([]map[string]uint, 0, 100),
|
||||
|
||||
// i starts off as -1 so we don't need an extra state flag when doing the first Next
|
||||
i: -1,
|
||||
more: true,
|
||||
}
|
||||
}
|
||||
|
||||
// InMemoryBufferWith returns a new buffer with the given value getters
|
||||
func InMemoryBufferWith(ctx context.Context, vv ...ValueGetter) (Buffer, error) {
|
||||
b := InMemoryBuffer()
|
||||
for _, v := range vv {
|
||||
if err := b.Add(ctx, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) InOrder(ss ...*filter.SortExpr) (err error) {
|
||||
b.sort = ss
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Single makes the buffer only keep one element at the time
|
||||
// @todo make this option convert the buffer into a circular buffer
|
||||
func (b *inmemBuffer) Single() {
|
||||
b.cap = 1
|
||||
b.rows = make([]ValueGetter, 1)
|
||||
b.ctrs = make([]map[string]uint, 1)
|
||||
b.i = -1
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Seek(_ context.Context, i int) (err error) {
|
||||
// we go one level further and start at -1
|
||||
b.i = i - 1
|
||||
|
||||
b.more = i < b.Len()
|
||||
return
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) isSingle() bool {
|
||||
return b.cap == 1
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) isSorted() bool {
|
||||
return len(b.sort) > 0
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Add(ctx context.Context, v ValueGetter) (err error) {
|
||||
if b.i != -1 && b.isSorted() {
|
||||
return fmt.Errorf("cannot buffer items after an access occurred and the buffer is ordered")
|
||||
}
|
||||
|
||||
if b.isSingle() {
|
||||
b.rows[0] = v
|
||||
b.ctrs[0] = v.CountValues()
|
||||
} else {
|
||||
b.rows = append(b.rows, v)
|
||||
b.ctrs = append(b.ctrs, v.CountValues())
|
||||
}
|
||||
|
||||
b.more = true
|
||||
return
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Next(ctx context.Context) bool {
|
||||
if !b.more {
|
||||
return false
|
||||
}
|
||||
if b.closed {
|
||||
panic("cannot call Next on closed buffer")
|
||||
}
|
||||
|
||||
// @todo use something like a b-tree to sort while inserting
|
||||
if b.i == -1 && len(b.sort) > 0 {
|
||||
sort.Sort(b)
|
||||
}
|
||||
|
||||
b.i++
|
||||
if b.isSingle() {
|
||||
b.i = b.i % b.cap
|
||||
}
|
||||
|
||||
b.more = !b.isSingle() && b.i+1 < b.Len()
|
||||
|
||||
return b.i < b.Len()
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) More(uint, ValueGetter) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Err() error {
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Scan(s ValueSetter) (err error) {
|
||||
if b.closed {
|
||||
panic("cannot call Scan on closed buffer")
|
||||
}
|
||||
|
||||
r, ctrs, err := b.rowAt(b.i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var v any
|
||||
for name, count := range ctrs {
|
||||
for i := uint(0); i < count; i++ {
|
||||
v, err = r.GetValue(name, i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = s.SetValue(name, i, v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// @todo should this get a context for things like IO ops
|
||||
func (b *inmemBuffer) Close() error {
|
||||
b.ctrs = nil
|
||||
b.rows = nil
|
||||
b.closed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) BackCursor(ValueGetter) (*filter.PagingCursor, error) {
|
||||
return nil, fmt.Errorf("not supported")
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) ForwardCursor(ValueGetter) (*filter.PagingCursor, error) {
|
||||
return nil, fmt.Errorf("not supported")
|
||||
}
|
||||
|
||||
// rowAt returns the row at a given index
|
||||
func (b *inmemBuffer) rowAt(i int) (ValueGetter, map[string]uint, error) {
|
||||
if i >= b.Len() {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
return b.rows[i], b.ctrs[i], nil
|
||||
}
|
||||
|
||||
// sort.Interface methods
|
||||
|
||||
func (b *inmemBuffer) Len() int {
|
||||
return len(b.rows)
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Less(i, j int) bool {
|
||||
ra, _, err := b.rowAt(i)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rb, _, err := b.rowAt(j)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return makeRowComparator(b.sort...)(ra, rb)
|
||||
}
|
||||
|
||||
func (b *inmemBuffer) Swap(i, j int) {
|
||||
b.rows[i], b.rows[j] = b.rows[j], b.rows[i]
|
||||
b.ctrs[i], b.ctrs[j] = b.ctrs[j], b.ctrs[i]
|
||||
}
|
||||
163
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/issues.go
generated
vendored
Normal file
163
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/issues.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package dal
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
type (
|
||||
Issue struct {
|
||||
err error
|
||||
Issue string `json:"issue,omitempty"`
|
||||
|
||||
Kind issueKind `json:"kind,omitempty"`
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
}
|
||||
issueSet []Issue
|
||||
|
||||
issueHelper struct {
|
||||
// these two will be used to help clear out unneeded errors
|
||||
connections []uint64
|
||||
models []uint64
|
||||
|
||||
connectionIssues dalIssueIndex
|
||||
modelIssues dalIssueIndex
|
||||
}
|
||||
|
||||
issueKind string
|
||||
dalIssueIndex map[uint64]issueSet
|
||||
)
|
||||
|
||||
const (
|
||||
connectionIssue issueKind = "connection"
|
||||
modelIssue issueKind = "model"
|
||||
)
|
||||
|
||||
func newIssueHelper() *issueHelper {
|
||||
return &issueHelper{
|
||||
connectionIssues: make(dalIssueIndex),
|
||||
modelIssues: make(dalIssueIndex),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *service) SearchConnectionIssues(connectionID uint64) (out []Issue) {
|
||||
return svc.connectionIssues[connectionID]
|
||||
}
|
||||
|
||||
func (svc *service) SearchModelIssues(resourceID uint64) (out []Issue) {
|
||||
return svc.modelIssues[resourceID]
|
||||
}
|
||||
|
||||
func (svc *service) SearchResourceIssues(resourceType, resource string) (out []Issue) {
|
||||
var m *Model
|
||||
for _, ax := range svc.models {
|
||||
m = ax.FindByResourceIdent(resourceType, resource)
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
return svc.modelIssues[m.ResourceID]
|
||||
}
|
||||
|
||||
func (svc *service) hasConnectionIssues(connectionID uint64) bool {
|
||||
return len(svc.SearchConnectionIssues(connectionID)) > 0
|
||||
}
|
||||
|
||||
func (svc *service) hasModelIssues(modelID uint64) bool {
|
||||
return len(svc.SearchModelIssues(modelID)) > 0
|
||||
}
|
||||
|
||||
func (svc *service) updateIssues(issues *issueHelper) {
|
||||
for _, connectionID := range issues.connections {
|
||||
delete(svc.connectionIssues, connectionID)
|
||||
}
|
||||
for connectionID, issues := range issues.connectionIssues {
|
||||
svc.connectionIssues[connectionID] = issues
|
||||
}
|
||||
|
||||
for _, modelID := range issues.models {
|
||||
delete(svc.modelIssues, modelID)
|
||||
}
|
||||
for modelID, issues := range issues.modelIssues {
|
||||
svc.modelIssues[modelID] = issues
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *service) clearModelIssues() {
|
||||
svc.modelIssues = make(dalIssueIndex)
|
||||
}
|
||||
|
||||
func (rd *issueHelper) addConnection(connectionID uint64) *issueHelper {
|
||||
rd.connections = append(rd.connections, connectionID)
|
||||
return rd
|
||||
}
|
||||
|
||||
func (rd *issueHelper) addModel(modelID uint64) *issueHelper {
|
||||
rd.models = append(rd.models, modelID)
|
||||
return rd
|
||||
}
|
||||
|
||||
func (rd *issueHelper) addConnectionIssue(connectionID uint64, i Issue) {
|
||||
i.Kind = connectionIssue
|
||||
i.Issue = i.err.Error()
|
||||
rd.connectionIssues[connectionID] = append(rd.connectionIssues[connectionID], i)
|
||||
}
|
||||
|
||||
func (rd *issueHelper) addModelIssue(resourceID uint64, i Issue) {
|
||||
i.Kind = modelIssue
|
||||
i.Issue = i.err.Error()
|
||||
rd.modelIssues[resourceID] = append(rd.modelIssues[resourceID], i)
|
||||
}
|
||||
|
||||
func (rd *issueHelper) hasConnectionIssues() bool {
|
||||
return len(rd.connectionIssues) > 0
|
||||
}
|
||||
|
||||
func (rd *issueHelper) hasModelIssues() bool {
|
||||
return len(rd.modelIssues) > 0
|
||||
}
|
||||
|
||||
func (a *issueHelper) mergeWith(b *issueHelper) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for connectionID, issues := range b.connectionIssues {
|
||||
a.connectionIssues[connectionID] = append(a.connectionIssues[connectionID], issues...)
|
||||
}
|
||||
|
||||
for modelID, issues := range b.modelIssues {
|
||||
a.modelIssues[modelID] = append(a.modelIssues[modelID], issues...)
|
||||
}
|
||||
}
|
||||
|
||||
// Op check utils
|
||||
func (svc *service) canOpData(ref ModelRef) (err error) {
|
||||
if svc.hasConnectionIssues(ref.ConnectionID) {
|
||||
for _, i := range svc.connectionIssues[ref.ConnectionID] {
|
||||
svc.logger.Debug(
|
||||
"can not perform data operation due to connection issue: "+i.err.Error(),
|
||||
zap.Any("ref", ref.ResourceID),
|
||||
)
|
||||
}
|
||||
|
||||
return errRecordOpProblematicConnection(ref.ConnectionID)
|
||||
}
|
||||
|
||||
var mod = svc.FindModelByRef(ref)
|
||||
if mod == nil {
|
||||
return errModelNotFound(ref.ResourceID)
|
||||
}
|
||||
|
||||
if svc.hasModelIssues(mod.ResourceID) {
|
||||
for _, i := range svc.modelIssues[mod.ResourceID] {
|
||||
svc.logger.Debug(
|
||||
"can not perform data operation due to model issue: "+i.err.Error(),
|
||||
zap.Any("ref", ref.ResourceID),
|
||||
)
|
||||
}
|
||||
|
||||
return errRecordOpProblematicModel(mod.ResourceID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
119
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/iterator.go
generated
vendored
Normal file
119
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/iterator.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
// Iterator provides an interface for loading data from the underlying store
|
||||
Iterator interface {
|
||||
Next(ctx context.Context) bool
|
||||
More(uint, ValueGetter) error
|
||||
Err() error
|
||||
Scan(ValueSetter) error
|
||||
Close() error
|
||||
|
||||
BackCursor(ValueGetter) (*filter.PagingCursor, error)
|
||||
ForwardCursor(ValueGetter) (*filter.PagingCursor, error)
|
||||
|
||||
// // -1 means unknown
|
||||
// Total() int
|
||||
// Cursor() any
|
||||
// // ... do we need anything else here?
|
||||
}
|
||||
|
||||
iterator interface {
|
||||
Preload(context.Context, uint, *filter.PagingCursor) error
|
||||
Sorting() filter.SortExprSet
|
||||
}
|
||||
)
|
||||
|
||||
// IteratorEncodeJSON helper function that encodes each item from the iterator as JSON
|
||||
// and writes it to th given io.Writer.
|
||||
//
|
||||
// target initialization function is intentionally used to avoid use of reflection
|
||||
func IteratorEncodeJSON(ctx context.Context, w io.Writer, iter Iterator, initTarget func() ValueSetter) (err error) {
|
||||
var (
|
||||
target ValueSetter
|
||||
firstOut = false
|
||||
)
|
||||
|
||||
for iter.Next(ctx) {
|
||||
if err = iter.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if firstOut {
|
||||
if _, err = w.Write([]byte(`,`)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
firstOut = true
|
||||
|
||||
target = initTarget()
|
||||
|
||||
if err = iter.Scan(target); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(target)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// PreLoadCursor into iterator and check it exist then return the cursor
|
||||
// @todo this should be move to the Iterator
|
||||
func PreLoadCursor(ctx context.Context, iter Iterator, limit uint, reverse bool, r ValueGetter, fx func(Iterator) (bool, error)) (out *filter.PagingCursor, err error) {
|
||||
makeCursor := func() (*filter.PagingCursor, error) {
|
||||
if reverse {
|
||||
return iter.BackCursor(r)
|
||||
} else {
|
||||
return iter.ForwardCursor(r)
|
||||
}
|
||||
}
|
||||
|
||||
out, err = makeCursor()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = iter.(iterator).Preload(ctx, limit, out)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for {
|
||||
if !iter.Next(ctx) {
|
||||
out = nil
|
||||
return
|
||||
}
|
||||
|
||||
ok, err := fx(iter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ok {
|
||||
return out, err
|
||||
|
||||
// // @todo Skip the things we don't have access to; could cause some edge cases so probably not
|
||||
// // It adds some performance since we skip unneeded stuff but could some records change in the mean time?
|
||||
// // return makeCursor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorSorting return iterator sorting
|
||||
// @todo this should be move to the Iterator
|
||||
func IteratorSorting(iter Iterator) filter.SortExprSet {
|
||||
return iter.(iterator).Sorting()
|
||||
}
|
||||
578
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/model.go
generated
vendored
Normal file
578
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/model.go
generated
vendored
Normal file
@@ -0,0 +1,578 @@
|
||||
package dal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/handle"
|
||||
"github.com/modern-go/reflect2"
|
||||
)
|
||||
|
||||
type (
|
||||
// ModelRef is used to retrieve a model from the DAL based on given params
|
||||
ModelRef struct {
|
||||
ConnectionID uint64
|
||||
|
||||
ResourceID uint64
|
||||
|
||||
ResourceType string
|
||||
Resource string
|
||||
|
||||
Refs map[string]any
|
||||
}
|
||||
|
||||
// Model describes the underlying data and its shape
|
||||
Model struct {
|
||||
ConnectionID uint64 `json:"connectionID"`
|
||||
Ident string `json:"ident"`
|
||||
Label string `json:"label"`
|
||||
|
||||
Resource string `json:"resource"`
|
||||
ResourceID uint64 `json:"resourceID"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
|
||||
// Refs is an arbitrary map to identify a model
|
||||
// @todo consider reworking this; I'm not the biggest fan
|
||||
Refs map[string]any `json:"refs"`
|
||||
|
||||
SensitivityLevelID uint64 `json:"sensitivityLevelID"`
|
||||
|
||||
Attributes AttributeSet `json:"attributes"`
|
||||
|
||||
Constraints map[string][]any `json:"constraints"`
|
||||
Indexes IndexSet `json:"indexes"`
|
||||
}
|
||||
|
||||
ModelSet []*Model
|
||||
|
||||
// Attribute describes a specific value of the dataset
|
||||
Attribute struct {
|
||||
Ident string `json:"ident"`
|
||||
Label string `json:"label"`
|
||||
|
||||
SensitivityLevelID uint64 `json:"sensitivityLevelID"`
|
||||
|
||||
MultiValue bool `json:"multiValue"`
|
||||
|
||||
PrimaryKey bool `json:"primaryKey"`
|
||||
|
||||
// If attribute has SoftDeleteFlag=true we use it
|
||||
// when filtering out deleted items
|
||||
SoftDeleteFlag bool `json:"softDeleteFlag"`
|
||||
|
||||
// System indicates the attribute was defined by the system
|
||||
System bool `json:"system"`
|
||||
|
||||
// Is attribute sortable?
|
||||
// Note: all primary keys are sortable
|
||||
Sortable bool `json:"sortable"`
|
||||
|
||||
// Can attribute be used in query expression?
|
||||
Filterable bool `json:"filterable"`
|
||||
|
||||
// Store describes the strategy the underlying storage system should
|
||||
// apply to the underlying value
|
||||
Store Codec `json:"store"`
|
||||
|
||||
// Type describes what the value represents and how it should be
|
||||
// encoded/decoded
|
||||
Type Type `json:"type"`
|
||||
}
|
||||
|
||||
// auxAttribute is a helper struct used for marshaling/unmarshaling
|
||||
//
|
||||
// This is required since some fields are interfaces
|
||||
auxAttribute struct {
|
||||
Ident string `json:"ident"`
|
||||
Label string `json:"label"`
|
||||
SensitivityLevelID uint64 `json:"sensitivityLevelID"`
|
||||
MultiValue bool `json:"multiValue"`
|
||||
PrimaryKey bool `json:"primaryKey"`
|
||||
SoftDeleteFlag bool `json:"softDeleteFlag"`
|
||||
System bool `json:"system"`
|
||||
Sortable bool `json:"sortable"`
|
||||
Filterable bool `json:"filterable"`
|
||||
|
||||
Store *auxAttributeStore `json:"store"`
|
||||
Type *auxAttributeType `json:"type"`
|
||||
}
|
||||
|
||||
// auxAttributeStore is a helper struct used for marshaling/unmarshaling
|
||||
//
|
||||
// This is required since some fields are interfaces
|
||||
auxAttributeStore struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
Plain *CodecPlain `json:"plain,omitempty"`
|
||||
RecordValueSetJSON *CodecRecordValueSetJSON `json:"recordValueSetJSON,omitempty"`
|
||||
Alias *CodecAlias `json:"alias,omitempty"`
|
||||
}
|
||||
|
||||
// auxAttributeType is a helper struct used for marshaling/unmarshaling
|
||||
//
|
||||
// This is required since some fields are interfaces
|
||||
auxAttributeType struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
ID *TypeID `json:"id,omitempty"`
|
||||
Ref *TypeRef `json:"ref,omitempty"`
|
||||
Timestamp *TypeTimestamp `json:"timestamp,omitempty"`
|
||||
Time *TypeTime `json:"time,omitempty"`
|
||||
Date *TypeDate `json:"date,omitempty"`
|
||||
Number *TypeNumber `json:"number,omitempty"`
|
||||
Text *TypeText `json:"text,omitempty"`
|
||||
Boolean *TypeBoolean `json:"boolean,omitempty"`
|
||||
Enum *TypeEnum `json:"enum,omitempty"`
|
||||
Geometry *TypeGeometry `json:"geometry,omitempty"`
|
||||
JSON *TypeJSON `json:"jSON,omitempty"`
|
||||
Blob *TypeBlob `json:"blob,omitempty"`
|
||||
UUID *TypeUUID `json:"uuid,omitempty"`
|
||||
}
|
||||
|
||||
// auxStoreCodec is a helper struct used for marshaling/unmarshaling
|
||||
//
|
||||
// This is required since some fields are interfaces
|
||||
auxStoreCodec struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
CodecPlain *CodecPlain `json:"codecPlain"`
|
||||
CodecRecordValueSetJSON *CodecRecordValueSetJSON `json:"codecRecordValueSetJSON"`
|
||||
CodecAlias *CodecAlias `json:"codecAlias"`
|
||||
}
|
||||
|
||||
AttributeSet []*Attribute
|
||||
|
||||
Index struct {
|
||||
Ident string
|
||||
Type string
|
||||
Unique bool
|
||||
|
||||
Fields []*IndexField
|
||||
|
||||
Predicate string
|
||||
}
|
||||
|
||||
IndexField struct {
|
||||
AttributeIdent string
|
||||
Modifiers []IndexFieldModifier
|
||||
Sort IndexFieldSort
|
||||
Nulls IndexFieldNulls
|
||||
}
|
||||
|
||||
IndexSet []*Index
|
||||
|
||||
IndexFieldModifier string
|
||||
IndexFieldSort int
|
||||
IndexFieldNulls int
|
||||
)
|
||||
|
||||
const (
|
||||
IndexFieldSortDesc IndexFieldSort = -1
|
||||
IndexFieldSortAsc IndexFieldSort = 1
|
||||
|
||||
IndexFieldNullsLast IndexFieldNulls = -1
|
||||
IndexFieldNullsFirst IndexFieldNulls = 1
|
||||
|
||||
IndexFieldModifierLower = "LOWERCASE"
|
||||
)
|
||||
|
||||
func PrimaryAttribute(ident string, codec Codec) *Attribute {
|
||||
out := FullAttribute(ident, TypeID{}, codec)
|
||||
out.Type = &TypeID{}
|
||||
out.PrimaryKey = true
|
||||
return out
|
||||
}
|
||||
|
||||
func FullAttribute(ident string, at Type, codec Codec) *Attribute {
|
||||
return &Attribute{
|
||||
Ident: ident,
|
||||
Label: ident,
|
||||
Sortable: true,
|
||||
Filterable: true,
|
||||
Store: codec,
|
||||
Type: at,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Attribute) WithSoftDelete() *Attribute {
|
||||
a.SoftDeleteFlag = true
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Attribute) WithMultiValue() *Attribute {
|
||||
a.MultiValue = true
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Attribute) StoreIdent() string {
|
||||
switch s := a.Store.(type) {
|
||||
case *CodecRecordValueSetJSON:
|
||||
return s.Ident
|
||||
|
||||
case *CodecAlias:
|
||||
return s.Ident
|
||||
|
||||
default:
|
||||
return a.Ident
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the two attributes
|
||||
func (a *Attribute) Compare(b *Attribute) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
|
||||
out := true
|
||||
out = out && a.Ident == b.Ident
|
||||
out = out && a.Label == b.Label
|
||||
out = out && a.SensitivityLevelID == b.SensitivityLevelID
|
||||
out = out && a.MultiValue == b.MultiValue
|
||||
out = out && a.PrimaryKey == b.PrimaryKey
|
||||
out = out && a.SoftDeleteFlag == b.SoftDeleteFlag
|
||||
out = out && a.System == b.System
|
||||
out = out && a.Sortable == b.Sortable
|
||||
out = out && a.Filterable == b.Filterable
|
||||
if a.Store == nil || b.Store == nil {
|
||||
out = out && a.Store == b.Store
|
||||
} else {
|
||||
out = out && a.Store.Type() == b.Store.Type()
|
||||
}
|
||||
if a.Type == nil || b.Type == nil {
|
||||
out = out && a.Type == b.Type
|
||||
} else {
|
||||
out = out && a.Type.Type() == b.Type.Type()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (mm ModelSet) FindByResourceID(resourceID uint64) *Model {
|
||||
for _, m := range mm {
|
||||
if m.ResourceID == resourceID {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mm ModelSet) FindByResourceIdent(resourceType, resourceIdent string) *Model {
|
||||
for _, m := range mm {
|
||||
if m.ResourceType != resourceType {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Resource != resourceIdent {
|
||||
continue
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mm ModelSet) FindByIdent(ident string) *Model {
|
||||
for _, m := range mm {
|
||||
if m.Ident == ident {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByRefs returns the first Model that matches the given refs
|
||||
func (mm ModelSet) FindByRefs(refs map[string]any) *Model {
|
||||
for _, model := range mm {
|
||||
for k, v := range refs {
|
||||
ref, ok := model.Refs[k]
|
||||
if !ok {
|
||||
goto skip
|
||||
}
|
||||
if v != ref {
|
||||
goto skip
|
||||
}
|
||||
}
|
||||
|
||||
return model
|
||||
|
||||
skip:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterByReferenced returns all of the models that reference b
|
||||
func (aa ModelSet) FilterByReferenced(b *Model) (out ModelSet) {
|
||||
for _, aModel := range aa {
|
||||
if aModel.Resource == b.Resource {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, aAttribute := range aModel.Attributes {
|
||||
switch casted := aAttribute.Type.(type) {
|
||||
case *TypeRef:
|
||||
if casted.RefModel.Resource == b.Resource {
|
||||
out = append(out, aModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m Model) ToFilter() ModelRef {
|
||||
return ModelRef{
|
||||
ConnectionID: m.ConnectionID,
|
||||
|
||||
ResourceID: m.ResourceID,
|
||||
|
||||
ResourceType: m.ResourceType,
|
||||
Resource: m.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
// HasAttribute returns true when the model includes the specified attribute
|
||||
func (m Model) HasAttribute(ident string) bool {
|
||||
return m.Attributes.FindByIdent(ident) != nil
|
||||
}
|
||||
|
||||
func (aa AttributeSet) FindByIdent(ident string) *Attribute {
|
||||
for _, a := range aa {
|
||||
if strings.EqualFold(a.Ident, ident) {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (aa AttributeSet) FindByStoreIdent(ident string) *Attribute {
|
||||
for _, a := range aa {
|
||||
if strings.EqualFold(a.StoreIdent(), ident) {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate performs a base model validation before it is passed down
|
||||
func (m Model) Validate() error {
|
||||
if m.Resource == "" {
|
||||
return fmt.Errorf("resource not defined")
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
for _, attr := range m.Attributes {
|
||||
if attr.Ident == "" {
|
||||
return fmt.Errorf("invalid attribute ident: ident must not be empty")
|
||||
}
|
||||
|
||||
if !handle.IsValid(attr.Ident) {
|
||||
return fmt.Errorf("invalid attribute ident: %s is not a valid handle", attr.Ident)
|
||||
}
|
||||
|
||||
if seen[attr.Ident] {
|
||||
return fmt.Errorf("invalid attribute %s: duplicate attributes are not allowed", attr.Ident)
|
||||
}
|
||||
seen[attr.Ident] = true
|
||||
|
||||
if reflect2.IsNil(attr.Type) {
|
||||
return fmt.Errorf("attribute does not define a type: %s", attr.Ident)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare the two models
|
||||
//
|
||||
// This only checks model metadata, the attributes are excluded.
|
||||
func (a Model) Compare(b Model) bool {
|
||||
out := true
|
||||
|
||||
out = out && a.ConnectionID == b.ConnectionID
|
||||
out = out && a.Ident == b.Ident
|
||||
out = out && a.Label == b.Label
|
||||
out = out && a.Resource == b.Resource
|
||||
out = out && a.ResourceID == b.ResourceID
|
||||
out = out && a.ResourceType == b.ResourceType
|
||||
out = out && a.SensitivityLevelID == b.SensitivityLevelID
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (a *Attribute) MarshalJSON() ([]byte, error) {
|
||||
aux := &auxAttribute{
|
||||
Ident: a.Ident,
|
||||
Label: a.Label,
|
||||
SensitivityLevelID: a.SensitivityLevelID,
|
||||
MultiValue: a.MultiValue,
|
||||
PrimaryKey: a.PrimaryKey,
|
||||
SoftDeleteFlag: a.SoftDeleteFlag,
|
||||
System: a.System,
|
||||
Sortable: a.Sortable,
|
||||
Filterable: a.Filterable,
|
||||
|
||||
Store: &auxAttributeStore{},
|
||||
Type: &auxAttributeType{},
|
||||
}
|
||||
|
||||
switch s := a.Store.(type) {
|
||||
case *CodecPlain:
|
||||
aux.Store.Type = "plain"
|
||||
aux.Store.Plain = s
|
||||
|
||||
case *CodecRecordValueSetJSON:
|
||||
aux.Store.Type = "recordValueSetJSON"
|
||||
aux.Store.RecordValueSetJSON = s
|
||||
|
||||
case *CodecAlias:
|
||||
aux.Store.Type = "alias"
|
||||
aux.Store.Alias = s
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown store codec type: %T", s)
|
||||
}
|
||||
|
||||
switch t := a.Type.(type) {
|
||||
case *TypeID:
|
||||
aux.Type.Type = "ID"
|
||||
aux.Type.ID = t
|
||||
|
||||
case *TypeRef:
|
||||
aux.Type.Type = "Ref"
|
||||
aux.Type.Ref = t
|
||||
|
||||
case *TypeTimestamp:
|
||||
aux.Type.Type = "Timestamp"
|
||||
aux.Type.Timestamp = t
|
||||
|
||||
case *TypeTime:
|
||||
aux.Type.Type = "Time"
|
||||
aux.Type.Time = t
|
||||
|
||||
case *TypeDate:
|
||||
aux.Type.Type = "Date"
|
||||
aux.Type.Date = t
|
||||
|
||||
case *TypeNumber:
|
||||
aux.Type.Type = "Number"
|
||||
aux.Type.Number = t
|
||||
|
||||
case *TypeText:
|
||||
aux.Type.Type = "Text"
|
||||
aux.Type.Text = t
|
||||
|
||||
case *TypeBoolean:
|
||||
aux.Type.Type = "Boolean"
|
||||
aux.Type.Boolean = t
|
||||
|
||||
case *TypeEnum:
|
||||
aux.Type.Type = "Enum"
|
||||
aux.Type.Enum = t
|
||||
|
||||
case *TypeGeometry:
|
||||
aux.Type.Type = "Geometry"
|
||||
aux.Type.Geometry = t
|
||||
|
||||
case *TypeJSON:
|
||||
aux.Type.Type = "JSON"
|
||||
aux.Type.JSON = t
|
||||
|
||||
case *TypeBlob:
|
||||
aux.Type.Type = "Blob"
|
||||
aux.Type.Blob = t
|
||||
|
||||
case *TypeUUID:
|
||||
aux.Type.Type = "UUID"
|
||||
aux.Type.UUID = t
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown attribute type type: %T", t)
|
||||
}
|
||||
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
func (a *Attribute) UnmarshalJSON(data []byte) (err error) {
|
||||
aux := &auxAttribute{}
|
||||
err = json.Unmarshal(data, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
*a = Attribute{}
|
||||
}
|
||||
|
||||
a.Ident = aux.Ident
|
||||
a.Label = aux.Label
|
||||
a.SensitivityLevelID = aux.SensitivityLevelID
|
||||
a.MultiValue = aux.MultiValue
|
||||
a.PrimaryKey = aux.PrimaryKey
|
||||
a.SoftDeleteFlag = aux.SoftDeleteFlag
|
||||
a.System = aux.System
|
||||
a.Sortable = aux.Sortable
|
||||
a.Filterable = aux.Filterable
|
||||
|
||||
switch aux.Store.Type {
|
||||
case "plain":
|
||||
a.Store = aux.Store.Plain
|
||||
|
||||
case "recordValueSetJSON":
|
||||
a.Store = aux.Store.RecordValueSetJSON
|
||||
|
||||
case "alias":
|
||||
a.Store = aux.Store.Alias
|
||||
}
|
||||
|
||||
switch aux.Type.Type {
|
||||
case "ID":
|
||||
a.Type = aux.Type.ID
|
||||
|
||||
case "Ref":
|
||||
a.Type = aux.Type.Ref
|
||||
|
||||
case "Timestamp":
|
||||
a.Type = aux.Type.Timestamp
|
||||
|
||||
case "Time":
|
||||
a.Type = aux.Type.Time
|
||||
|
||||
case "Date":
|
||||
a.Type = aux.Type.Date
|
||||
|
||||
case "Number":
|
||||
a.Type = aux.Type.Number
|
||||
|
||||
case "Text":
|
||||
a.Type = aux.Type.Text
|
||||
|
||||
case "Boolean":
|
||||
a.Type = aux.Type.Boolean
|
||||
|
||||
case "Enum":
|
||||
a.Type = aux.Type.Enum
|
||||
|
||||
case "Geometry":
|
||||
a.Type = aux.Type.Geometry
|
||||
|
||||
case "JSON":
|
||||
a.Type = aux.Type.JSON
|
||||
|
||||
case "Blob":
|
||||
a.Type = aux.Type.Blob
|
||||
|
||||
case "UUID":
|
||||
a.Type = aux.Type.UUID
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown attribute type type: %s", aux.Type.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
54
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/normalizer.go
generated
vendored
Normal file
54
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/normalizer.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package dal
|
||||
|
||||
import "strings"
|
||||
|
||||
// NormalizeAttrNames normalizes system identifiers to attribute identifiers
|
||||
// @todo this is temporary until we re-visit field referencing
|
||||
//
|
||||
// The normalization step must exist since legacy versions allowed multiple
|
||||
// identifier variations for the same system field, such as created_at and createdAt.
|
||||
func NormalizeAttrNames(name string) string {
|
||||
switch strings.ToLower(name) {
|
||||
case
|
||||
"recordid",
|
||||
"record_id",
|
||||
"id":
|
||||
return "ID"
|
||||
|
||||
case "moduleid",
|
||||
"module_id",
|
||||
"module":
|
||||
return "moduleID"
|
||||
|
||||
case "owned_by",
|
||||
"ownedby":
|
||||
return "ownedBy"
|
||||
|
||||
case "created_by",
|
||||
"createdby":
|
||||
return "createdBy"
|
||||
|
||||
case "updated_by",
|
||||
"updatedby":
|
||||
return "updatedBy"
|
||||
|
||||
case "deleted_by",
|
||||
"deletedby":
|
||||
return "deletedBy"
|
||||
|
||||
case "created_at",
|
||||
"createdat":
|
||||
return "createdAt"
|
||||
|
||||
case "updated_at",
|
||||
"updatedat":
|
||||
return "updatedAt"
|
||||
|
||||
case "deleted_at",
|
||||
"deletedat":
|
||||
return "deletedAt"
|
||||
}
|
||||
|
||||
return name
|
||||
|
||||
}
|
||||
155
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/operations.go
generated
vendored
Normal file
155
extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/pkg/dal/operations.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
package dal
|
||||
|
||||
type (
|
||||
Operation string
|
||||
OperationSet []Operation
|
||||
)
|
||||
|
||||
const (
|
||||
Create Operation = "corteza::dal:operation:create"
|
||||
Update Operation = "corteza::dal:operation:update"
|
||||
Delete Operation = "corteza::dal:operation:delete"
|
||||
Search Operation = "corteza::dal:operation:search"
|
||||
Lookup Operation = "corteza::dal:operation:lookup"
|
||||
Paging Operation = "corteza::dal:operation:paging"
|
||||
Sorting Operation = "corteza::dal:operation:sorting"
|
||||
)
|
||||
|
||||
var (
|
||||
full = OperationSet{
|
||||
Create,
|
||||
Update,
|
||||
Delete,
|
||||
Search,
|
||||
Lookup,
|
||||
Paging,
|
||||
Sorting,
|
||||
}
|
||||
|
||||
createOperations = OperationSet{
|
||||
Create,
|
||||
}
|
||||
|
||||
updateOperations = OperationSet{
|
||||
Update,
|
||||
}
|
||||
|
||||
deleteOperations = OperationSet{
|
||||
Update,
|
||||
}
|
||||
|
||||
searchOperations = OperationSet{
|
||||
Search,
|
||||
Paging,
|
||||
Sorting,
|
||||
}
|
||||
|
||||
lookupOperations = OperationSet{
|
||||
Lookup,
|
||||
}
|
||||
)
|
||||
|
||||
// FullOperations returns all base system defined operations
|
||||
func FullOperations() (cc OperationSet) {
|
||||
// Doing an union just to make a fresh copy
|
||||
return full.Union(nil)
|
||||
}
|
||||
|
||||
// CreateOperations returns only requested operations used for Create operations
|
||||
func CreateOperations(requested ...Operation) (required OperationSet) {
|
||||
return common(createOperations, requested)
|
||||
}
|
||||
|
||||
// UpdateOperations returns only requested operations used for Update operations
|
||||
func UpdateOperations(requested ...Operation) (required OperationSet) {
|
||||
return common(updateOperations, requested)
|
||||
}
|
||||
|
||||
// DeleteOperations returns only requested operations used for delete operations
|
||||
func DeleteOperations(requested ...Operation) (required OperationSet) {
|
||||
return common(deleteOperations, requested)
|
||||
}
|
||||
|
||||
// SearchOperations returns only requested operations used for Search operations
|
||||
func SearchOperations(requested ...Operation) (required OperationSet) {
|
||||
return common(searchOperations, requested)
|
||||
}
|
||||
|
||||
// LookupOperations returns only requested operations used for Search operations
|
||||
func LookupOperations(requested ...Operation) (required OperationSet) {
|
||||
return common(lookupOperations, requested)
|
||||
}
|
||||
|
||||
func common(aa, bb OperationSet) OperationSet {
|
||||
return aa.Intersect(bb)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
// IsSuperset is inverse IsSubset
|
||||
//
|
||||
// IsSuperset checks if all bb operations are inside aa
|
||||
func (aa OperationSet) IsSuperset(bb ...Operation) bool {
|
||||
return OperationSet(bb).IsSubset(aa...)
|
||||
}
|
||||
|
||||
// IsSubset checks if all aa operations are inside bb
|
||||
func (aa OperationSet) IsSubset(bb ...Operation) bool {
|
||||
if len(aa) > len(bb) {
|
||||
return false
|
||||
}
|
||||
|
||||
// When A is subset of B, the difference between the two must be 0
|
||||
return len(aa.Diff(bb)) == 0
|
||||
}
|
||||
|
||||
// Intersect returns the intersection between the two sets
|
||||
func (aa OperationSet) Intersect(bb OperationSet) (cc OperationSet) {
|
||||
cc = make(OperationSet, 0, len(aa))
|
||||
for _, a := range aa {
|
||||
for _, b := range bb {
|
||||
if a == b {
|
||||
cc = append(cc, a)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Intersect returns the union between the two sets
|
||||
//
|
||||
// Duplicates are omitted
|
||||
func (aa OperationSet) Union(bb OperationSet) (cc OperationSet) {
|
||||
ix := make(map[Operation]bool)
|
||||
for _, c := range append(aa, bb...) {
|
||||
if !ix[c] {
|
||||
ix[c] = true
|
||||
cc = append(cc, c)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Diff calculates the difference between the two operation sets
|
||||
//
|
||||
// The diff uses aa as base
|
||||
func (aa OperationSet) Diff(bb OperationSet) (cc OperationSet) {
|
||||
for _, a := range aa {
|
||||
found := false
|
||||
for _, b := range bb {
|
||||
found = a == b
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
cc = append(cc, a)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user