From a1a5fb6d8b1e4a5efda6ce7ef5051ee1eee69c39 Mon Sep 17 00:00:00 2001 From: AndrewSisley Date: Tue, 17 Jan 2023 15:05:14 -0500 Subject: [PATCH] refactor: Extract query schema errors to dedicated file (#1037) --- query/graphql/schema/descriptions.go | 12 +-- query/graphql/schema/errors.go | 100 ++++++++++++++++++++++++ query/graphql/schema/generate.go | 33 +++----- query/graphql/schema/relations.go | 15 ++-- tests/integration/schema/simple_test.go | 4 +- 5 files changed, 121 insertions(+), 43 deletions(-) create mode 100644 query/graphql/schema/errors.go diff --git a/query/graphql/schema/descriptions.go b/query/graphql/schema/descriptions.go index 9813ac45da..f88b8d5136 100644 --- a/query/graphql/schema/descriptions.go +++ b/query/graphql/schema/descriptions.go @@ -19,7 +19,6 @@ import ( "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" - "github.com/sourcenetwork/defradb/errors" ) var ( @@ -167,7 +166,7 @@ func (g *Generator) CreateDescriptions( // let's make sure its an _id field, otherwise // we might have an error here if !strings.HasSuffix(fname, "_id") { - return nil, errors.New(fmt.Sprintf("Error: found a duplicate field '%s' for type %s", fname, t.Name())) + return nil, NewErrDuplicateField(fname, t.Name()) } continue } @@ -186,18 +185,13 @@ func (g *Generator) CreateDescriptions( rel := g.manager.Relations.getRelationByDescription( fname, schemaName, t.Name()) if rel == nil { - return nil, errors.New(fmt.Sprintf( - "Field missing associated relation. FieldName: %s, SchemaType: %s, ObjectType: %s", - fname, - field.Type.Name(), - t.Name(), - )) + return nil, NewErrFieldMissingRelation(field.Type.Name(), fname, t.Name()) } fd.RelationName = rel.name _, fieldRelationType, ok := rel.GetField(schemaName, fname) if !ok { - return nil, errors.New("relation is missing field") + return nil, NewErrRelationMissingField(field.Type.Name(), fname) } fd.RelationType = rel.Kind() | fieldRelationType diff --git a/query/graphql/schema/errors.go b/query/graphql/schema/errors.go new file mode 100644 index 0000000000..73499df468 --- /dev/null +++ b/query/graphql/schema/errors.go @@ -0,0 +1,100 @@ +// Copyright 2022 Democratized Data Foundation +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package schema + +import "github.com/sourcenetwork/defradb/errors" + +const ( + errDuplicateField string = "duplicate field" + errFieldMissingRelation string = "field missing associated relation" + errRelationMissingField string = "relation missing field" + errAggregateTargetNotFound string = "aggregate target not found" + errSchemaTypeAlreadyExist string = "schema type already exists" + errObjectNotFoundDuringThunk string = "object not found whilst executing fields thunk" + errTypeNotFound string = "no type found for given name" + errRelationNotFound string = "no relation found" +) + +var ( + ErrDuplicateField = errors.New(errDuplicateField) + ErrFieldMissingRelation = errors.New(errFieldMissingRelation) + ErrRelationMissingField = errors.New(errRelationMissingField) + ErrAggregateTargetNotFound = errors.New(errAggregateTargetNotFound) + ErrSchemaTypeAlreadyExist = errors.New(errSchemaTypeAlreadyExist) + ErrObjectNotFoundDuringThunk = errors.New(errObjectNotFoundDuringThunk) + ErrTypeNotFound = errors.New(errTypeNotFound) + ErrRelationNotFound = errors.New(errRelationNotFound) + ErrRelationMutlipleTypes = errors.New("relation type can only be either One or Many, not both") + ErrRelationMissingTypes = errors.New("relation is missing its defined types and fields") + ErrRelationInvalidType = errors.New("relation has an invalid type to be finalize") + ErrMultipleRelationPrimaries = errors.New("relation can only have a single field set as primary") +) + +func NewErrDuplicateField(objectName, fieldName string) error { + return errors.New( + errDuplicateField, + errors.NewKV("Object", objectName), + errors.NewKV("Field", fieldName), + ) +} + +func NewErrFieldMissingRelation(objectName, fieldName string, objectType string) error { + return errors.New( + errFieldMissingRelation, + errors.NewKV("Object", objectName), + errors.NewKV("Field", fieldName), + errors.NewKV("ObjectType", objectType), + ) +} + +func NewErrRelationMissingField(objectName, fieldName string) error { + return errors.New( + errRelationMissingField, + errors.NewKV("Object", objectName), + errors.NewKV("Field", fieldName), + ) +} + +func NewErrAggregateTargetNotFound(objectName, target string) error { + return errors.New( + errAggregateTargetNotFound, + errors.NewKV("Object", objectName), + errors.NewKV("Target", target), + ) +} + +func NewErrSchemaTypeAlreadyExist(name string) error { + return errors.New( + errSchemaTypeAlreadyExist, + errors.NewKV("Name", name), + ) +} + +func NewErrObjectNotFoundDuringThunk(object string) error { + return errors.New( + errObjectNotFoundDuringThunk, + errors.NewKV("Object", object), + ) +} + +func NewErrTypeNotFound(typeName string) error { + return errors.New( + errTypeNotFound, + errors.NewKV("Type", typeName), + ) +} + +func NewErrRelationNotFound(relationName string) error { + return errors.New( + errRelationNotFound, + errors.NewKV("RelationName", relationName), + ) +} diff --git a/query/graphql/schema/generate.go b/query/graphql/schema/generate.go index 817bccf981..297b42a1a7 100644 --- a/query/graphql/schema/generate.go +++ b/query/graphql/schema/generate.go @@ -20,7 +20,6 @@ import ( "github.com/graphql-go/graphql/language/source" "github.com/sourcenetwork/defradb/client" - "github.com/sourcenetwork/defradb/errors" "github.com/sourcenetwork/defradb/client/request" schemaTypes "github.com/sourcenetwork/defradb/query/graphql/schema/types" @@ -303,11 +302,7 @@ func (g *Generator) createExpandedFieldAggregate( filterTypeName = targeted.Type.Name() + "FilterArg" } } else { - return errors.New(fmt.Sprintf( - "Aggregate target not found. HostObject: {%s}, Target: {%s}", - obj.Name(), - target, - )) + return NewErrAggregateTargetNotFound(obj.Name(), target) } } @@ -386,7 +381,7 @@ func (g *Generator) buildTypesFromAST( case *ast.ObjectDefinition: // check if type exists if _, ok := g.manager.schema.TypeMap()[defType.Name.Value]; ok { - return nil, errors.New(fmt.Sprintf("Schema type already exists: %s", defType.Name.Value)) + return nil, NewErrSchemaTypeAlreadyExist(defType.Name.Value) } objconf := gql.ObjectConfig{} @@ -487,10 +482,7 @@ func (g *Generator) buildTypesFromAST( gqlType, ok := g.manager.schema.TypeMap()[defType.Name.Value] if !ok { - return nil, errors.New(fmt.Sprintf( - "object not found whilst executing fields thunk: %s", - defType.Name.Value, - )) + return nil, NewErrObjectNotFoundDuringThunk(defType.Name.Value) } fields[request.GroupFieldName] = &gql.Field{ @@ -530,10 +522,7 @@ func getRelationshipName( if argument.Name.Value == "name" { name, isString := argument.Value.GetValue().(string) if !isString { - return "", errors.New(fmt.Sprintf( - "Relationship name must be of type string, but was: %v", - argument.Value.GetKind(), - )) + return "", client.NewErrUnexpectedType[string]("Relationship name", argument.Value.GetValue()) } return name, nil } @@ -947,7 +936,7 @@ func appendCommitChildGroupField() { // The latter two are wrappers, and need to be further extracted func astNodeToGqlType(typeMap map[string]gql.Type, t ast.Type) (gql.Type, error) { if t == nil { - return nil, errors.New("type can't be nil") + return nil, client.NewErrUninitializeProperty("astNodeToGqlType", "t") } switch astTypeVal := t.(type) { @@ -974,7 +963,7 @@ func astNodeToGqlType(typeMap map[string]gql.Type, t ast.Type) (gql.Type, error) name := t.(*ast.Named).Name.Value ttype, ok := typeMap[name] if !ok { - return nil, errors.New(fmt.Sprintf("No type found for given name: %s", name)) + return nil, NewErrTypeNotFound(name) } return ttype, nil @@ -1013,7 +1002,7 @@ func (g *Generator) GenerateMutationInputForGQLType(obj *gql.Object) ([]*gql.Fie typeName := obj.Name() filter, ok := g.manager.schema.TypeMap()[typeName+"FilterArg"].(*gql.InputObject) if !ok { - return nil, errors.New("missing filter arg for mutation type generation " + typeName) + return nil, NewErrTypeNotFound(typeName + "FilterArg") } return g.genTypeMutationFields(obj, filter) @@ -1192,17 +1181,13 @@ func (g *Generator) genLeafFilterArgInput(obj gql.Type) *gql.InputObject { operatorType, hasOperatorType := g.manager.schema.TypeMap()[operatorBlockName] if !hasOperatorType { // This should be impossible - return nil, errors.New("operator block not found", errors.NewKV("Name", operatorBlockName)) + return nil, NewErrTypeNotFound(operatorBlockName) } operatorObject, isInputObj := operatorType.(*gql.InputObject) if !isInputObj { // This should be impossible - return nil, errors.New( - "invalid cast", - errors.NewKV("Expected type", "*gql.InputObject"), - errors.NewKV("Actual type", fmt.Sprintf("%T", operatorType)), - ) + return nil, client.NewErrUnexpectedType[*gql.InputObject]("operatorType", operatorType) } for f, field := range operatorObject.Fields() { diff --git a/query/graphql/schema/relations.go b/query/graphql/schema/relations.go index cb8a021583..b35be0f7ea 100644 --- a/query/graphql/schema/relations.go +++ b/query/graphql/schema/relations.go @@ -15,7 +15,6 @@ import ( "strings" "github.com/sourcenetwork/defradb/client" - "github.com/sourcenetwork/defradb/errors" ) // type uint8 uint8 @@ -47,7 +46,7 @@ func (rm *RelationManager) GetRelations() {} func (rm *RelationManager) GetRelation(name string) (*Relation, error) { rel, ok := rm.relations[name] if !ok { - return nil, errors.New("no relation found") + return nil, NewErrRelationNotFound(name) } return rel, nil } @@ -105,12 +104,12 @@ func (rm *RelationManager) RegisterSingle( relType client.RelationType, ) (bool, error) { if name == "" { - return false, errors.New("relation name must be non empty") + return false, client.NewErrUninitializeProperty("RegisterSingle", "name") } // make sure the relation type is ONLY One or Many, not both if relType.IsSet(client.Relation_Type_ONE) == relType.IsSet(client.Relation_Type_MANY) { - return false, errors.New("relation type can only be either One or Many, not both") + return false, ErrRelationMutlipleTypes } // make a copy of rel type, one goes to the relation.relType, and the other goes into the []types. @@ -200,14 +199,14 @@ type Relation struct { func (r *Relation) finalize() error { // make sure all the types/fields are set if len(r.types) != 2 || len(r.schemaTypes) != 2 || len(r.fields) != 2 { - return errors.New("relation is missing its defined types and fields") + return ErrRelationMissingTypes } // make sure its one of One-to-One, One-to-Many, Many-to-Many if !r.relType.IsSet(client.Relation_Type_ONEONE) && !r.relType.IsSet(client.Relation_Type_ONEMANY) && !r.relType.IsSet(client.Relation_Type_MANYMANY) { - return errors.New("relation has an invalid type to be finalize") + return ErrRelationInvalidType } // make sure we have a primary set if its a one-to-one or many-to-many @@ -218,7 +217,7 @@ func (r *Relation) finalize() error { // both types have primary set if aBit.IsSet(client.Relation_Type_Primary) { - return errors.New("relation can only have a single field set as primary") + return ErrMultipleRelationPrimaries } else if !xBit.IsSet(client.Relation_Type_Primary) { // neither type has primary set, auto add to // lexicographically first one by schema type name @@ -320,7 +319,7 @@ func (r Relation) GetFieldFromSchemaType(schemaType string) (string, client.Rela func genRelationName(t1, t2 string) (string, error) { if t1 == "" || t2 == "" { - return "", errors.New("relation types cannot be empty") + return "", client.NewErrUninitializeProperty("genRelationName", "relation types") } t1 = strings.ToLower(t1) t2 = strings.ToLower(t2) diff --git a/tests/integration/schema/simple_test.go b/tests/integration/schema/simple_test.go index ba108fb521..39eb488622 100644 --- a/tests/integration/schema/simple_test.go +++ b/tests/integration/schema/simple_test.go @@ -55,7 +55,7 @@ func TestSchemaSimpleErrorsGivenDuplicateSchema(t *testing.T) { } } `, - ExpectedError: "Schema type already exists", + ExpectedError: "schema type already exists", } ExecuteQueryTestCase(t, test) @@ -136,7 +136,7 @@ func TestSchemaSimpleErrorsGivenTypeWithInvalidFieldType(t *testing.T) { } } `, - ExpectedError: "No type found for given name", + ExpectedError: "no type found for given name", } ExecuteQueryTestCase(t, test)