github.com/dennys-bd/goals@v0.0.0-20210328114421-251a004d41e3/core/core.go (about) 1 /*Package core create your graphql server 2 3 This package has functions to mount the schema 4 and server your application, authenticate, 5 serve static file, etc.*/ 6 package core 7 8 import ( 9 "fmt" 10 "log" 11 "net/http" 12 "os" 13 "reflect" 14 "strconv" 15 "strings" 16 "time" 17 18 "github.com/dennys-bd/goals/graphql" 19 "github.com/dennys-bd/goals/graphql/relay" 20 errs "github.com/dennys-bd/goals/shortcuts/errors" 21 "github.com/rs/cors" 22 ) 23 24 var registers []register 25 26 // Schema definition 27 type Schema struct { 28 name string 29 schema string 30 } 31 32 type register struct { 33 endpoint string 34 schema Schema 35 resolver interface{} 36 opt []graphql.SchemaOpt 37 } 38 39 // RegisterSchema register your schema to a resolver in an endpoint 40 func RegisterSchema(endpoint string, schema Schema, resolver interface{}, opt ...graphql.SchemaOpt) { 41 if endpoint == "/" { 42 endpoint = "" 43 } 44 45 r := register{ 46 endpoint: endpoint, 47 schema: schema, 48 resolver: resolver, 49 } 50 51 registers = append(registers, r) 52 } 53 54 /* RegisterPrivateSchema register your private schema to a resolver in an endpoint 55 56 RegisterPrivateSchema only calls RegisterSchema, but you may to use it if you want 57 to garantee that your resolver is a PrivateResolver and you have a closed Schema. */ 58 func RegisterPrivateSchema(endpoint string, schema Schema, resolver graphql.PrivateResolver, opt ...graphql.SchemaOpt) { 59 RegisterSchema(endpoint, schema, resolver, opt...) 60 } 61 62 // Server is user to run your goals application, 63 // User It after registering yours schemas. 64 func Server() { 65 project, corsOptions := recreateProjectFromGoals() 66 getRunServerFlags(&project) 67 68 for _, reg := range registers { 69 if os.Getenv("GOALS_ENV") == "development" || project.Config.Graphiql { 70 innerPage := fmt.Sprintf(page, reg.endpoint) 71 http.Handle(reg.endpoint+"/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 72 w.Write([]byte(innerPage)) 73 })) 74 } 75 76 schema := graphql.MustParseSchema(reg.schema.schema, reg.resolver) 77 if corsOptions != nil { 78 c := cors.New(*corsOptions) 79 if res, ok := reg.resolver.(graphql.PrivateResolver); ok { 80 http.Handle("/graphql"+reg.endpoint, c.Handler(injectAuthToContext(&relay.Handler{Schema: schema}, res.GetAuthHeaders()...))) 81 } else { 82 http.Handle("/graphql"+reg.endpoint, c.Handler(&relay.Handler{Schema: schema})) 83 } 84 } else { 85 if res, ok := reg.resolver.(graphql.PrivateResolver); ok { 86 http.Handle("/graphql"+reg.endpoint, injectAuthToContext(&relay.Handler{Schema: schema}, res.GetAuthHeaders()...)) 87 } else { 88 http.Handle("/graphql"+reg.endpoint, &relay.Handler{Schema: schema}) 89 } 90 } 91 } 92 go printServers(project.Config) 93 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", project.Config.Port), nil)) 94 } 95 96 func getRunServerFlags(p *Project) { 97 if len(os.Args) > 1 { 98 for _, arg := range os.Args { 99 if arg == "verbose" { 100 p.Config.verbose = true 101 } else if strings.HasPrefix(arg, "PORT=") { 102 s := strings.Split(arg, "=") 103 if len(s) == 1 { 104 errs.Ex("You are trying to start with incorrect port configuration.") 105 } 106 if port, err := strconv.Atoi(s[1]); err != nil { 107 errs.Ex(fmt.Sprintf("Your port should be a number. We received: %s", s[1])) 108 } else { 109 p.Config.Port = port 110 } 111 } 112 } 113 } 114 } 115 116 func printServers(config config) { 117 time.Sleep(500 * time.Millisecond) 118 fmt.Printf("-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-\n") 119 for _, reg := range registers { 120 fmt.Printf("%s is registered at: http://localhost:%d/graphql%s\nWith the resolver: %s\n", 121 reg.schema.name, config.Port, reg.endpoint, reflect.TypeOf(reg.resolver).Elem()) 122 if os.Getenv("GOALS_ENV") == "development" || config.Graphiql { 123 fmt.Printf("You can visit it's GraphiQL page on http://localhost:%d%s\n", config.Port, reg.endpoint) 124 } 125 if config.verbose { 126 fmt.Printf("Check the schema:\n`%s`\n", reg.schema.schema) 127 } 128 fmt.Printf("-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-=x=-\n") 129 time.Sleep(500 * time.Millisecond) 130 } 131 println("Your server is running, press ctrl+c to stop it.") 132 } 133 134 // MountSchema from params 135 func MountSchema(name, types, queries, mutations, subscriptions, scalars string) Schema { 136 schemaDefinition := "schema {\n" 137 if queries != "" { 138 schemaDefinition += " query: Query\n" 139 } else { 140 errs.Ex(fmt.Sprintf("Your query can't be empty. Error in your schema: \"%s\"\n", name)) 141 } 142 if mutations != "" { 143 schemaDefinition += " mutation: Mutation\n" 144 } 145 if subscriptions != "" { 146 schemaDefinition += " subscription: Subscription\n" 147 } 148 schemaDefinition += "}\n" 149 150 schema := schemaDefinition 151 if queries != "" { 152 q := "type Query {" + queries + "}\n" 153 schema += q 154 } 155 if mutations != "" { 156 m := "type Mutation {" + mutations + "}\n" 157 schema += m 158 } 159 if subscriptions != "" { 160 s := "type Subscription {" + subscriptions + "}\n" 161 schema += s 162 } 163 if scalars != "" { 164 schema += scalars 165 } 166 if types != "" { 167 schema += types 168 } 169 170 s := Schema{ 171 name: name, 172 schema: schema, 173 } 174 175 return s 176 } 177 178 const page = `<!DOCTYPE html> 179 <html> 180 <head> 181 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.css" /> 182 <script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.1.0/fetch.min.js"></script> 183 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js"></script> 184 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js"></script> 185 <script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.js"></script> 186 </head> 187 <body style="width: 100%%; height: 100%%; margin: 0; overflow: hidden;"> 188 <div id="graphiql" style="height: 100vh;">Loading...</div> 189 <script> 190 function graphQLFetcher(graphQLParams) { 191 return fetch("/graphql%s", { 192 method: "post", 193 body: JSON.stringify(graphQLParams), 194 credentials: "include", 195 }).then(function (response) { 196 return response.text(); 197 }).then(function (responseBody) { 198 try { 199 return JSON.parse(responseBody); 200 } catch (error) { 201 return responseBody; 202 } 203 }); 204 } 205 ReactDOM.render( 206 React.createElement(GraphiQL, {fetcher: graphQLFetcher}), 207 document.getElementById("graphiql") 208 ); 209 </script> 210 </body> 211 </html> 212 `