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  `