diff --git a/definition.go b/definition.go index e37fa077..531583d5 100644 --- a/definition.go +++ b/definition.go @@ -193,16 +193,16 @@ func GetNamed(ttype Type) Named { // // Example: // -// var OddType = new Scalar({ -// name: 'Odd', -// serialize(value) { -// return value % 2 === 1 ? value : null; -// } -// }); -// +// var OddType = new Scalar({ +// name: 'Odd', +// serialize(value) { +// return value % 2 === 1 ? value : null; +// } +// }); type Scalar struct { - PrivateName string `json:"name"` - PrivateDescription string `json:"description"` + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` scalarConfig ScalarConfig err error @@ -219,11 +219,12 @@ type ParseLiteralFn func(valueAST ast.Value) interface{} // ScalarConfig options for creating a new GraphQLScalar type ScalarConfig struct { - Name string `json:"name"` - Description string `json:"description"` - Serialize SerializeFn - ParseValue ParseValueFn - ParseLiteral ParseLiteralFn + Name string `json:"name"` + Description string `json:"description"` + Serialize SerializeFn + ParseValue ParseValueFn + ParseLiteral ParseLiteralFn + AppliedDirectives []*AppliedDirective } // NewScalar creates a new GraphQLScalar @@ -243,6 +244,7 @@ func NewScalar(config ScalarConfig) *Scalar { st.PrivateName = config.Name st.PrivateDescription = config.Description + st.AppliedDirectives = config.AppliedDirectives err = invariantf( config.Serialize != nil, @@ -306,19 +308,19 @@ func (st *Scalar) Error() error { // have a name, but most importantly describe their fields. // Example: // -// var AddressType = new Object({ -// name: 'Address', -// fields: { -// street: { type: String }, -// number: { type: Int }, -// formatted: { -// type: String, -// resolve(obj) { -// return obj.number + ' ' + obj.street -// } -// } -// } -// }); +// var AddressType = new Object({ +// name: 'Address', +// fields: { +// street: { type: String }, +// number: { type: Int }, +// formatted: { +// type: String, +// resolve(obj) { +// return obj.number + ' ' + obj.street +// } +// } +// } +// }); // // When two types need to refer to each other, or a type needs to refer to // itself in a field, you can use a function expression (aka a closure or a @@ -326,19 +328,20 @@ func (st *Scalar) Error() error { // // Example: // -// var PersonType = new Object({ -// name: 'Person', -// fields: () => ({ -// name: { type: String }, -// bestFriend: { type: PersonType }, -// }) -// }); +// var PersonType = new Object({ +// name: 'Person', +// fields: () => ({ +// name: { type: String }, +// bestFriend: { type: PersonType }, +// }) +// }); // // / type Object struct { PrivateName string `json:"name"` PrivateDescription string `json:"description"` IsTypeOf IsTypeOfFn + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` typeConfig ObjectConfig initialisedFields bool @@ -369,11 +372,12 @@ type IsTypeOfFn func(p IsTypeOfParams) bool type InterfacesThunk func() []*Interface type ObjectConfig struct { - Name string `json:"name"` - Interfaces interface{} `json:"interfaces"` - Fields interface{} `json:"fields"` - IsTypeOf IsTypeOfFn `json:"isTypeOf"` - Description string `json:"description"` + Name string `json:"name"` + Interfaces interface{} `json:"interfaces"` + Fields interface{} `json:"fields"` + IsTypeOf IsTypeOfFn `json:"isTypeOf"` + Description string `json:"description"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` } type FieldsThunk func() Fields @@ -395,6 +399,7 @@ func NewObject(config ObjectConfig) *Object { objectType.PrivateName = config.Name objectType.PrivateDescription = config.Description objectType.IsTypeOf = config.IsTypeOf + objectType.AppliedDirectives = config.AppliedDirectives objectType.typeConfig = config return objectType @@ -536,6 +541,7 @@ func defineFieldMap(ttype Named, fieldMap Fields) (FieldDefinitionMap, error) { Resolve: field.Resolve, Subscribe: field.Subscribe, DeprecationReason: field.DeprecationReason, + AppliedDirectives: field.AppliedDirectives, } fieldDef.Args = []*Argument{} @@ -610,39 +616,44 @@ type Field struct { Subscribe FieldResolveFn `json:"-"` DeprecationReason string `json:"deprecationReason"` Description string `json:"description"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` } type FieldConfigArgument map[string]*ArgumentConfig type ArgumentConfig struct { - Type Input `json:"type"` - DefaultValue interface{} `json:"defaultValue"` - Description string `json:"description"` + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + Description string `json:"description"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` } type FieldDefinitionMap map[string]*FieldDefinition type FieldDefinition struct { - Name string `json:"name"` - Description string `json:"description"` - Type Output `json:"type"` - Args []*Argument `json:"args"` - Resolve FieldResolveFn `json:"-"` - Subscribe FieldResolveFn `json:"-"` - DeprecationReason string `json:"deprecationReason"` + Name string `json:"name"` + Description string `json:"description"` + Type Output `json:"type"` + Args []*Argument `json:"args"` + Resolve FieldResolveFn `json:"-"` + Subscribe FieldResolveFn `json:"-"` + DeprecationReason string `json:"deprecationReason"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` } type FieldArgument struct { - Name string `json:"name"` - Type Type `json:"type"` - DefaultValue interface{} `json:"defaultValue"` - Description string `json:"description"` + Name string `json:"name"` + Type Type `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + Description string `json:"description"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` } type Argument struct { - PrivateName string `json:"name"` - Type Input `json:"type"` - DefaultValue interface{} `json:"defaultValue"` - PrivateDescription string `json:"description"` + PrivateName string `json:"name"` + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + PrivateDescription string `json:"description"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` } func (st *Argument) Name() string { @@ -659,6 +670,19 @@ func (st *Argument) Error() error { return nil } +type AppliedDirectiveConfigArgument map[string]*AppliedDirectiveArgumentConfig + +type AppliedDirectiveArgumentConfig struct { + Type Type `json:"type"` + Value interface{} `json:"value"` + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` +} + +type AppliedDirectiveArgument struct { + Name string `json:"name"` + Value interface{} `json:"value"` +} + // Interface Type Definition // // When a field can return one of a heterogeneous set of types, a Interface type @@ -668,18 +692,17 @@ func (st *Argument) Error() error { // // Example: // -// var EntityType = new Interface({ -// name: 'Entity', -// fields: { -// name: { type: String } -// } -// }); -// -// +// var EntityType = new Interface({ +// name: 'Entity', +// fields: { +// name: { type: String } +// } +// }); type Interface struct { PrivateName string `json:"name"` PrivateDescription string `json:"description"` ResolveType ResolveTypeFn + AppliedDirectives []*AppliedDirective `json:"appliedDirectives"` typeConfig InterfaceConfig initialisedFields bool @@ -687,10 +710,11 @@ type Interface struct { err error } type InterfaceConfig struct { - Name string `json:"name"` - Fields interface{} `json:"fields"` - ResolveType ResolveTypeFn - Description string `json:"description"` + Name string `json:"name"` + Fields interface{} `json:"fields"` + ResolveType ResolveTypeFn + Description string `json:"description"` + AppliedDirectives []*AppliedDirective } // ResolveTypeParams Params for ResolveTypeFn() @@ -722,6 +746,7 @@ func NewInterface(config InterfaceConfig) *Interface { it.PrivateName = config.Name it.PrivateDescription = config.Description it.ResolveType = config.ResolveType + it.AppliedDirectives = config.AppliedDirectives it.typeConfig = config return it @@ -779,22 +804,23 @@ func (it *Interface) Error() error { // // Example: // -// var PetType = new Union({ -// name: 'Pet', -// types: [ DogType, CatType ], -// resolveType(value) { -// if (value instanceof Dog) { -// return DogType; -// } -// if (value instanceof Cat) { -// return CatType; -// } -// } -// }); +// var PetType = new Union({ +// name: 'Pet', +// types: [ DogType, CatType ], +// resolveType(value) { +// if (value instanceof Dog) { +// return DogType; +// } +// if (value instanceof Cat) { +// return CatType; +// } +// } +// }); type Union struct { PrivateName string `json:"name"` PrivateDescription string `json:"description"` ResolveType ResolveTypeFn + AppliedDirectives []*AppliedDirective typeConfig UnionConfig initalizedTypes bool @@ -807,10 +833,11 @@ type Union struct { type UnionTypesThunk func() []*Object type UnionConfig struct { - Name string `json:"name"` - Types interface{} `json:"types"` - ResolveType ResolveTypeFn - Description string `json:"description"` + Name string `json:"name"` + Types interface{} `json:"types"` + ResolveType ResolveTypeFn + Description string `json:"description"` + AppliedDirectives []*AppliedDirective } func NewUnion(config UnionConfig) *Union { @@ -825,6 +852,7 @@ func NewUnion(config UnionConfig) *Union { objectType.PrivateName = config.Name objectType.PrivateDescription = config.Description objectType.ResolveType = config.ResolveType + objectType.AppliedDirectives = config.AppliedDirectives objectType.typeConfig = config @@ -927,6 +955,7 @@ func (ut *Union) Error() error { type Enum struct { PrivateName string `json:"name"` PrivateDescription string `json:"description"` + AppliedDirectives []*AppliedDirective enumConfig EnumConfig values []*EnumValueDefinition @@ -940,17 +969,20 @@ type EnumValueConfig struct { Value interface{} `json:"value"` DeprecationReason string `json:"deprecationReason"` Description string `json:"description"` + AppliedDirectives []*AppliedDirective } type EnumConfig struct { - Name string `json:"name"` - Values EnumValueConfigMap `json:"values"` - Description string `json:"description"` + Name string `json:"name"` + Values EnumValueConfigMap `json:"values"` + Description string `json:"description"` + AppliedDirectives []*AppliedDirective } type EnumValueDefinition struct { Name string `json:"name"` Value interface{} `json:"value"` DeprecationReason string `json:"deprecationReason"` Description string `json:"description"` + AppliedDirectives []*AppliedDirective } func NewEnum(config EnumConfig) *Enum { @@ -963,6 +995,8 @@ func NewEnum(config EnumConfig) *Enum { gt.PrivateName = config.Name gt.PrivateDescription = config.Description + gt.AppliedDirectives = config.AppliedDirectives + if gt.values, gt.err = gt.defineEnumValues(config.Values); gt.err != nil { return gt } @@ -996,6 +1030,7 @@ func (gt *Enum) defineEnumValues(valueMap EnumValueConfigMap) ([]*EnumValueDefin Value: valueConfig.Value, DeprecationReason: valueConfig.DeprecationReason, Description: valueConfig.Description, + AppliedDirectives: valueConfig.AppliedDirectives, } if value.Value == nil { value.Value = valueName @@ -1085,21 +1120,22 @@ func (gt *Enum) getNameLookup() map[string]*EnumValueDefinition { // An input object defines a structured collection of fields which may be // supplied to a field argument. // -// Using `NonNull` will ensure that a value must be provided by the query +// # Using `NonNull` will ensure that a value must be provided by the query // // Example: // -// var GeoPoint = new InputObject({ -// name: 'GeoPoint', -// fields: { -// lat: { type: new NonNull(Float) }, -// lon: { type: new NonNull(Float) }, -// alt: { type: Float, defaultValue: 0 }, -// } -// }); +// var GeoPoint = new InputObject({ +// name: 'GeoPoint', +// fields: { +// lat: { type: new NonNull(Float) }, +// lon: { type: new NonNull(Float) }, +// alt: { type: Float, defaultValue: 0 }, +// } +// }); type InputObject struct { PrivateName string `json:"name"` PrivateDescription string `json:"description"` + AppliedDirectives []*AppliedDirective typeConfig InputObjectConfig fields InputObjectFieldMap @@ -1107,15 +1143,17 @@ type InputObject struct { err error } type InputObjectFieldConfig struct { - Type Input `json:"type"` - DefaultValue interface{} `json:"defaultValue"` - Description string `json:"description"` + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + Description string `json:"description"` + AppliedDirectives []*AppliedDirective } type InputObjectField struct { PrivateName string `json:"name"` Type Input `json:"type"` DefaultValue interface{} `json:"defaultValue"` PrivateDescription string `json:"description"` + AppliedDirectives []*AppliedDirective } func (st *InputObjectField) Name() string { @@ -1135,9 +1173,10 @@ type InputObjectConfigFieldMap map[string]*InputObjectFieldConfig type InputObjectFieldMap map[string]*InputObjectField type InputObjectConfigFieldMapThunk func() InputObjectConfigFieldMap type InputObjectConfig struct { - Name string `json:"name"` - Fields interface{} `json:"fields"` - Description string `json:"description"` + Name string `json:"name"` + Fields interface{} `json:"fields"` + Description string `json:"description"` + AppliedDirectives []*AppliedDirective } func NewInputObject(config InputObjectConfig) *InputObject { @@ -1148,6 +1187,7 @@ func NewInputObject(config InputObjectConfig) *InputObject { gt.PrivateName = config.Name gt.PrivateDescription = config.Description + gt.AppliedDirectives = config.AppliedDirectives gt.typeConfig = config return gt } @@ -1190,6 +1230,7 @@ func (gt *InputObject) defineFieldMap() InputObjectFieldMap { field.Type = fieldConfig.Type field.PrivateDescription = fieldConfig.Description field.DefaultValue = fieldConfig.DefaultValue + field.AppliedDirectives = fieldConfig.AppliedDirectives resultFieldMap[fieldName] = field } gt.init = true @@ -1235,14 +1276,13 @@ func (gt *InputObject) Error() error { // // Example: // -// var PersonType = new Object({ -// name: 'Person', -// fields: () => ({ -// parents: { type: new List(Person) }, -// children: { type: new List(Person) }, -// }) -// }) -// +// var PersonType = new Object({ +// name: 'Person', +// fields: () => ({ +// parents: { type: new List(Person) }, +// children: { type: new List(Person) }, +// }) +// }) type List struct { OfType Type `json:"ofType"` @@ -1286,12 +1326,12 @@ func (gl *List) Error() error { // // Example: // -// var RowType = new Object({ -// name: 'Row', -// fields: () => ({ -// id: { type: new NonNull(String) }, -// }) -// }) +// var RowType = new Object({ +// name: 'Row', +// fields: () => ({ +// id: { type: new NonNull(String) }, +// }) +// }) // // Note: the enforcement of non-nullability occurs within the executor. type NonNull struct { diff --git a/directives.go b/directives.go index e31a2f5a..82ffb5cb 100644 --- a/directives.go +++ b/directives.go @@ -41,6 +41,7 @@ type Directive struct { Description string `json:"description"` Locations []string `json:"locations"` Args []*Argument `json:"args"` + Repeatable bool `json:"repeatable"` err error } @@ -51,6 +52,7 @@ type DirectiveConfig struct { Description string `json:"description"` Locations []string `json:"locations"` Args FieldConfigArgument `json:"args"` + Repeatable bool `json:"repeatable"` } func NewDirective(config DirectiveConfig) *Directive { @@ -89,6 +91,51 @@ func NewDirective(config DirectiveConfig) *Directive { dir.Description = config.Description dir.Locations = config.Locations dir.Args = args + dir.Repeatable = config.Repeatable + return dir +} + +// Directive instance that is applied to a target location (e.g. field) within a schema. +type AppliedDirective struct { + Name string `json:"name"` + Args []*AppliedDirectiveArgument `json:"args"` + + err error +} + +// AppliedDirectiveConfig options for creating new AppliedDirective +type AppliedDirectiveConfig struct { + Name string `json:"name"` + Args AppliedDirectiveConfigArgument `json:"args"` +} + +func NewAppliedDirective(config AppliedDirectiveConfig) *AppliedDirective { + dir := &AppliedDirective{} + + // Ensure directive is named + if dir.err = invariant(config.Name != "", "Directive must be named."); dir.err != nil { + return dir + } + + // Ensure directive name is valid + if dir.err = assertValidName(config.Name); dir.err != nil { + return dir + } + + args := []*AppliedDirectiveArgument{} + + for argName, argConfig := range config.Args { + if dir.err = assertValidName(argName); dir.err != nil { + return dir + } + args = append(args, &AppliedDirectiveArgument{ + Name: argName, + Value: argConfig.Value, + }) + } + + dir.Name = config.Name + dir.Args = args return dir } diff --git a/schema.go b/schema.go index 35519ac4..821438cf 100644 --- a/schema.go +++ b/schema.go @@ -1,12 +1,13 @@ package graphql type SchemaConfig struct { - Query *Object - Mutation *Object - Subscription *Object - Types []Type - Directives []*Directive - Extensions []Extension + Query *Object + Mutation *Object + Subscription *Object + Types []Type + Directives []*Directive + AppliedDirectives []*AppliedDirective + Extensions []Extension } type TypeMap map[string]Type @@ -16,24 +17,27 @@ type TypeMap map[string]Type // query, mutation (optional) and subscription (optional). A schema definition is then supplied to the // validator and executor. // Example: -// myAppSchema, err := NewSchema(SchemaConfig({ -// Query: MyAppQueryRootType, -// Mutation: MyAppMutationRootType, -// Subscription: MyAppSubscriptionRootType, -// }); +// +// myAppSchema, err := NewSchema(SchemaConfig({ +// Query: MyAppQueryRootType, +// Mutation: MyAppMutationRootType, +// Subscription: MyAppSubscriptionRootType, +// }); +// // Note: If an array of `directives` are provided to GraphQLSchema, that will be // the exact list of directives represented and allowed. If `directives` is not // provided then a default set of the specified directives (e.g. @include and // @skip) will be used. If you wish to provide *additional* directives to these // specified directives, you must explicitly declare them. Example: // -// const MyAppSchema = new GraphQLSchema({ -// ... -// directives: specifiedDirectives.concat([ myCustomDirective ]), -// }) +// const MyAppSchema = new GraphQLSchema({ +// ... +// directives: specifiedDirectives.concat([ myCustomDirective ]), +// }) type Schema struct { - typeMap TypeMap - directives []*Directive + typeMap TypeMap + directives []*Directive + appliedDirectives []*AppliedDirective queryType *Object mutationType *Object @@ -76,6 +80,8 @@ func NewSchema(config SchemaConfig) (Schema, error) { } } + schema.appliedDirectives = config.AppliedDirectives + // Build type map now to detect any errors within this schema. typeMap := TypeMap{} initialTypes := []Type{} @@ -145,8 +151,8 @@ func NewSchema(config SchemaConfig) (Schema, error) { return schema, nil } -//Added Check implementation of interfaces at runtime.. -//Add Implementations at Runtime.. +// Added Check implementation of interfaces at runtime.. +// Add Implementations at Runtime.. func (gq *Schema) AddImplementation() error { // Keep track of all implementations by interface name. @@ -181,8 +187,8 @@ func (gq *Schema) AddImplementation() error { return nil } -//Edited. To check add Types at RunTime.. -//Append Runtime schema to typeMap +// Edited. To check add Types at RunTime.. +// Append Runtime schema to typeMap func (gq *Schema) AppendType(objectType Type) error { if objectType.Error() != nil { return objectType.Error() @@ -543,3 +549,12 @@ func isTypeSubTypeOf(schema *Schema, maybeSubType Type, superType Type) bool { // Otherwise, the child type is not a valid subtype of the parent type. return false } + +func (gq *Schema) AppendAppliedDirective(appliedDirectiveType AppliedDirective) error { + gq.appliedDirectives = append(gq.appliedDirectives, &appliedDirectiveType) + return nil +} + +func (gq *Schema) AppliedDirectives() []*AppliedDirective { + return gq.appliedDirectives +}