Compare commits

...

4 Commits

8 changed files with 124 additions and 54 deletions

View File

@ -7,7 +7,7 @@
![Docker build](https://img.shields.io/docker/cloud/build/dosco/super-graph.svg?style=flat-square)
[![Discord Chat](https://img.shields.io/discord/628796009539043348.svg)](https://discord.gg/6pSWCTZ)
Super Graph gives you a high performance GraphQL API without you having to write any code. GraphQL is automatically compiled efficient an SQL query. Use it either as a library or a standalone service.
Super Graph gives you a high performance GraphQL API without you having to write any code. GraphQL is automagically compiled into an efficient SQL query. Use it either as a library or a standalone service.
## Using it as a service

View File

@ -91,7 +91,6 @@ auth:
# password: ""
# max_idle: 80
# max_active: 12000
# In most cases you don't need these
# salt: "encrypted cookie"
# sign_salt: "signed encrypted cookie"
@ -165,7 +164,6 @@ tables:
- name: email
related_to: products.name
roles_query: "SELECT * FROM users WHERE id = $user_id"
roles:

View File

@ -107,6 +107,7 @@ type Role struct {
// RoleTable struct contains role specific access control values for a database table
type RoleTable struct {
Name string
ReadOnly *bool `mapstructure:"read_only"`
Query Query
Insert Insert
@ -120,7 +121,7 @@ type Query struct {
Filters []string
Columns []string
DisableFunctions bool `mapstructure:"disable_functions"`
Block bool
Block *bool
}
// Insert struct contains access control values for insert operations
@ -128,7 +129,7 @@ type Insert struct {
Filters []string
Columns []string
Presets map[string]string
Block bool
Block *bool
}
// Insert struct contains access control values for update operations
@ -136,14 +137,14 @@ type Update struct {
Filters []string
Columns []string
Presets map[string]string
Block bool
Block *bool
}
// Delete struct contains access control values for delete operations
type Delete struct {
Filters []string
Columns []string
Block bool
Block *bool
}
// ReadInConfig function reads in the config file for the environment specified in the GO_ENV

View File

@ -216,49 +216,70 @@ func addRoles(c *Config, qc *qcode.Compiler) error {
}
func addRole(qc *qcode.Compiler, r Role, t RoleTable) error {
blockFilter := []string{"false"}
blocked := struct {
readOnly bool
query bool
insert bool
update bool
delete bool
}{true, true, true, true, true}
if r.Name == "anon" {
blocked.query = false
} else {
blocked.readOnly = false
blocked.query = false
blocked.insert = false
blocked.update = false
blocked.delete = false
}
if t.ReadOnly != nil {
blocked.readOnly = *t.ReadOnly
}
if t.Query.Block != nil {
blocked.query = *t.Query.Block
}
if t.Insert.Block != nil {
blocked.insert = *t.Insert.Block
}
if t.Update.Block != nil {
blocked.update = *t.Update.Block
}
if t.Delete.Block != nil {
blocked.delete = *t.Delete.Block
}
query := qcode.QueryConfig{
Limit: t.Query.Limit,
Filters: t.Query.Filters,
Columns: t.Query.Columns,
DisableFunctions: t.Query.DisableFunctions,
}
if t.Query.Block {
query.Filters = blockFilter
Block: blocked.query,
}
insert := qcode.InsertConfig{
Filters: t.Insert.Filters,
Columns: t.Insert.Columns,
Presets: t.Insert.Presets,
}
if t.Insert.Block {
insert.Filters = blockFilter
Block: blocked.insert,
}
update := qcode.UpdateConfig{
Filters: t.Update.Filters,
Columns: t.Update.Columns,
Presets: t.Update.Presets,
}
if t.Update.Block {
update.Filters = blockFilter
Block: blocked.update,
}
delete := qcode.DeleteConfig{
Filters: t.Delete.Filters,
Columns: t.Delete.Columns,
}
if t.Delete.Block {
delete.Filters = blockFilter
Block: blocked.delete,
}
return qc.AddRole(r.Name, t.Name, qcode.TRConfig{
ReadOnly: blocked.readOnly,
Query: query,
Insert: insert,
Update: update,

View File

@ -10,6 +10,7 @@ import (
"sort"
"strings"
"github.com/chirino/graphql/schema"
"github.com/dosco/super-graph/jsn"
)
@ -140,6 +141,7 @@ func (al *List) Set(vars []byte, query, comment string) error {
func (al *List) Load() ([]Item, error) {
var list []Item
varString := "variables"
b, err := ioutil.ReadFile(al.filepath)
if err != nil {
@ -180,9 +182,9 @@ func (al *List) Load() ([]Item, error) {
s = e
}
ty = AL_QUERY
} else if matchPrefix(b, e, "variables") {
} else if matchPrefix(b, e, varString) {
if c == 0 {
s = e + len("variables") + 1
s = e + len(varString) + 1
}
ty = AL_VARS
} else if b[e] == '{' {
@ -234,7 +236,21 @@ func (al *List) Load() ([]Item, error) {
func (al *List) save(item Item) error {
var buf bytes.Buffer
item.Name = QueryName(item.Query)
qd := &schema.QueryDocument{}
if err := qd.Parse(item.Query); err != nil {
fmt.Println("##", item.Query)
return err
}
qd.WriteTo(&buf)
query := buf.String()
buf.Reset()
// fmt.Println(">", query)
item.Name = QueryName(query)
item.key = strings.ToLower(item.Name)
if len(item.Name) == 0 {

View File

@ -16,26 +16,31 @@ type QueryConfig struct {
Filters []string
Columns []string
DisableFunctions bool
Block bool
}
type InsertConfig struct {
Filters []string
Columns []string
Presets map[string]string
Block bool
}
type UpdateConfig struct {
Filters []string
Columns []string
Presets map[string]string
Block bool
}
type DeleteConfig struct {
Filters []string
Columns []string
Block bool
}
type TRConfig struct {
ReadOnly bool
Query QueryConfig
Insert InsertConfig
Update UpdateConfig
@ -43,14 +48,14 @@ type TRConfig struct {
}
type trval struct {
readOnly bool
query struct {
limit string
fil *Exp
filNU bool
cols map[string]struct{}
disable struct {
funcs bool
}
disable struct{ funcs bool }
block bool
}
insert struct {
@ -59,6 +64,7 @@ type trval struct {
cols map[string]struct{}
psmap map[string]string
pslist []string
block bool
}
update struct {
@ -67,12 +73,14 @@ type trval struct {
cols map[string]struct{}
psmap map[string]string
pslist []string
block bool
}
delete struct {
fil *Exp
filNU bool
cols map[string]struct{}
block bool
}
}

View File

@ -207,7 +207,7 @@ func NewFilter() *Exp {
func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
var err error
trv := &trval{}
trv := &trval{readOnly: trc.ReadOnly}
// query config
trv.query.fil, trv.query.filNU, err = compileFilter(trc.Query.Filters)
@ -219,6 +219,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
}
trv.query.cols = listToMap(trc.Query.Columns)
trv.query.disable.funcs = trc.Query.DisableFunctions
trv.query.block = trc.Query.Block
// insert config
trv.insert.fil, trv.insert.filNU, err = compileFilter(trc.Insert.Filters)
@ -228,6 +229,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
trv.insert.cols = listToMap(trc.Insert.Columns)
trv.insert.psmap = parsePresets(trc.Insert.Presets)
trv.insert.pslist = mapToList(trv.insert.psmap)
trv.insert.block = trc.Insert.Block
// update config
trv.update.fil, trv.update.filNU, err = compileFilter(trc.Update.Filters)
@ -237,6 +239,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
trv.update.cols = listToMap(trc.Update.Columns)
trv.update.psmap = parsePresets(trc.Update.Presets)
trv.update.pslist = mapToList(trv.update.psmap)
trv.update.block = trc.Update.Block
// delete config
trv.delete.fil, trv.delete.filNU, err = compileFilter(trc.Delete.Filters)
@ -244,6 +247,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
return err
}
trv.delete.cols = listToMap(trc.Delete.Columns)
trv.delete.block = trc.Delete.Block
singular := flect.Singularize(table)
plural := flect.Pluralize(table)
@ -330,6 +334,28 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
trv := com.getRole(role, field.Name)
switch action {
case QTQuery:
if trv.query.block {
continue
}
case QTInsert:
if trv.insert.block || trv.readOnly {
return fmt.Errorf("insert blocked: %s", field.Name)
}
case QTUpdate:
if trv.update.block || trv.readOnly {
return fmt.Errorf("update blocked: %s", field.Name)
}
case QTDelete:
if trv.delete.block || trv.readOnly {
return fmt.Errorf("delete blocked: %s", field.Name)
}
}
selects = append(selects, Select{
ID: id,
ParentID: parentID,

View File

@ -36,7 +36,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; // Add to the top of the file b
### Fetching data with GraphQL
Just image the code or SQL you'll need to fetch this data, the user, all his posts, all the votes on the posts, the authors information and the related tags. Oh ya you also need efficient cursor based pagination. And Remember you also need to maintain this code forever.
Just imagine the code or SQL you'll need to fetch this data, the user, all his posts, all the votes on the posts, the authors information and the related tags. Oh yeah and you also need efficient cursor based pagination. And Remember you also need to maintain this code forever.
Instead just describe the data you need in GraphQL and give that to Super Graph it'll automatically learn your database and generate the most efficient SQL query fetching your data in the JSON structure you expected.
@ -71,7 +71,7 @@ query {
### Instant results
Here's the data Super Graph fetched using the GraphQL above it's even in the JSON structure you
Here's the data Super Graph fetched using the GraphQL above, it's even in the JSON structure you
wanted it in. All this without you writing any code or SQL.
```json