package graphql import ( "fmt" ) type SchemaConfig struct { Query *Object Mutation *Object Subscription *Object Types []Type Directives []*Directive } type TypeMap map[string]Type // Schema Definition // A Schema is created by supplying the root types of each type of operation, // 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, // }); // 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 ]), // }) type Schema struct { typeMap TypeMap directives []*Directive queryType *Object mutationType *Object subscriptionType *Object implementations map[string][]*Object possibleTypeMap map[string]map[string]bool } func NewSchema(config SchemaConfig) (Schema, error) { var err error schema := Schema{} err = invariant(config.Query != nil, "Schema query must be Object Type but got: nil.") if err != nil { return schema, err } // if schema config contains error at creation time, return those errors if config.Query != nil && config.Query.err != nil { return schema, config.Query.err } if config.Mutation != nil && config.Mutation.err != nil { return schema, config.Mutation.err } schema.queryType = config.Query schema.mutationType = config.Mutation schema.subscriptionType = config.Subscription // Provide specified directives (e.g. @include and @skip) by default. schema.directives = config.Directives if len(schema.directives) == 0 { schema.directives = SpecifiedDirectives } // Ensure directive definitions are error-free for _, dir := range schema.directives { if dir.err != nil { return schema, dir.err } } // Build type map now to detect any errors within this schema. typeMap := TypeMap{} initialTypes := []Type{} if schema.QueryType() != nil { initialTypes = append(initialTypes, schema.QueryType()) } if schema.MutationType() != nil { initialTypes = append(initialTypes, schema.MutationType()) } if schema.SubscriptionType() != nil { initialTypes = append(initialTypes, schema.SubscriptionType()) } if SchemaType != nil { initialTypes = append(initialTypes, SchemaType) } for _, ttype := range config.Types { // assume that user will never add a nil object to config initialTypes = append(initialTypes, ttype) } for _, ttype := range initialTypes { if ttype.Error() != nil { return schema, ttype.Error() } typeMap, err = typeMapReducer(&schema, typeMap, ttype) if err != nil { return schema, err } } schema.typeMap = typeMap // Keep track of all implementations by interface name. if schema.implementations == nil { schema.implementations = map[string][]*Object{} } for _, ttype := range schema.typeMap { if ttype, ok := ttype.(*Object); ok { for _, iface := range ttype.Interfaces() { impls, ok := schema.implementations[iface.Name()] if impls == nil || !ok { impls = []*Object{} } impls = append(impls, ttype) schema.implementations[iface.Name()] = impls } } } // Enforce correct interface implementations for _, ttype := range schema.typeMap { if ttype, ok := ttype.(*Object); ok { for _, iface := range ttype.Interfaces() { err := assertObjectImplementsInterface(&schema, ttype, iface) if err != nil { return schema, err } } } } return schema, nil } //Added Check implementation of interfaces at runtime.. //Add Implementations at Runtime.. func (gq *Schema) AddImplementation() error{ // Keep track of all implementations by interface name. if gq.implementations == nil { gq.implementations = map[string][]*Object{} } for _, ttype := range gq.typeMap { if ttype, ok := ttype.(*Object); ok { for _, iface := range ttype.Interfaces() { impls, ok := gq.implementations[iface.Name()] if impls == nil || !ok { impls = []*Object{} } impls = append(impls, ttype) gq.implementations[iface.Name()] = impls } } } // Enforce correct interface implementations for _, ttype := range gq.typeMap { if ttype, ok := ttype.(*Object); ok { for _, iface := range ttype.Interfaces() { err := assertObjectImplementsInterface(gq, ttype, iface) if err != nil { return err } } } } return nil } //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() } var err error gq.typeMap, err = typeMapReducer(gq, gq.typeMap, objectType) if err != nil { return err } //Now Add interface implementation.. return gq.AddImplementation() } func (gq *Schema) QueryType() *Object { return gq.queryType } func (gq *Schema) MutationType() *Object { return gq.mutationType } func (gq *Schema) SubscriptionType() *Object { return gq.subscriptionType } func (gq *Schema) Directives() []*Directive { return gq.directives } func (gq *Schema) Directive(name string) *Directive { for _, directive := range gq.Directives() { if directive.Name == name { return directive } } return nil } func (gq *Schema) TypeMap() TypeMap { return gq.typeMap } func (gq *Schema) Type(name string) Type { return gq.TypeMap()[name] } func (gq *Schema) PossibleTypes(abstractType Abstract) []*Object { if abstractType, ok := abstractType.(*Union); ok { return abstractType.Types() } if abstractType, ok := abstractType.(*Interface); ok { if impls, ok := gq.implementations[abstractType.Name()]; ok { return impls } } return []*Object{} } func (gq *Schema) IsPossibleType(abstractType Abstract, possibleType *Object) bool { possibleTypeMap := gq.possibleTypeMap if possibleTypeMap == nil { possibleTypeMap = map[string]map[string]bool{} } if typeMap, ok := possibleTypeMap[abstractType.Name()]; !ok { typeMap = map[string]bool{} for _, possibleType := range gq.PossibleTypes(abstractType) { typeMap[possibleType.Name()] = true } possibleTypeMap[abstractType.Name()] = typeMap } gq.possibleTypeMap = possibleTypeMap if typeMap, ok := possibleTypeMap[abstractType.Name()]; ok { isPossible, _ := typeMap[possibleType.Name()] return isPossible } return false } func typeMapReducer(schema *Schema, typeMap TypeMap, objectType Type) (TypeMap, error) { var err error if objectType == nil || objectType.Name() == "" { return typeMap, nil } switch objectType := objectType.(type) { case *List: if objectType.OfType != nil { return typeMapReducer(schema, typeMap, objectType.OfType) } case *NonNull: if objectType.OfType != nil { return typeMapReducer(schema, typeMap, objectType.OfType) } case *Object: if objectType.err != nil { return typeMap, objectType.err } } if mappedObjectType, ok := typeMap[objectType.Name()]; ok { err = invariantf( mappedObjectType == objectType, `Schema must contain unique named types but contains multiple types named "%v".`, objectType.Name()) if err != nil { return typeMap, err } return typeMap, err } if objectType.Name() == "" { return typeMap, nil } typeMap[objectType.Name()] = objectType switch objectType := objectType.(type) { case *Union: types := schema.PossibleTypes(objectType) if objectType.err != nil { return typeMap, objectType.err } for _, innerObjectType := range types { if innerObjectType.err != nil { return typeMap, innerObjectType.err } typeMap, err = typeMapReducer(schema, typeMap, innerObjectType) if err != nil { return typeMap, err } } case *Interface: types := schema.PossibleTypes(objectType) if objectType.err != nil { return typeMap, objectType.err } for _, innerObjectType := range types { if innerObjectType.err != nil { return typeMap, innerObjectType.err } typeMap, err = typeMapReducer(schema, typeMap, innerObjectType) if err != nil { return typeMap, err } } case *Object: interfaces := objectType.Interfaces() if objectType.err != nil { return typeMap, objectType.err } for _, innerObjectType := range interfaces { if innerObjectType.err != nil { return typeMap, innerObjectType.err } typeMap, err = typeMapReducer(schema, typeMap, innerObjectType) if err != nil { return typeMap, err } } } switch objectType := objectType.(type) { case *Object: fieldMap := objectType.Fields() if objectType.err != nil { return typeMap, objectType.err } for _, field := range fieldMap { for _, arg := range field.Args { typeMap, err = typeMapReducer(schema, typeMap, arg.Type) if err != nil { return typeMap, err } } typeMap, err = typeMapReducer(schema, typeMap, field.Type) if err != nil { return typeMap, err } } case *Interface: fieldMap := objectType.Fields() if objectType.err != nil { return typeMap, objectType.err } for _, field := range fieldMap { for _, arg := range field.Args { typeMap, err = typeMapReducer(schema, typeMap, arg.Type) if err != nil { return typeMap, err } } typeMap, err = typeMapReducer(schema, typeMap, field.Type) if err != nil { return typeMap, err } } case *InputObject: fieldMap := objectType.Fields() if objectType.err != nil { return typeMap, objectType.err } for _, field := range fieldMap { typeMap, err = typeMapReducer(schema, typeMap, field.Type) if err != nil { return typeMap, err } } } return typeMap, nil } func assertObjectImplementsInterface(schema *Schema, object *Object, iface *Interface) error { objectFieldMap := object.Fields() ifaceFieldMap := iface.Fields() // Assert each interface field is implemented. for fieldName := range ifaceFieldMap { objectField := objectFieldMap[fieldName] ifaceField := ifaceFieldMap[fieldName] // Assert interface field exists on object. err := invariantf( objectField != nil, `"%v" expects field "%v" but "%v" does not `+ `provide it.`, iface, fieldName, object) if err != nil { return err } // Assert interface field type is satisfied by object field type, by being // a valid subtype. (covariant) err = invariant( isTypeSubTypeOf(schema, objectField.Type, ifaceField.Type), fmt.Sprintf(`%v.%v expects type "%v" but `+ `%v.%v provides type "%v".`, iface, fieldName, ifaceField.Type, object, fieldName, objectField.Type), ) if err != nil { return err } // Assert each interface field arg is implemented. for _, ifaceArg := range ifaceField.Args { argName := ifaceArg.PrivateName var objectArg *Argument for _, arg := range objectField.Args { if arg.PrivateName == argName { objectArg = arg break } } // Assert interface field arg exists on object field. err = invariant( objectArg != nil, fmt.Sprintf(`%v.%v expects argument "%v" but `+ `%v.%v does not provide it.`, iface, fieldName, argName, object, fieldName), ) if err != nil { return err } // Assert interface field arg type matches object field arg type. // (invariant) err = invariant( isEqualType(ifaceArg.Type, objectArg.Type), fmt.Sprintf( `%v.%v(%v:) expects type "%v" `+ `but %v.%v(%v:) provides `+ `type "%v".`, iface, fieldName, argName, ifaceArg.Type, object, fieldName, argName, objectArg.Type), ) if err != nil { return err } } // Assert additional arguments must not be required. for _, objectArg := range objectField.Args { argName := objectArg.PrivateName var ifaceArg *Argument for _, arg := range ifaceField.Args { if arg.PrivateName == argName { ifaceArg = arg break } } if ifaceArg == nil { _, ok := objectArg.Type.(*NonNull) err = invariant( !ok, fmt.Sprintf(`%v.%v(%v:) is of required type `+ `"%v" but is not also provided by the interface %v.%v.`, object, fieldName, argName, objectArg.Type, iface, fieldName), ) if err != nil { return err } } } } return nil } func isEqualType(typeA Type, typeB Type) bool { // Equivalent type is a valid subtype if typeA == typeB { return true } // If either type is non-null, the other must also be non-null. if typeA, ok := typeA.(*NonNull); ok { if typeB, ok := typeB.(*NonNull); ok { return isEqualType(typeA.OfType, typeB.OfType) } } // If either type is a list, the other must also be a list. if typeA, ok := typeA.(*List); ok { if typeB, ok := typeB.(*List); ok { return isEqualType(typeA.OfType, typeB.OfType) } } // Otherwise the types are not equal. return false } // isTypeSubTypeOf Provided a type and a super type, return true if the first type is either // equal or a subset of the second super type (covariant). func isTypeSubTypeOf(schema *Schema, maybeSubType Type, superType Type) bool { // Equivalent type is a valid subtype if maybeSubType == superType { return true } // If superType is non-null, maybeSubType must also be nullable. if superType, ok := superType.(*NonNull); ok { if maybeSubType, ok := maybeSubType.(*NonNull); ok { return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType) } return false } if maybeSubType, ok := maybeSubType.(*NonNull); ok { // If superType is nullable, maybeSubType may be non-null. return isTypeSubTypeOf(schema, maybeSubType.OfType, superType) } // If superType type is a list, maybeSubType type must also be a list. if superType, ok := superType.(*List); ok { if maybeSubType, ok := maybeSubType.(*List); ok { return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType) } return false } else if _, ok := maybeSubType.(*List); ok { // If superType is not a list, maybeSubType must also be not a list. return false } // If superType type is an abstract type, maybeSubType type may be a currently // possible object type. if superType, ok := superType.(*Interface); ok { if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) { return true } } if superType, ok := superType.(*Union); ok { if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) { return true } } // Otherwise, the child type is not a valid subtype of the parent type. return false }