Skip to content

Commit

Permalink
docs: add a new nodejs api section
Browse files Browse the repository at this point in the history
  • Loading branch information
dosco committed Dec 18, 2022
1 parent c261e35 commit 6fccb98
Show file tree
Hide file tree
Showing 19 changed files with 250 additions and 354 deletions.
6 changes: 4 additions & 2 deletions core/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ func (rc *ReqConfig) GetNamespace() (string, bool) {
// SQL query and execute it on the database. In production mode prepared statements are directly used
// and no query compiling takes places.
//
// In developer mode all names queries are saved into a file `allow.list` and in production mode only
// queries from this file can be run.
// In developer mode all named queries are saved into the queries folder and in production mode only
// queries from these saved queries can be used
func (g *GraphJin) GraphQL(
c context.Context,
query string,
Expand Down Expand Up @@ -346,6 +346,8 @@ func (g *GraphJin) GraphQL(
return res, err
}

// GraphQLByName is similiar to the GraphQL function except that queries saved
// in the queries folder can directly be used by their filename.
func (g *GraphJin) GraphQLByName(
c context.Context,
name string,
Expand Down
7 changes: 2 additions & 5 deletions core/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ func (gj *graphjin) compileQuery(qr queryReq, role string) (*queryComp, error) {
func (gj *graphjin) compileQueryForRoleOnce(qcomp *queryComp, role string) (*queryComp, error) {
var err error

val, loaded := gj.queries.LoadOrStore(reqKey(qcomp.qr, role), qcomp)
qr := qcomp.qr
val, loaded := gj.queries.LoadOrStore((qr.ns + qr.name + role), qcomp)
if loaded {
return val.(*queryComp), nil
}
Expand Down Expand Up @@ -129,7 +130,3 @@ func (gj *graphjin) compileQueryForRole(
st.sql = w.String()
return st, nil
}

func reqKey(qr queryReq, role string) string {
return (qr.ns + qr.name + role)
}
4 changes: 2 additions & 2 deletions core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ type Config struct {
// Enable automatic coversion of camel case in GraphQL to snake case in SQL
EnableCamelcase bool `mapstructure:"enable_camelcase" jsonschema:"title=Enable Camel Case,default=false"`

// Enable production mode. This defaults to true if GO_ENV is set to
// "production". When true the allow list is enforced
// When enabled GraphJin runs with production level security defaults.
// For example allow lists are enforced.
Production bool `jsonschema:"title=Production Mode,default=false"`

// Duration for polling the database to detect schema changes
Expand Down
53 changes: 43 additions & 10 deletions core/subs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var (
)

type sub struct {
ns string
name string
role string
qc *queryComp
Expand Down Expand Up @@ -63,6 +64,7 @@ type mmsg struct {
}

type Member struct {
ns string
params json.RawMessage
sub *sub
Result chan *Result
Expand All @@ -74,7 +76,11 @@ type Member struct {
cindx int
}

// GraphQLEx is the extended version of the Subscribe function allowing for request specific config.
// Subscribe function is called on the GraphJin struct to subscribe to query.
// Any database changes that apply to the query are streamed back in realtime.
//
// In developer mode all named queries are saved into the queries folder and in production mode only
// queries from these saved queries can be used.
func (g *GraphJin) Subscribe(
c context.Context,
query string,
Expand All @@ -84,15 +90,39 @@ func (g *GraphJin) Subscribe(

h, err := graph.FastParse(query)
if err != nil {
panic(err)
return nil, err
}
op := qcode.GetQTypeByName(h.Operation)
name := h.Name

gj := g.Load().(*graphjin)
return gj.subscribeWithOpName(c, op, name, query, vars, rc)

if gj.prod && !gj.conf.DisableAllowList {
item, err := gj.allowList.GetByName(name, gj.prod)
if err != nil {
return nil, err
}
op = qcode.GetQTypeByName(item.Operation)
query = item.Query
}

m, err := gj.subscribeWithOpName(c, op, name, query, vars, rc)
if err != nil {
return nil, err
}

if !gj.prod {
err := gj.saveToAllowList(nil, query, m.ns)
if err != nil {
return nil, err
}
}

return m, err
}

// SubscribeByName is similiar to the Subscribe function except that queries saved
// in the queries folder can directly be used by their filename.
func (g *GraphJin) SubscribeByName(
c context.Context,
name string,
Expand All @@ -118,7 +148,7 @@ func (gj *graphjin) subscribeWithOpName(
vars json.RawMessage,
rc *ReqConfig) (*Member, error) {

if op != qcode.QTSubscription && op != qcode.QTQuery {
if op != qcode.QTSubscription {
return nil, errors.New("subscription: not a subscription query")
}

Expand Down Expand Up @@ -147,7 +177,13 @@ func (gj *graphjin) subscribeWithOpName(
}
}

v, _ := gj.subs.LoadOrStore((name + role), &sub{
ns := gj.namespace
if rc != nil && rc.ns != nil {
ns = *rc.ns
}

v, _ := gj.subs.LoadOrStore((ns + name + role), &sub{
ns: ns,
name: name,
role: role,
add: make(chan *Member),
Expand Down Expand Up @@ -179,6 +215,7 @@ func (gj *graphjin) subscribeWithOpName(
}

m := &Member{
ns: ns,
id: xid.New(),
Result: make(chan *Result, 10),
sub: s,
Expand All @@ -201,17 +238,13 @@ func (gj *graphjin) newSub(c context.Context,
var err error

qr := queryReq{
ns: gj.namespace,
ns: s.ns,
op: qcode.QTSubscription,
name: s.name,
query: []byte(query),
vars: vars,
}

if rc != nil && rc.ns != nil {
qr.ns = *rc.ns
}

if s.qc, err = gj.compileQuery(qr, s.role); err != nil {
return err
}
Expand Down
Loading

1 comment on commit 6fccb98

@vercel
Copy link

@vercel vercel bot commented on 6fccb98 Dec 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.