Skip to content

Commit

Permalink
refactor: Index field directive (sourcenetwork#2994)
Browse files Browse the repository at this point in the history
## Relevant issue(s)

Resolves sourcenetwork#2926

## Description

This PR fixes an issue where the index directive was defined twice. ~The
`@index` field level directive has been renamed to `@indexField`.~

The `fields` arg has been renamed to `includes` and is now a list of
objects of type:

```
type IndexFieldInput {
  name: String
  direction: Ordering
}
```

The `direction` argument now sets the default direction of all fields in
the `includes` list.

When the index is used on a field definition and the field is not in the
`includes` list it will be implicitly added.

## Tasks

- [x] I made sure the code is well commented, particularly
hard-to-understand areas.
- [x] I made sure the repository-held documentation is changed
accordingly.
- [x] I made sure the pull request title adheres to the conventional
commit style (the subset used in the project can be found in
[tools/configs/chglog/config.yml](tools/configs/chglog/config.yml)).
- [x] I made sure to discuss its limitations such as threats to
validity, vulnerability to mistake and misuse, robustness to
invalidation of assumptions, resource requirements, ...

## How has this been tested?

Covered by existing tests.

Specify the platform(s) on which this was tested:
- MacOS
  • Loading branch information
nasdf authored Sep 16, 2024
1 parent ccf4d93 commit f5567f5
Show file tree
Hide file tree
Showing 13 changed files with 502 additions and 212 deletions.
172 changes: 98 additions & 74 deletions internal/request/graphql/schema/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func collectionFromAstDefinition(

for _, directive := range field.Directives {
if directive.Name.Value == types.IndexDirectiveLabel {
index, err := fieldIndexFromAST(field, directive)
index, err := indexFromAST(directive, field)
if err != nil {
return client.CollectionDefinition{}, err
}
Expand Down Expand Up @@ -164,7 +164,7 @@ func collectionFromAstDefinition(

for _, directive := range def.Directives {
if directive.Name.Value == types.IndexDirectiveLabel {
index, err := indexFromAST(directive)
index, err := indexFromAST(directive, nil)
if err != nil {
return client.CollectionDefinition{}, err
}
Expand Down Expand Up @@ -239,110 +239,134 @@ func IsValidIndexName(name string) bool {
return true
}

func fieldIndexFromAST(field *ast.FieldDefinition, directive *ast.Directive) (client.IndexDescription, error) {
desc := client.IndexDescription{
Fields: []client.IndexedFieldDescription{
{
Name: field.Name.Value,
},
},
}
func indexFromAST(directive *ast.Directive, fieldDef *ast.FieldDefinition) (client.IndexDescription, error) {
var name string
var unique bool

var direction *ast.EnumValue
var includes *ast.ListValue

for _, arg := range directive.Arguments {
switch arg.Name.Value {
case types.IndexDirectivePropName:
nameVal, ok := arg.Value.(*ast.StringValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
desc.Name = nameVal.Value
if !IsValidIndexName(desc.Name) {
return client.IndexDescription{}, NewErrIndexWithInvalidName(desc.Name)
name = nameVal.Value
if !IsValidIndexName(name) {
return client.IndexDescription{}, NewErrIndexWithInvalidName(name)
}
case types.IndexDirectivePropUnique:
boolVal, ok := arg.Value.(*ast.BooleanValue)

case types.IndexDirectivePropIncludes:
includesVal, ok := arg.Value.(*ast.ListValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
desc.Unique = boolVal.Value
includes = includesVal

case types.IndexDirectivePropDirection:
dirVal, ok := arg.Value.(*ast.EnumValue)
directionVal, ok := arg.Value.(*ast.EnumValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
if dirVal.Value == types.FieldOrderDESC {
desc.Fields[0].Descending = true
direction = directionVal

case types.IndexDirectivePropUnique:
uniqueVal, ok := arg.Value.(*ast.BooleanValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
unique = uniqueVal.Value

default:
return client.IndexDescription{}, ErrIndexWithUnknownArg
}
}
return desc, nil
}

func indexFromAST(directive *ast.Directive) (client.IndexDescription, error) {
desc := client.IndexDescription{}
var directions *ast.ListValue
for _, arg := range directive.Arguments {
switch arg.Name.Value {
case types.IndexDirectivePropName:
nameVal, ok := arg.Value.(*ast.StringValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
desc.Name = nameVal.Value
if !IsValidIndexName(desc.Name) {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
case types.IndexDirectivePropFields:
fieldsVal, ok := arg.Value.(*ast.ListValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
for _, field := range fieldsVal.Values {
fieldVal, ok := field.(*ast.StringValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
desc.Fields = append(desc.Fields, client.IndexedFieldDescription{
Name: fieldVal.Value,
})
}
case types.IndexDirectivePropDirections:
var ok bool
directions, ok = arg.Value.(*ast.ListValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
var containsField bool
var fields []client.IndexedFieldDescription

if includes != nil {
for _, include := range includes.Values {
field, err := indexFieldFromAST(include, direction)
if err != nil {
return client.IndexDescription{}, err
}
case types.IndexDirectivePropUnique:
boolVal, ok := arg.Value.(*ast.BooleanValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
if fieldDef != nil && fieldDef.Name.Value == field.Name {
containsField = true
}
desc.Unique = boolVal.Value
default:
return client.IndexDescription{}, ErrIndexWithUnknownArg
fields = append(fields, field)
}
}
if len(desc.Fields) == 0 {

// if the directive is applied to a field and
// the field is not in the includes list
// implicitly add it as the first entry
if !containsField && fieldDef != nil {
field := client.IndexedFieldDescription{
Name: fieldDef.Name.Value,
}
if direction != nil {
field.Descending = direction.Value == types.FieldOrderDESC
}
fields = append([]client.IndexedFieldDescription{field}, fields...)
}

if len(fields) == 0 {
return client.IndexDescription{}, ErrIndexMissingFields
}
if directions != nil {
if len(directions.Values) != len(desc.Fields) {
return client.IndexDescription{}, ErrIndexWithInvalidArg
}
for i := range desc.Fields {
dirVal, ok := directions.Values[i].(*ast.EnumValue)

return client.IndexDescription{
Name: name,
Fields: fields,
Unique: unique,
}, nil
}

func indexFieldFromAST(value ast.Value, defaultDirection *ast.EnumValue) (client.IndexedFieldDescription, error) {
argTypeObject, ok := value.(*ast.ObjectValue)
if !ok {
return client.IndexedFieldDescription{}, ErrIndexWithInvalidArg
}

var name string
var direction *ast.EnumValue

for _, field := range argTypeObject.Fields {
switch field.Name.Value {
case types.IndexFieldInputName:
nameVal, ok := field.Value.(*ast.StringValue)
if !ok {
return client.IndexDescription{}, ErrIndexWithInvalidArg
return client.IndexedFieldDescription{}, ErrIndexWithInvalidArg
}
if dirVal.Value == types.FieldOrderASC {
desc.Fields[i].Descending = false
} else if dirVal.Value == types.FieldOrderDESC {
desc.Fields[i].Descending = true
name = nameVal.Value

case types.IndexFieldInputDirection:
directionVal, ok := field.Value.(*ast.EnumValue)
if !ok {
return client.IndexedFieldDescription{}, ErrIndexWithInvalidArg
}
direction = directionVal

default:
return client.IndexedFieldDescription{}, ErrIndexWithUnknownArg
}
}
return desc, nil

var descending bool
// if the direction is explicitly set use that value, otherwise
// if the default direction was set on the index use that value
if direction != nil {
descending = direction.Value == types.FieldOrderDESC
} else if defaultDirection != nil {
descending = defaultDirection.Value == types.FieldOrderDESC
}

return client.IndexedFieldDescription{
Name: name,
Descending: descending,
}, nil
}

func fieldsFromAST(
Expand Down
Loading

0 comments on commit f5567f5

Please sign in to comment.