diff --git a/request/graphql/schema/collection.go b/request/graphql/schema/collection.go index 4680bdceff..d9ebefa680 100644 --- a/request/graphql/schema/collection.go +++ b/request/graphql/schema/collection.go @@ -15,15 +15,14 @@ import ( "fmt" "sort" + "github.com/sourcenetwork/graphql-go/language/ast" + gqlp "github.com/sourcenetwork/graphql-go/language/parser" + "github.com/sourcenetwork/graphql-go/language/source" "github.com/sourcenetwork/immutable" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/request/graphql/schema/types" - - "github.com/sourcenetwork/graphql-go/language/ast" - gqlp "github.com/sourcenetwork/graphql-go/language/parser" - "github.com/sourcenetwork/graphql-go/language/source" ) // FromString parses a GQL SDL string into a set of collection descriptions. @@ -390,18 +389,15 @@ func setCRDTType(field *ast.FieldDefinition, kind client.FieldKind) (client.CTyp for _, arg := range directive.Arguments { switch arg.Name.Value { case "type": - cType := arg.Value.GetValue().(string) - switch cType { - case client.PN_COUNTER.String(): - if !client.PN_COUNTER.IsCompatibleWith(kind) { - return 0, client.NewErrCRDTKindMismatch(cType, kind.String()) - } - return client.PN_COUNTER, nil - case client.LWW_REGISTER.String(): - return client.LWW_REGISTER, nil - default: - return 0, client.NewErrInvalidCRDTType(field.Name.Value, cType) + cTypeString := arg.Value.GetValue().(string) + cType, validCRDTEnum := types.CRDTEnum.ParseValue(cTypeString).(client.CType) + if !validCRDTEnum { + return 0, client.NewErrInvalidCRDTType(field.Name.Value, cTypeString) + } + if !cType.IsCompatibleWith(kind) { + return 0, client.NewErrCRDTKindMismatch(cType.String(), kind.String()) } + return cType, nil } } } diff --git a/request/graphql/schema/manager.go b/request/graphql/schema/manager.go index 92286ac85e..89860d2c53 100644 --- a/request/graphql/schema/manager.go +++ b/request/graphql/schema/manager.go @@ -111,9 +111,12 @@ func defaultMutationType() *gql.Object { // default directives type. func defaultDirectivesType() []*gql.Directive { return []*gql.Directive{ + schemaTypes.CRDTFieldDirective, schemaTypes.ExplainDirective, schemaTypes.IndexDirective, schemaTypes.IndexFieldDirective, + schemaTypes.PrimaryDirective, + schemaTypes.RelationDirective, } } @@ -166,6 +169,7 @@ func defaultTypes() []gql.Type { schemaTypes.CommitLinkObject, schemaTypes.CommitObject, + schemaTypes.CRDTEnum, schemaTypes.ExplainEnum, } } diff --git a/request/graphql/schema/types/descriptions.go b/request/graphql/schema/types/descriptions.go index 42c1ba956e..ded07f81e6 100644 --- a/request/graphql/schema/types/descriptions.go +++ b/request/graphql/schema/types/descriptions.go @@ -217,6 +217,9 @@ Sort the results in ascending order, e.g. null,1,2,3,a,b,c. ` descOrderDescription string = ` Sort the results in descending order, e.g. c,b,a,3,2,1,null. +` + crdtDirectiveDescription string = ` +Allows the explicit definition of a field's CRDT type. By default it is defined as LWWRegister. ` primaryDirectiveDescription string = ` Indicate the primary side of a one-to-one relationship. diff --git a/request/graphql/schema/types/types.go b/request/graphql/schema/types/types.go index 199f21deb3..37cb840d05 100644 --- a/request/graphql/schema/types/types.go +++ b/request/graphql/schema/types/types.go @@ -12,6 +12,8 @@ package types import ( gql "github.com/sourcenetwork/graphql-go" + + "github.com/sourcenetwork/defradb/client" ) const ( @@ -24,6 +26,9 @@ const ( ExplainArgExecute string = "execute" ExplainArgDebug string = "debug" + CRDTDirectiveLabel = "crdt" + CRDTDirectivePropType = "type" + IndexDirectiveLabel = "index" IndexDirectivePropName = "name" IndexDirectivePropUnique = "unique" @@ -127,6 +132,35 @@ var ( }, }) + CRDTEnum = gql.NewEnum(gql.EnumConfig{ + Name: "CRDTType", + Description: "One of the possible CRDT Types.", + Values: gql.EnumValueConfigMap{ + client.LWW_REGISTER.String(): &gql.EnumValueConfig{ + Value: client.LWW_REGISTER, + Description: "Last Write Wins register", + }, + client.PN_COUNTER.String(): &gql.EnumValueConfig{ + Value: client.PN_COUNTER, + Description: "Positive-Negative Counter", + }, + }, + }) + + // CRDTFieldDirective @crdt is used to define the CRDT type of a field + CRDTFieldDirective *gql.Directive = gql.NewDirective(gql.DirectiveConfig{ + Name: CRDTDirectiveLabel, + Description: crdtDirectiveDescription, + Args: gql.FieldConfigArgument{ + CRDTDirectivePropType: &gql.ArgumentConfig{ + Type: CRDTEnum, + }, + }, + Locations: []string{ + gql.DirectiveLocationField, + }, + }) + // PrimaryDirective @primary is used to indicate the primary // side of a one-to-one relationship. PrimaryDirective = gql.NewDirective(gql.DirectiveConfig{