2020-04-20 05:48:49 +02:00
package core
import (
"strings"
"github.com/chirino/graphql"
"github.com/chirino/graphql/resolvers"
"github.com/chirino/graphql/schema"
"github.com/dosco/super-graph/core/internal/psql"
)
var typeMap map [ string ] string = map [ string ] string {
"smallint" : "Int" ,
"integer" : "Int" ,
"bigint" : "Int" ,
"smallserial" : "Int" ,
"serial" : "Int" ,
"bigserial" : "Int" ,
"decimal" : "Float" ,
"numeric" : "Float" ,
"real" : "Float" ,
"double precision" : "Float" ,
"money" : "Float" ,
"boolean" : "Boolean" ,
}
2020-04-23 02:51:14 +02:00
func ( sg * SuperGraph ) initGraphQLEgine ( ) error {
2020-04-21 16:03:05 +02:00
engine := graphql . New ( )
engineSchema := engine . Schema
2020-04-20 05:48:49 +02:00
dbSchema := sg . schema
2020-05-01 08:20:13 +02:00
if err := engineSchema . Parse ( ` enum OrderDirection { asc desc } ` ) ; err != nil {
return err
}
2020-04-20 05:48:49 +02:00
gqltype := func ( col psql . DBColumn ) schema . Type {
typeName := typeMap [ strings . ToLower ( col . Type ) ]
if typeName == "" {
typeName = "String"
}
2020-05-01 08:03:35 +02:00
var t schema . Type = & schema . TypeName { Name : typeName }
2020-04-20 05:48:49 +02:00
if col . NotNull {
t = & schema . NonNull { OfType : t }
}
return t
}
query := & schema . Object {
Name : "Query" ,
Fields : schema . FieldList { } ,
}
mutation := & schema . Object {
Name : "Mutation" ,
Fields : schema . FieldList { } ,
}
engineSchema . Types [ query . Name ] = query
engineSchema . Types [ mutation . Name ] = mutation
engineSchema . EntryPoints [ schema . Query ] = query
engineSchema . EntryPoints [ schema . Mutation ] = mutation
2020-04-23 02:51:14 +02:00
//validGraphQLIdentifierRegex := regexp.MustCompile(`^[A-Za-z_][A-Za-z_0-9]*$`)
2020-04-21 16:03:05 +02:00
scalarExpressionTypesNeeded := map [ string ] bool { }
2020-04-20 05:48:49 +02:00
tableNames := dbSchema . GetTableNames ( )
2020-04-23 02:51:14 +02:00
funcs := dbSchema . GetFunctions ( )
2020-04-20 05:48:49 +02:00
2020-04-23 02:51:14 +02:00
for _ , table := range tableNames {
2020-04-20 05:48:49 +02:00
ti , err := dbSchema . GetTable ( table )
if err != nil {
2020-04-23 02:51:14 +02:00
return err
2020-04-20 05:48:49 +02:00
}
if ! ti . IsSingular {
continue
}
2020-04-21 16:03:05 +02:00
singularName := ti . Singular
2020-04-23 02:51:14 +02:00
// if !validGraphQLIdentifierRegex.MatchString(singularName) {
// return errors.New("table name is not a valid GraphQL identifier: " + singularName)
// }
2020-04-21 16:03:05 +02:00
pluralName := ti . Plural
2020-04-23 02:51:14 +02:00
// if !validGraphQLIdentifierRegex.MatchString(pluralName) {
// return errors.New("table name is not a valid GraphQL identifier: " + pluralName)
// }
2020-04-20 05:48:49 +02:00
outputType := & schema . Object {
2020-04-21 16:03:05 +02:00
Name : singularName + "Output" ,
2020-04-20 05:48:49 +02:00
Fields : schema . FieldList { } ,
}
2020-04-21 16:03:05 +02:00
engineSchema . Types [ outputType . Name ] = outputType
2020-04-20 05:48:49 +02:00
inputType := & schema . InputObject {
2020-04-21 16:03:05 +02:00
Name : singularName + "Input" ,
2020-04-20 05:48:49 +02:00
Fields : schema . InputValueList { } ,
}
2020-04-21 16:03:05 +02:00
engineSchema . Types [ inputType . Name ] = inputType
orderByType := & schema . InputObject {
Name : singularName + "OrderBy" ,
Fields : schema . InputValueList { } ,
}
engineSchema . Types [ orderByType . Name ] = orderByType
expressionTypeName := singularName + "Expression"
expressionType := & schema . InputObject {
Name : expressionTypeName ,
Fields : schema . InputValueList {
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "and" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : expressionTypeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "or" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : expressionTypeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "not" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : expressionTypeName } } ,
2020-04-21 16:03:05 +02:00
} ,
} ,
}
engineSchema . Types [ expressionType . Name ] = expressionType
2020-04-20 05:48:49 +02:00
for _ , col := range ti . Columns {
2020-04-21 16:03:05 +02:00
colName := col . Name
2020-04-23 02:51:14 +02:00
// if !validGraphQLIdentifierRegex.MatchString(colName) {
// return errors.New("column name is not a valid GraphQL identifier: " + colName)
// }
2020-04-21 16:03:05 +02:00
colType := gqltype ( col )
nullableColType := ""
if x , ok := colType . ( * schema . NonNull ) ; ok {
2020-05-01 08:03:35 +02:00
nullableColType = x . OfType . ( * schema . TypeName ) . Name
2020-04-21 16:03:05 +02:00
} else {
2020-05-01 08:03:35 +02:00
nullableColType = colType . ( * schema . TypeName ) . Name
2020-04-21 16:03:05 +02:00
}
2020-04-20 05:48:49 +02:00
outputType . Fields = append ( outputType . Fields , & schema . Field {
2020-04-21 16:03:05 +02:00
Name : colName ,
Type : colType ,
2020-04-20 05:48:49 +02:00
} )
2020-04-21 16:03:05 +02:00
2020-04-23 02:51:14 +02:00
for _ , f := range funcs {
if col . Type != f . Params [ 0 ] . Type {
continue
}
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : f . Name + "_" + colName ,
Type : colType ,
} )
}
2020-04-21 16:03:05 +02:00
// If it's a numeric type...
if nullableColType == "Float" || nullableColType == "Int" {
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "avg_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "count_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "max_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "min_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "stddev_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "stddev_pop_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "stddev_samp_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "variance_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "var_pop_" + colName ,
Type : colType ,
} )
outputType . Fields = append ( outputType . Fields , & schema . Field {
Name : "var_samp_" + colName ,
Type : colType ,
} )
}
2020-04-20 05:48:49 +02:00
inputType . Fields = append ( inputType . Fields , & schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : colName ,
2020-04-21 16:03:05 +02:00
Type : colType ,
} )
orderByType . Fields = append ( orderByType . Fields , & schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : colName ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "OrderDirection" } } ,
2020-04-20 05:48:49 +02:00
} )
2020-04-21 16:03:05 +02:00
scalarExpressionTypesNeeded [ nullableColType ] = true
expressionType . Fields = append ( expressionType . Fields , & schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : colName ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : nullableColType + "Expression" } } ,
2020-04-21 16:03:05 +02:00
} )
}
2020-04-20 05:48:49 +02:00
2020-05-01 08:03:35 +02:00
outputTypeName := & schema . TypeName { Name : outputType . Name }
inputTypeName := & schema . TypeName { Name : inputType . Name }
pluralOutputTypeName := & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : outputType . Name } } } }
pluralInputTypeName := & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : inputType . Name } } } }
2020-04-20 05:48:49 +02:00
2020-04-21 16:03:05 +02:00
args := schema . InputValueList {
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "To sort or ordering results just use the order_by argument. This can be combined with where, search, etc to build complex queries to fit you needs." } ,
Name : "order_by" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : orderByType . Name } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "where" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : expressionType . Name } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "limit" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "Int" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "offset" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "Int" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "first" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "Int" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "last" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "Int" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "before" ,
Type : & schema . TypeName { Name : "String" } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "after" ,
Type : & schema . TypeName { Name : "String" } ,
2020-04-21 16:03:05 +02:00
} ,
}
2020-04-20 05:48:49 +02:00
if ti . PrimaryCol != nil {
t := gqltype ( * ti . PrimaryCol )
if _ , ok := t . ( * schema . NonNull ) ; ! ok {
t = & schema . NonNull { OfType : t }
}
2020-04-21 16:03:05 +02:00
args = append ( args , & schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "Finds the record by the primary key" } ,
Name : "id" ,
2020-04-20 05:48:49 +02:00
Type : t ,
2020-04-21 16:03:05 +02:00
} )
}
if ti . TSVCol != nil {
args = append ( args , & schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "Performs full text search using a TSV index" } ,
Name : "search" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} )
2020-04-20 05:48:49 +02:00
}
query . Fields = append ( query . Fields , & schema . Field {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
2020-04-21 16:03:05 +02:00
Name : singularName ,
2020-04-20 05:48:49 +02:00
Type : outputTypeName ,
Args : args ,
} )
query . Fields = append ( query . Fields , & schema . Field {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
2020-04-21 16:03:05 +02:00
Name : pluralName ,
2020-04-20 05:48:49 +02:00
Type : pluralOutputTypeName ,
Args : args ,
} )
2020-04-21 16:41:28 +02:00
mutationArgs := append ( args , schema . InputValueList {
2020-04-21 16:03:05 +02:00
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "insert" ,
2020-04-21 16:03:05 +02:00
Type : inputTypeName ,
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "update" ,
2020-04-21 16:03:05 +02:00
Type : inputTypeName ,
} ,
2020-04-21 16:41:28 +02:00
2020-04-21 16:03:05 +02:00
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "upsert" ,
2020-04-21 16:03:05 +02:00
Type : inputTypeName ,
} ,
2020-04-21 16:41:28 +02:00
} ... )
2020-04-20 05:48:49 +02:00
mutation . Fields = append ( mutation . Fields , & schema . Field {
2020-04-21 16:03:05 +02:00
Name : singularName ,
2020-04-21 16:41:28 +02:00
Args : mutationArgs ,
2020-04-21 16:03:05 +02:00
Type : outputType ,
} )
mutation . Fields = append ( mutation . Fields , & schema . Field {
Name : pluralName ,
2020-04-21 16:41:28 +02:00
Args : append ( mutationArgs , schema . InputValueList {
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "inserts" ,
2020-04-21 16:41:28 +02:00
Type : pluralInputTypeName ,
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "updates" ,
2020-04-21 16:41:28 +02:00
Type : pluralInputTypeName ,
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Desc : schema . Description { Text : "" } ,
Name : "upserts" ,
2020-04-21 16:41:28 +02:00
Type : pluralInputTypeName ,
} ,
} ... ) ,
2020-04-21 16:03:05 +02:00
Type : outputType ,
} )
}
2020-05-01 08:20:13 +02:00
for typeName := range scalarExpressionTypesNeeded {
2020-04-21 16:03:05 +02:00
expressionType := & schema . InputObject {
Name : typeName + "Expression" ,
Fields : schema . InputValueList {
2020-04-20 05:48:49 +02:00
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "eq" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-20 05:48:49 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "equals" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-20 05:48:49 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "neq" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-20 05:48:49 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "not_equals" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "gt" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "greater_than" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "lt" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "lesser_than" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "gte" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "greater_or_equals" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "lte" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "lesser_or_equals" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "in" ,
Type : & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "nin" ,
Type : & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "not_in" ,
Type : & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } } } ,
2020-04-20 05:48:49 +02:00
} ,
2020-04-21 16:03:05 +02:00
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "like" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "nlike" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "not_like" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "ilike" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "nilike" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "not_ilike" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "similar" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "nsimilar" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "not_similar" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "has_key" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "has_key_any" ,
Type : & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "has_key_all" ,
Type : & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "contains" ,
Type : & schema . NonNull { OfType : & schema . List { OfType : & schema . NonNull { OfType : & schema . TypeName { Name : typeName } } } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "contained_in" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "String" } } ,
2020-04-21 16:03:05 +02:00
} ,
& schema . InputValue {
2020-05-01 08:03:35 +02:00
Name : "is_null" ,
Type : & schema . NonNull { OfType : & schema . TypeName { Name : "Boolean" } } ,
2020-04-21 16:03:05 +02:00
} ,
} ,
}
engineSchema . Types [ expressionType . Name ] = expressionType
2020-04-20 05:48:49 +02:00
}
2020-04-21 16:03:05 +02:00
2020-05-01 08:20:13 +02:00
if err := engineSchema . ResolveTypes ( ) ; err != nil {
2020-04-23 02:51:14 +02:00
return err
2020-04-20 05:48:49 +02:00
}
2020-04-21 16:03:05 +02:00
engine . Resolver = resolvers . Func ( func ( request * resolvers . ResolveRequest , next resolvers . Resolution ) resolvers . Resolution {
2020-04-20 05:48:49 +02:00
resolver := resolvers . MetadataResolver . Resolve ( request , next )
if resolver != nil {
return resolver
}
resolver = resolvers . MethodResolver . Resolve ( request , next ) // needed by the MetadataResolver
if resolver != nil {
return resolver
}
return nil
} )
2020-04-23 02:51:14 +02:00
sg . ge = engine
return nil
2020-04-20 05:48:49 +02:00
}