Skip to content

Commit

Permalink
MGMT-16391: Add support for exclude_fields (#40)
Browse files Browse the repository at this point in the history
This patch adds support for the `exclude_fields` query parameter as
defined in section 5.3.2 of the ETSI GS NFV-SOL 013 document. For,
example, to exclude the _name_ field from the list of deployment
managers:

```
GET /o2ims-infrastructureInventory/v1/deploymentManagers?exclude_fields=name
```

This requires a large refactoring of the search package, as well as
changes in the adapters. The logic handlers aren't affected.

Related: https://issues.redhat.com/browse/MGMT-16391
Related: https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/013/03.03.01_60/gs_NFV-SOL013v030301p.pdf

Signed-off-by: Juan Hernandez <[email protected]>
  • Loading branch information
jhernand authored Dec 13, 2023
1 parent 9b5ff4f commit 8c36791
Show file tree
Hide file tree
Showing 19 changed files with 1,443 additions and 298 deletions.
28 changes: 28 additions & 0 deletions internal/search/path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright (c) 2023 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions and limitations under the
License.
*/

package search

import (
"slices"
)

// Path represents the path of an attribute inside a complex data type. Each value of the slice is
// an attribute name, starting with the outermost field name.
type Path []string

// Clone creates a deep copy of the path.
func (p Path) Clone() Path {
return slices.Clone(p)
}
2 changes: 1 addition & 1 deletion internal/search/path_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (b *PathEvaluatorBuilder) Build() (result *PathEvaluator, err error) {
}

// Evaluate receives the attribute path and the object and returns the value of that attribute.
func (r *PathEvaluator) Evaluate(ctx context.Context, path []string, object any) (result any,
func (r *PathEvaluator) Evaluate(ctx context.Context, path Path, object any) (result any,
err error) {
value, err := r.evaluate(ctx, path, reflect.ValueOf(object))
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,86 +23,86 @@ import (
"unicode"
)

// projectorLexerBuilder contains the data and logic needed to create a new lexical scanner for
// field selection expressions. Don't create instances of this directly, use the newProjectorLexer
// pathsLexerBuilder contains the data and logic needed to create a new lexical scanner for field
// paths. Don't create instances of this directly, use the newPthsLexer
// function instead.
type projectorLexerBuilder struct {
type pathsLexerBuilder struct {
logger *slog.Logger
source string
}

// projectorLexer is a lexical scanner for the field selector expression language. Don't create
// instances of this type directly, use the newProjectorLexer function instead.
type projectorLexer struct {
// pathsLexer is a lexical scanner for the fields paths. Don't create instances of this type
// directly, use the newPthsLexer function instead.
type pathsLexer struct {
logger *slog.Logger
buffer *bytes.Buffer
}

// projectorSymbol represents the terminal symbols of the field selection language.
type projectorSymbol int
// pathsSymbol represents the terminal symbols of the field selection language.
type pathsSymbol int

const (
projectorSymbolEnd projectorSymbol = iota
projectorSymbolIdentifier
projectorSymbolComma
projectorSymbolSlash
pathsSymbolEnd pathsSymbol = iota
pathsSymbolIdentifier
pathsSymbolComma
pathsSymbolSlash
)

// String generates a string representation of the terminal symbol.
func (s projectorSymbol) String() string {
func (s pathsSymbol) String() string {
switch s {
case projectorSymbolEnd:
case pathsSymbolEnd:
return "End"
case projectorSymbolIdentifier:
case pathsSymbolIdentifier:
return "Identifier"
case projectorSymbolComma:
case pathsSymbolComma:
return "Comma"
case projectorSymbolSlash:
case pathsSymbolSlash:
return "Slash"
default:
return fmt.Sprintf("Unknown:%d", s)
}
}

// projectorToken represents the tokens returned by the lexical scanner. Each token contains the
// pathsToken represents the tokens returned by the lexical scanner. Each token contains the
// terminal symbol and its text.
type projectorToken struct {
Symbol projectorSymbol
type pathsToken struct {
Symbol pathsSymbol
Text string
}

// String geneates a string representation of the token.
func (t *projectorToken) String() string {
func (t *pathsToken) String() string {
if t == nil {
return "Nil"
}
switch t.Symbol {
case projectorSymbolIdentifier:
case pathsSymbolIdentifier:
return fmt.Sprintf("%s:%s", t.Symbol, t.Text)
default:
return t.Symbol.String()
}
}

// newProjectorLexer creates a builder that can then be used to configure and create lexers.
func newProjectorLexer() *projectorLexerBuilder {
return &projectorLexerBuilder{}
// newPathsLexer creates a builder that can then be used to configure and create lexers.
func newPathsLexer() *pathsLexerBuilder {
return &pathsLexerBuilder{}
}

// SetLogger sets the logger that the lexer will use to write log messesages. This is mandatory.
func (b *projectorLexerBuilder) SetLogger(value *slog.Logger) *projectorLexerBuilder {
func (b *pathsLexerBuilder) SetLogger(value *slog.Logger) *pathsLexerBuilder {
b.logger = value
return b
}

// SetSource sets the source string to parse. This is mandatory.
func (b *projectorLexerBuilder) SetSource(value string) *projectorLexerBuilder {
func (b *pathsLexerBuilder) SetSource(value string) *pathsLexerBuilder {
b.source = value
return b
}

// Build uses the data stored in the builder to create a new lexer.
func (b *projectorLexerBuilder) Build() (result *projectorLexer, err error) {
func (b *pathsLexerBuilder) Build() (result *pathsLexer, err error) {
// Check parameters:
if b.logger == nil {
err = errors.New("logger is mandatory")
Expand All @@ -114,15 +114,15 @@ func (b *projectorLexerBuilder) Build() (result *projectorLexer, err error) {
}

// Create and populate the object:
result = &projectorLexer{
result = &pathsLexer{
logger: b.logger,
buffer: bytes.NewBufferString(b.source),
}
return
}

// FetchToken fetches the next token from the source.
func (l *projectorLexer) FetchToken() (token *projectorToken, err error) {
func (l *pathsLexer) FetchToken() (token *pathsToken, err error) {
type State int
const (
S0 State = iota
Expand All @@ -142,22 +142,22 @@ func (l *projectorLexer) FetchToken() (token *projectorToken, err error) {
lexeme.WriteRune(r)
state = S1
case r == ',':
token = &projectorToken{
Symbol: projectorSymbolComma,
token = &pathsToken{
Symbol: pathsSymbolComma,
Text: ",",
}
return
case r == '/':
token = &projectorToken{
Symbol: projectorSymbolSlash,
token = &pathsToken{
Symbol: pathsSymbolSlash,
Text: "/",
}
return
case r == '~':
state = S2
case r == 0:
token = &projectorToken{
Symbol: projectorSymbolEnd,
token = &pathsToken{
Symbol: pathsSymbolEnd,
}
return
default:
Expand All @@ -177,8 +177,8 @@ func (l *projectorLexer) FetchToken() (token *projectorToken, err error) {
state = S2
default:
l.unreadRune()
token = &projectorToken{
Symbol: projectorSymbolIdentifier,
token = &pathsToken{
Symbol: pathsSymbolIdentifier,
Text: lexeme.String(),
}
return
Expand Down Expand Up @@ -209,7 +209,7 @@ func (l *projectorLexer) FetchToken() (token *projectorToken, err error) {
}
}

func (l *projectorLexer) readRune() rune {
func (l *pathsLexer) readRune() rune {
r, _, err := l.buffer.ReadRune()
if errors.Is(err, io.EOF) {
return 0
Expand All @@ -224,7 +224,7 @@ func (l *projectorLexer) readRune() rune {
return r
}

func (l *projectorLexer) unreadRune() {
func (l *pathsLexer) unreadRune() {
err := l.buffer.UnreadRune()
if err != nil {
l.logger.Error(
Expand Down
Loading

0 comments on commit 8c36791

Please sign in to comment.