Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
09d6460a13 | |||
40c99e9ef3 | |||
75ff5510d4 | |||
1370d24985 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,3 +35,4 @@ suppressions
|
|||||||
release
|
release
|
||||||
.gofuzz
|
.gofuzz
|
||||||
*-fuzz.zip
|
*-fuzz.zip
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ rules:
|
|||||||
- name: run
|
- name: run
|
||||||
match: \.go$
|
match: \.go$
|
||||||
ignore: web|examples|docs|_test\.go$
|
ignore: web|examples|docs|_test\.go$
|
||||||
command: go run cmd/main.go serv
|
command: go run main.go serv
|
||||||
- name: test
|
- name: test
|
||||||
match: _test\.go$
|
match: _test\.go$
|
||||||
command: go test -cover {PKG}
|
command: go test -cover {PKG}
|
@ -1,7 +1,7 @@
|
|||||||
# stage: 1
|
# stage: 1
|
||||||
FROM node:10 as react-build
|
FROM node:10 as react-build
|
||||||
WORKDIR /web
|
WORKDIR /web
|
||||||
COPY /cmd/internal/serv/web/ ./
|
COPY /internal/serv/web/ ./
|
||||||
RUN yarn
|
RUN yarn
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
@ -24,8 +24,8 @@ RUN chmod 755 /usr/local/bin/sops
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
COPY . /app
|
||||||
|
|
||||||
RUN mkdir -p /app/cmd/internal/serv/web/build
|
RUN mkdir -p /app/internal/serv/web/build
|
||||||
COPY --from=react-build /web/build/ ./cmd/internal/serv/web/build
|
COPY --from=react-build /web/build/ ./internal/serv/web/build
|
||||||
|
|
||||||
RUN go mod vendor
|
RUN go mod vendor
|
||||||
RUN make build
|
RUN make build
|
||||||
@ -45,7 +45,7 @@ RUN mkdir -p /config
|
|||||||
COPY --from=go-build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=go-build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
COPY --from=go-build /app/config/* /config/
|
COPY --from=go-build /app/config/* /config/
|
||||||
COPY --from=go-build /app/super-graph .
|
COPY --from=go-build /app/super-graph .
|
||||||
COPY --from=go-build /app/cmd/scripts/start.sh .
|
COPY --from=go-build /app/internal/scripts/start.sh .
|
||||||
COPY --from=go-build /usr/local/bin/sops .
|
COPY --from=go-build /usr/local/bin/sops .
|
||||||
|
|
||||||
RUN chmod +x /super-graph
|
RUN chmod +x /super-graph
|
||||||
|
23
Makefile
23
Makefile
@ -12,10 +12,10 @@ endif
|
|||||||
export GO111MODULE := on
|
export GO111MODULE := on
|
||||||
|
|
||||||
# Build-time Go variables
|
# Build-time Go variables
|
||||||
version = github.com/dosco/super-graph/serv.version
|
version = github.com/dosco/super-graph/internal/serv.version
|
||||||
gitBranch = github.com/dosco/super-graph/serv.gitBranch
|
gitBranch = github.com/dosco/super-graph/internal/serv.gitBranch
|
||||||
lastCommitSHA = github.com/dosco/super-graph/serv.lastCommitSHA
|
lastCommitSHA = github.com/dosco/super-graph/internal/serv.lastCommitSHA
|
||||||
lastCommitTime = github.com/dosco/super-graph/serv.lastCommitTime
|
lastCommitTime = github.com/dosco/super-graph/internal/serv.lastCommitTime
|
||||||
|
|
||||||
BUILD_FLAGS ?= -ldflags '-s -w -X ${lastCommitSHA}=${BUILD} -X "${lastCommitTime}=${BUILD_DATE}" -X "${version}=${BUILD_VERSION}" -X ${gitBranch}=${BUILD_BRANCH}'
|
BUILD_FLAGS ?= -ldflags '-s -w -X ${lastCommitSHA}=${BUILD} -X "${lastCommitTime}=${BUILD_DATE}" -X "${version}=${BUILD_VERSION}" -X ${gitBranch}=${BUILD_BRANCH}'
|
||||||
|
|
||||||
@ -28,18 +28,18 @@ BIN_DIR := $(GOPATH)/bin
|
|||||||
GORICE := $(BIN_DIR)/rice
|
GORICE := $(BIN_DIR)/rice
|
||||||
GOLANGCILINT := $(BIN_DIR)/golangci-lint
|
GOLANGCILINT := $(BIN_DIR)/golangci-lint
|
||||||
GITCHGLOG := $(BIN_DIR)/git-chglog
|
GITCHGLOG := $(BIN_DIR)/git-chglog
|
||||||
WEB_BUILD_DIR := ./cmd/internal/serv/web/build/manifest.json
|
WEB_BUILD_DIR := ./internal/serv/web/build/manifest.json
|
||||||
|
|
||||||
$(GORICE):
|
$(GORICE):
|
||||||
@GO111MODULE=off go get -u github.com/GeertJohan/go.rice/rice
|
@GO111MODULE=off go get -u github.com/GeertJohan/go.rice/rice
|
||||||
|
|
||||||
$(WEB_BUILD_DIR):
|
$(WEB_BUILD_DIR):
|
||||||
@echo "First install Yarn and create a build of the web UI then re-run make install"
|
@echo "First install Yarn and create a build of the web UI then re-run make install"
|
||||||
@echo "Run this command: yarn --cwd cmd/internal/serv/web/ build"
|
@echo "Run this command: yarn --cwd internal/serv/web/ build"
|
||||||
@exit 1
|
@exit 1
|
||||||
|
|
||||||
$(GITCHGLOG):
|
$(GITCHGLOG):
|
||||||
@GO111MODULE=off go get -u github.com/git-chglog/git-chglog/cmd/git-chglog
|
@GO111MODULE=off go get -u github.com/git-chglog/git-chglog/git-chglog
|
||||||
|
|
||||||
changelog: $(GITCHGLOG)
|
changelog: $(GITCHGLOG)
|
||||||
@git-chglog $(ARGS)
|
@git-chglog $(ARGS)
|
||||||
@ -57,7 +57,7 @@ os = $(word 1, $@)
|
|||||||
|
|
||||||
$(PLATFORMS): lint test
|
$(PLATFORMS): lint test
|
||||||
@mkdir -p release
|
@mkdir -p release
|
||||||
@GOOS=$(os) GOARCH=amd64 go build $(BUILD_FLAGS) -o release/$(BINARY)-$(BUILD_VERSION)-$(os)-amd64 cmd/main.go
|
@GOOS=$(os) GOARCH=amd64 go build $(BUILD_FLAGS) -o release/$(BINARY)-$(BUILD_VERSION)-$(os)-amd64 main.go
|
||||||
|
|
||||||
release: windows linux darwin
|
release: windows linux darwin
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ gen: $(GORICE) $(WEB_BUILD_DIR)
|
|||||||
@go generate ./...
|
@go generate ./...
|
||||||
|
|
||||||
$(BINARY): clean
|
$(BINARY): clean
|
||||||
@go build $(BUILD_FLAGS) -o $(BINARY) cmd/main.go
|
@go build $(BUILD_FLAGS) -o $(BINARY) main.go
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f $(BINARY)
|
@rm -f $(BINARY)
|
||||||
@ -77,11 +77,10 @@ clean:
|
|||||||
run: clean
|
run: clean
|
||||||
@go run $(BUILD_FLAGS) main.go $(ARGS)
|
@go run $(BUILD_FLAGS) main.go $(ARGS)
|
||||||
|
|
||||||
install:
|
install: clean build
|
||||||
@echo $(GOPATH)
|
|
||||||
@echo "Commit Hash: `git rev-parse HEAD`"
|
@echo "Commit Hash: `git rev-parse HEAD`"
|
||||||
@echo "Old Hash: `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`"
|
@echo "Old Hash: `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`"
|
||||||
@go install $(BUILD_FLAGS) cmd
|
@mv $(BINARY) $(GOPATH)/bin/$(BINARY)
|
||||||
@echo "New Hash:" `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`
|
@echo "New Hash:" `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`
|
||||||
|
|
||||||
uninstall: clean
|
uninstall: clean
|
||||||
|
@ -87,6 +87,7 @@ type SuperGraph struct {
|
|||||||
prepared map[string]*preparedItem
|
prepared map[string]*preparedItem
|
||||||
roles map[string]*Role
|
roles map[string]*Role
|
||||||
getRole *sql.Stmt
|
getRole *sql.Stmt
|
||||||
|
rmap map[uint64]*resolvFn
|
||||||
abacEnabled bool
|
abacEnabled bool
|
||||||
anonExists bool
|
anonExists bool
|
||||||
qc *qcode.Compiler
|
qc *qcode.Compiler
|
||||||
@ -118,6 +119,10 @@ func NewSuperGraph(conf *Config, db *sql.DB) (*SuperGraph, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := sg.initResolvers(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if len(conf.SecretKey) != 0 {
|
if len(conf.SecretKey) != 0 {
|
||||||
sk := sha256.Sum256([]byte(conf.SecretKey))
|
sk := sha256.Sum256([]byte(conf.SecretKey))
|
||||||
conf.SecretKey = ""
|
conf.SecretKey = ""
|
||||||
|
13
core/core.go
13
core/core.go
@ -89,25 +89,28 @@ func (sg *SuperGraph) initCompilers() error {
|
|||||||
|
|
||||||
func (c *scontext) execQuery() ([]byte, error) {
|
func (c *scontext) execQuery() ([]byte, error) {
|
||||||
var data []byte
|
var data []byte
|
||||||
// var st *stmt
|
var st *stmt
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if c.sg.conf.UseAllowList {
|
if c.sg.conf.UseAllowList {
|
||||||
data, _, err = c.resolvePreparedSQL()
|
data, st, err = c.resolvePreparedSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
data, _, err = c.resolveSQL()
|
data, st, err = c.resolveSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
if len(data) == 0 || st.skipped == 0 {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
//return execRemoteJoin(st, data, c.req.hdr)
|
// return c.sg.execRemoteJoin(st, data, c.req.hdr)
|
||||||
|
return c.sg.execRemoteJoin(st, data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *scontext) resolvePreparedSQL() ([]byte, *stmt, error) {
|
func (c *scontext) resolvePreparedSQL() ([]byte, *stmt, error) {
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package qcode
|
package qcode
|
||||||
|
|
||||||
func GetQType(gql string) QType {
|
func GetQType(gql string) QType {
|
||||||
|
ic := false
|
||||||
for i := range gql {
|
for i := range gql {
|
||||||
b := gql[i]
|
b := gql[i]
|
||||||
if b == '{' {
|
switch {
|
||||||
|
case b == '#':
|
||||||
|
ic = true
|
||||||
|
case b == '\n':
|
||||||
|
ic = false
|
||||||
|
case !ic && b == '{':
|
||||||
return QTQuery
|
return QTQuery
|
||||||
}
|
case !ic && al(b):
|
||||||
if al(b) {
|
|
||||||
switch b {
|
switch b {
|
||||||
case 'm', 'M':
|
case 'm', 'M':
|
||||||
return QTMutation
|
return QTMutation
|
||||||
|
50
core/internal/qcode/utils_test.go
Normal file
50
core/internal/qcode/utils_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package qcode
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestGetQType(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
gql string
|
||||||
|
}
|
||||||
|
type ts struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want QType
|
||||||
|
}
|
||||||
|
tests := []ts{
|
||||||
|
ts{
|
||||||
|
name: "query",
|
||||||
|
args: args{gql: " query {"},
|
||||||
|
want: QTQuery,
|
||||||
|
},
|
||||||
|
ts{
|
||||||
|
name: "mutation",
|
||||||
|
args: args{gql: " mutation {"},
|
||||||
|
want: QTMutation,
|
||||||
|
},
|
||||||
|
ts{
|
||||||
|
name: "default query",
|
||||||
|
args: args{gql: " {"},
|
||||||
|
want: QTQuery,
|
||||||
|
},
|
||||||
|
ts{
|
||||||
|
name: "default query with comment",
|
||||||
|
args: args{gql: `# query is good
|
||||||
|
{`},
|
||||||
|
want: QTQuery,
|
||||||
|
},
|
||||||
|
ts{
|
||||||
|
name: "failed query with comment",
|
||||||
|
args: args{gql: `# query is good query {`},
|
||||||
|
want: -1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := GetQType(tt.args.gql); got != tt.want {
|
||||||
|
t.Errorf("GetQType() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
382
core/remote.go
382
core/remote.go
@ -1,253 +1,249 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
// import (
|
import (
|
||||||
// "bytes"
|
"bytes"
|
||||||
// "errors"
|
"errors"
|
||||||
// "fmt"
|
"fmt"
|
||||||
// "net/http"
|
"net/http"
|
||||||
// "sync"
|
"sync"
|
||||||
|
|
||||||
// "github.com/cespare/xxhash/v2"
|
"github.com/cespare/xxhash/v2"
|
||||||
// "github.com/dosco/super-graph/jsn"
|
"github.com/dosco/super-graph/core/internal/qcode"
|
||||||
// "github.com/dosco/super-graph/core/internal/qcode"
|
"github.com/dosco/super-graph/jsn"
|
||||||
// )
|
)
|
||||||
|
|
||||||
// func execRemoteJoin(st *stmt, data []byte, hdr http.Header) ([]byte, error) {
|
func (sg *SuperGraph) execRemoteJoin(st *stmt, data []byte, hdr http.Header) ([]byte, error) {
|
||||||
// var err error
|
var err error
|
||||||
|
|
||||||
// if len(data) == 0 || st.skipped == 0 {
|
sel := st.qc.Selects
|
||||||
// return data, nil
|
h := xxhash.New()
|
||||||
// }
|
|
||||||
|
|
||||||
// sel := st.qc.Selects
|
// fetch the field name used within the db response json
|
||||||
// h := xxhash.New()
|
// that are used to mark insertion points and the mapping between
|
||||||
|
// those field names and their select objects
|
||||||
|
fids, sfmap := sg.parentFieldIds(h, sel, st.skipped)
|
||||||
|
|
||||||
// // fetch the field name used within the db response json
|
// fetch the field values of the marked insertion points
|
||||||
// // that are used to mark insertion points and the mapping between
|
// these values contain the id to be used with fetching remote data
|
||||||
// // those field names and their select objects
|
from := jsn.Get(data, fids)
|
||||||
// fids, sfmap := parentFieldIds(h, sel, st.skipped)
|
var to []jsn.Field
|
||||||
|
|
||||||
// // fetch the field values of the marked insertion points
|
switch {
|
||||||
// // these values contain the id to be used with fetching remote data
|
case len(from) == 1:
|
||||||
// from := jsn.Get(data, fids)
|
to, err = sg.resolveRemote(hdr, h, from[0], sel, sfmap)
|
||||||
// var to []jsn.Field
|
|
||||||
|
|
||||||
// switch {
|
case len(from) > 1:
|
||||||
// case len(from) == 1:
|
to, err = sg.resolveRemotes(hdr, h, from, sel, sfmap)
|
||||||
// to, err = resolveRemote(hdr, h, from[0], sel, sfmap)
|
|
||||||
|
|
||||||
// case len(from) > 1:
|
default:
|
||||||
// to, err = resolveRemotes(hdr, h, from, sel, sfmap)
|
return nil, errors.New("something wrong no remote ids found in db response")
|
||||||
|
}
|
||||||
|
|
||||||
// default:
|
if err != nil {
|
||||||
// return nil, errors.New("something wrong no remote ids found in db response")
|
return nil, err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if err != nil {
|
var ob bytes.Buffer
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var ob bytes.Buffer
|
err = jsn.Replace(&ob, data, from, to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// err = jsn.Replace(&ob, data, from, to)
|
return ob.Bytes(), nil
|
||||||
// if err != nil {
|
}
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return ob.Bytes(), nil
|
func (sg *SuperGraph) resolveRemote(
|
||||||
// }
|
hdr http.Header,
|
||||||
|
h *xxhash.Digest,
|
||||||
|
field jsn.Field,
|
||||||
|
sel []qcode.Select,
|
||||||
|
sfmap map[uint64]*qcode.Select) ([]jsn.Field, error) {
|
||||||
|
|
||||||
// func resolveRemote(
|
// replacement data for the marked insertion points
|
||||||
// hdr http.Header,
|
// key and value will be replaced by whats below
|
||||||
// h *xxhash.Digest,
|
toA := [1]jsn.Field{}
|
||||||
// field jsn.Field,
|
to := toA[:1]
|
||||||
// sel []qcode.Select,
|
|
||||||
// sfmap map[uint64]*qcode.Select) ([]jsn.Field, error) {
|
|
||||||
|
|
||||||
// // replacement data for the marked insertion points
|
// use the json key to find the related Select object
|
||||||
// // key and value will be replaced by whats below
|
k1 := xxhash.Sum64(field.Key)
|
||||||
// toA := [1]jsn.Field{}
|
|
||||||
// to := toA[:1]
|
|
||||||
|
|
||||||
// // use the json key to find the related Select object
|
s, ok := sfmap[k1]
|
||||||
// k1 := xxhash.Sum64(field.Key)
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
p := sel[s.ParentID]
|
||||||
|
|
||||||
// s, ok := sfmap[k1]
|
// then use the Table nme in the Select and it's parent
|
||||||
// if !ok {
|
// to find the resolver to use for this relationship
|
||||||
// return nil, nil
|
k2 := mkkey(h, s.Name, p.Name)
|
||||||
// }
|
|
||||||
// p := sel[s.ParentID]
|
|
||||||
|
|
||||||
// // then use the Table nme in the Select and it's parent
|
r, ok := sg.rmap[k2]
|
||||||
// // to find the resolver to use for this relationship
|
if !ok {
|
||||||
// k2 := mkkey(h, s.Name, p.Name)
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// r, ok := rmap[k2]
|
id := jsn.Value(field.Value)
|
||||||
// if !ok {
|
if len(id) == 0 {
|
||||||
// return nil, nil
|
return nil, nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// id := jsn.Value(field.Value)
|
//st := time.Now()
|
||||||
// if len(id) == 0 {
|
|
||||||
// return nil, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //st := time.Now()
|
b, err := r.Fn(hdr, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// b, err := r.Fn(hdr, id)
|
if len(r.Path) != 0 {
|
||||||
// if err != nil {
|
b = jsn.Strip(b, r.Path)
|
||||||
// return nil, err
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(r.Path) != 0 {
|
var ob bytes.Buffer
|
||||||
// b = jsn.Strip(b, r.Path)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var ob bytes.Buffer
|
if len(s.Cols) != 0 {
|
||||||
|
err = jsn.Filter(&ob, b, colsToList(s.Cols))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// if len(s.Cols) != 0 {
|
} else {
|
||||||
// err = jsn.Filter(&ob, b, colsToList(s.Cols))
|
ob.WriteString("null")
|
||||||
// if err != nil {
|
}
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// } else {
|
to[0] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
|
||||||
// ob.WriteString("null")
|
return to, nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// to[0] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
|
func (sg *SuperGraph) resolveRemotes(
|
||||||
// return to, nil
|
hdr http.Header,
|
||||||
// }
|
h *xxhash.Digest,
|
||||||
|
from []jsn.Field,
|
||||||
|
sel []qcode.Select,
|
||||||
|
sfmap map[uint64]*qcode.Select) ([]jsn.Field, error) {
|
||||||
|
|
||||||
// func resolveRemotes(
|
// replacement data for the marked insertion points
|
||||||
// hdr http.Header,
|
// key and value will be replaced by whats below
|
||||||
// h *xxhash.Digest,
|
to := make([]jsn.Field, len(from))
|
||||||
// from []jsn.Field,
|
|
||||||
// sel []qcode.Select,
|
|
||||||
// sfmap map[uint64]*qcode.Select) ([]jsn.Field, error) {
|
|
||||||
|
|
||||||
// // replacement data for the marked insertion points
|
var wg sync.WaitGroup
|
||||||
// // key and value will be replaced by whats below
|
wg.Add(len(from))
|
||||||
// to := make([]jsn.Field, len(from))
|
|
||||||
|
|
||||||
// var wg sync.WaitGroup
|
var cerr error
|
||||||
// wg.Add(len(from))
|
|
||||||
|
|
||||||
// var cerr error
|
for i, id := range from {
|
||||||
|
|
||||||
// for i, id := range from {
|
// use the json key to find the related Select object
|
||||||
|
k1 := xxhash.Sum64(id.Key)
|
||||||
|
|
||||||
// // use the json key to find the related Select object
|
s, ok := sfmap[k1]
|
||||||
// k1 := xxhash.Sum64(id.Key)
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
p := sel[s.ParentID]
|
||||||
|
|
||||||
// s, ok := sfmap[k1]
|
// then use the Table nme in the Select and it's parent
|
||||||
// if !ok {
|
// to find the resolver to use for this relationship
|
||||||
// return nil, nil
|
k2 := mkkey(h, s.Name, p.Name)
|
||||||
// }
|
|
||||||
// p := sel[s.ParentID]
|
|
||||||
|
|
||||||
// // then use the Table nme in the Select and it's parent
|
r, ok := sg.rmap[k2]
|
||||||
// // to find the resolver to use for this relationship
|
if !ok {
|
||||||
// k2 := mkkey(h, s.Name, p.Name)
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// r, ok := rmap[k2]
|
id := jsn.Value(id.Value)
|
||||||
// if !ok {
|
if len(id) == 0 {
|
||||||
// return nil, nil
|
return nil, nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// id := jsn.Value(id.Value)
|
go func(n int, id []byte, s *qcode.Select) {
|
||||||
// if len(id) == 0 {
|
defer wg.Done()
|
||||||
// return nil, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// go func(n int, id []byte, s *qcode.Select) {
|
//st := time.Now()
|
||||||
// defer wg.Done()
|
|
||||||
|
|
||||||
// //st := time.Now()
|
b, err := r.Fn(hdr, id)
|
||||||
|
if err != nil {
|
||||||
|
cerr = fmt.Errorf("%s: %s", s.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// b, err := r.Fn(hdr, id)
|
if len(r.Path) != 0 {
|
||||||
// if err != nil {
|
b = jsn.Strip(b, r.Path)
|
||||||
// cerr = fmt.Errorf("%s: %s", s.Name, err)
|
}
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(r.Path) != 0 {
|
var ob bytes.Buffer
|
||||||
// b = jsn.Strip(b, r.Path)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var ob bytes.Buffer
|
if len(s.Cols) != 0 {
|
||||||
|
err = jsn.Filter(&ob, b, colsToList(s.Cols))
|
||||||
|
if err != nil {
|
||||||
|
cerr = fmt.Errorf("%s: %s", s.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// if len(s.Cols) != 0 {
|
} else {
|
||||||
// err = jsn.Filter(&ob, b, colsToList(s.Cols))
|
ob.WriteString("null")
|
||||||
// if err != nil {
|
}
|
||||||
// cerr = fmt.Errorf("%s: %s", s.Name, err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// } else {
|
to[n] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
|
||||||
// ob.WriteString("null")
|
}(i, id, s)
|
||||||
// }
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
// to[n] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
|
return to, cerr
|
||||||
// }(i, id, s)
|
}
|
||||||
// }
|
|
||||||
// wg.Wait()
|
|
||||||
|
|
||||||
// return to, cerr
|
func (sg *SuperGraph) parentFieldIds(h *xxhash.Digest, sel []qcode.Select, skipped uint32) (
|
||||||
// }
|
[][]byte,
|
||||||
|
map[uint64]*qcode.Select) {
|
||||||
|
|
||||||
// func parentFieldIds(h *xxhash.Digest, sel []qcode.Select, skipped uint32) (
|
c := 0
|
||||||
// [][]byte,
|
for i := range sel {
|
||||||
// map[uint64]*qcode.Select) {
|
s := &sel[i]
|
||||||
|
if isSkipped(skipped, uint32(s.ID)) {
|
||||||
|
c++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// c := 0
|
// list of keys (and it's related value) to extract from
|
||||||
// for i := range sel {
|
// the db json response
|
||||||
// s := &sel[i]
|
fm := make([][]byte, c)
|
||||||
// if isSkipped(skipped, uint32(s.ID)) {
|
|
||||||
// c++
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // list of keys (and it's related value) to extract from
|
// mapping between the above extracted key and a Select
|
||||||
// // the db json response
|
// object
|
||||||
// fm := make([][]byte, c)
|
sm := make(map[uint64]*qcode.Select, c)
|
||||||
|
n := 0
|
||||||
|
|
||||||
// // mapping between the above extracted key and a Select
|
for i := range sel {
|
||||||
// // object
|
s := &sel[i]
|
||||||
// sm := make(map[uint64]*qcode.Select, c)
|
|
||||||
// n := 0
|
|
||||||
|
|
||||||
// for i := range sel {
|
if !isSkipped(skipped, uint32(s.ID)) {
|
||||||
// s := &sel[i]
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// if !isSkipped(skipped, uint32(s.ID)) {
|
p := sel[s.ParentID]
|
||||||
// continue
|
k := mkkey(h, s.Name, p.Name)
|
||||||
// }
|
|
||||||
|
|
||||||
// p := sel[s.ParentID]
|
if r, ok := sg.rmap[k]; ok {
|
||||||
// k := mkkey(h, s.Name, p.Name)
|
fm[n] = r.IDField
|
||||||
|
n++
|
||||||
|
|
||||||
// if r, ok := rmap[k]; ok {
|
k := xxhash.Sum64(r.IDField)
|
||||||
// fm[n] = r.IDField
|
sm[k] = s
|
||||||
// n++
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// k := xxhash.Sum64(r.IDField)
|
return fm, sm
|
||||||
// sm[k] = s
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return fm, sm
|
func isSkipped(n uint32, pos uint32) bool {
|
||||||
// }
|
return ((n & (1 << pos)) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
// func isSkipped(n uint32, pos uint32) bool {
|
func colsToList(cols []qcode.Column) []string {
|
||||||
// return ((n & (1 << pos)) != 0)
|
var f []string
|
||||||
// }
|
|
||||||
|
|
||||||
// func colsToList(cols []qcode.Column) []string {
|
for i := range cols {
|
||||||
// var f []string
|
f = append(f, cols[i].Name)
|
||||||
|
}
|
||||||
// for i := range cols {
|
return f
|
||||||
// f = append(f, cols[i].Name)
|
}
|
||||||
// }
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
|
127
core/resolve.go
127
core/resolve.go
@ -6,90 +6,90 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cespare/xxhash/v2"
|
||||||
|
"github.com/dosco/super-graph/core/internal/psql"
|
||||||
"github.com/dosco/super-graph/jsn"
|
"github.com/dosco/super-graph/jsn"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
rmap map[uint64]*resolvFn
|
|
||||||
)
|
|
||||||
|
|
||||||
type resolvFn struct {
|
type resolvFn struct {
|
||||||
IDField []byte
|
IDField []byte
|
||||||
Path [][]byte
|
Path [][]byte
|
||||||
Fn func(h http.Header, id []byte) ([]byte, error)
|
Fn func(h http.Header, id []byte) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func initResolvers() {
|
func (sg *SuperGraph) initResolvers() error {
|
||||||
// var err error
|
var err error
|
||||||
// rmap = make(map[uint64]*resolvFn)
|
sg.rmap = make(map[uint64]*resolvFn)
|
||||||
|
|
||||||
// for _, t := range conf.Tables {
|
for _, t := range sg.conf.Tables {
|
||||||
// err = initRemotes(t)
|
err = sg.initRemotes(t)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// break
|
break
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// errlog.Fatal().Err(err).Msg("failed to initialize resolvers")
|
return fmt.Errorf("failed to initialize resolvers: %v", err)
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// func initRemotes(t Table) error {
|
return nil
|
||||||
// h := xxhash.New()
|
}
|
||||||
|
|
||||||
// for _, r := range t.Remotes {
|
func (sg *SuperGraph) initRemotes(t Table) error {
|
||||||
// // defines the table column to be used as an id in the
|
h := xxhash.New()
|
||||||
// // remote request
|
|
||||||
// idcol := r.ID
|
|
||||||
|
|
||||||
// // if no table column specified in the config then
|
for _, r := range t.Remotes {
|
||||||
// // use the primary key of the table as the id
|
// defines the table column to be used as an id in the
|
||||||
// if len(idcol) == 0 {
|
// remote request
|
||||||
// pcol, err := pcompile.IDColumn(t.Name)
|
idcol := r.ID
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// idcol = pcol.Key
|
|
||||||
// }
|
|
||||||
// idk := fmt.Sprintf("__%s_%s", t.Name, idcol)
|
|
||||||
|
|
||||||
// // register a relationship between the remote data
|
// if no table column specified in the config then
|
||||||
// // and the database table
|
// use the primary key of the table as the id
|
||||||
|
if len(idcol) == 0 {
|
||||||
|
pcol, err := sg.pc.IDColumn(t.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
idcol = pcol.Key
|
||||||
|
}
|
||||||
|
idk := fmt.Sprintf("__%s_%s", t.Name, idcol)
|
||||||
|
|
||||||
// val := &psql.DBRel{Type: psql.RelRemote}
|
// register a relationship between the remote data
|
||||||
// val.Left.Col = idcol
|
// and the database table
|
||||||
// val.Right.Col = idk
|
|
||||||
|
|
||||||
// err := pcompile.AddRelationship(strings.ToLower(r.Name), t.Name, val)
|
val := &psql.DBRel{Type: psql.RelRemote}
|
||||||
// if err != nil {
|
val.Left.Col = idcol
|
||||||
// return err
|
val.Right.Col = idk
|
||||||
// }
|
|
||||||
|
|
||||||
// // the function thats called to resolve this remote
|
err := sg.pc.AddRelationship(sanitize(r.Name), t.Name, val)
|
||||||
// // data request
|
if err != nil {
|
||||||
// fn := buildFn(r)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// path := [][]byte{}
|
// the function thats called to resolve this remote
|
||||||
// for _, p := range strings.Split(r.Path, ".") {
|
// data request
|
||||||
// path = append(path, []byte(p))
|
fn := buildFn(r)
|
||||||
// }
|
|
||||||
|
|
||||||
// rf := &resolvFn{
|
path := [][]byte{}
|
||||||
// IDField: []byte(idk),
|
for _, p := range strings.Split(r.Path, ".") {
|
||||||
// Path: path,
|
path = append(path, []byte(p))
|
||||||
// Fn: fn,
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// // index resolver obj by parent and child names
|
rf := &resolvFn{
|
||||||
// rmap[mkkey(h, r.Name, t.Name)] = rf
|
IDField: []byte(idk),
|
||||||
|
Path: path,
|
||||||
|
Fn: fn,
|
||||||
|
}
|
||||||
|
|
||||||
// // index resolver obj by IDField
|
// index resolver obj by parent and child names
|
||||||
// rmap[xxhash.Sum64(rf.IDField)] = rf
|
sg.rmap[mkkey(h, r.Name, t.Name)] = rf
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil
|
// index resolver obj by IDField
|
||||||
// }
|
sg.rmap[xxhash.Sum64(rf.IDField)] = rf
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func buildFn(r Remote) func(http.Header, []byte) ([]byte, error) {
|
func buildFn(r Remote) func(http.Header, []byte) ([]byte, error) {
|
||||||
reqURL := strings.Replace(r.URL, "$id", "%s", 1)
|
reqURL := strings.Replace(r.URL, "$id", "%s", 1)
|
||||||
@ -114,12 +114,9 @@ func buildFn(r Remote) func(http.Header, []byte) ([]byte, error) {
|
|||||||
req.Header.Set(v, hdr.Get(v))
|
req.Header.Set(v, hdr.Get(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
// logger.Debug().Str("uri", uri).Msg("Remote Join")
|
|
||||||
|
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// errlog.Error().Err(err).Msgf("Failed to connect to: %s", uri)
|
return nil, fmt.Errorf("failed to connect to '%s': %v", uri, err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
15
core/utils.go
Normal file
15
core/utils.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cespare/xxhash/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nolint: errcheck
|
||||||
|
func mkkey(h *xxhash.Digest, k1 string, k2 string) uint64 {
|
||||||
|
h.WriteString(k1)
|
||||||
|
h.WriteString(k2)
|
||||||
|
v := h.Sum64()
|
||||||
|
h.Reset()
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
@ -1790,18 +1790,37 @@ database:
|
|||||||
# Enable this if you need the user id in triggers, etc
|
# Enable this if you need the user id in triggers, etc
|
||||||
set_user_id: false
|
set_user_id: false
|
||||||
|
|
||||||
# Define additional variables here to be used with filters
|
# database ping timeout is used for db health checking
|
||||||
variables:
|
ping_timeout: 1m
|
||||||
admin_account_id: "5"
|
|
||||||
|
|
||||||
# Field and table names that you wish to block
|
# Set up an secure tls encrypted db connection
|
||||||
blocklist:
|
enable_tls: false
|
||||||
- ar_internal_metadata
|
|
||||||
- schema_migrations
|
# Required for tls. For example with Google Cloud SQL it's
|
||||||
- secret
|
# <gcp-project-id>:<cloud-sql-instance>"
|
||||||
- password
|
# server_name: blah
|
||||||
- encrypted
|
|
||||||
- token
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# server_cert: ./server-ca.pem
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# client_cert: ./client-cert.pem
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# client_key: ./client-key.pem
|
||||||
|
|
||||||
|
# Define additional variables here to be used with filters
|
||||||
|
variables:
|
||||||
|
admin_account_id: "5"
|
||||||
|
|
||||||
|
# Field and table names that you wish to block
|
||||||
|
blocklist:
|
||||||
|
- ar_internal_metadata
|
||||||
|
- schema_migrations
|
||||||
|
- secret
|
||||||
|
- password
|
||||||
|
- encrypted
|
||||||
|
- token
|
||||||
|
|
||||||
# Create custom actions with their own api endpoints
|
# Create custom actions with their own api endpoints
|
||||||
# For example the below action will be available at /api/v1/actions/refresh_leaderboard_users
|
# For example the below action will be available at /api/v1/actions/refresh_leaderboard_users
|
||||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
|||||||
github.com/adjust/gorails v0.0.0-20171013043634-2786ed0c03d3
|
github.com/adjust/gorails v0.0.0-20171013043634-2786ed0c03d3
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
||||||
github.com/brianvoe/gofakeit v3.18.0+incompatible
|
github.com/brianvoe/gofakeit v3.18.0+incompatible
|
||||||
|
github.com/cespare/xxhash v1.1.0
|
||||||
github.com/cespare/xxhash/v2 v2.1.0
|
github.com/cespare/xxhash/v2 v2.1.0
|
||||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
|
@ -3,8 +3,8 @@ package serv
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dosco/super-graph/cmd/internal/serv/internal/auth"
|
|
||||||
"github.com/dosco/super-graph/core"
|
"github.com/dosco/super-graph/core"
|
||||||
|
"github.com/dosco/super-graph/internal/serv/internal/auth"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@ -60,6 +60,11 @@ type Serv struct {
|
|||||||
PoolSize int32 `mapstructure:"pool_size"`
|
PoolSize int32 `mapstructure:"pool_size"`
|
||||||
MaxRetries int `mapstructure:"max_retries"`
|
MaxRetries int `mapstructure:"max_retries"`
|
||||||
PingTimeout time.Duration `mapstructure:"ping_timeout"`
|
PingTimeout time.Duration `mapstructure:"ping_timeout"`
|
||||||
|
EnableTLS bool `mapstructure:"enable_tls"`
|
||||||
|
ServerName string `mapstructure:"server_name"`
|
||||||
|
ServerCert string `mapstructure:"server_cert"`
|
||||||
|
ClientCert string `mapstructure:"client_cert"`
|
||||||
|
ClientKey string `mapstructure:"client_key"`
|
||||||
} `mapstructure:"database"`
|
} `mapstructure:"database"`
|
||||||
|
|
||||||
Actions []Action
|
Actions []Action
|
@ -156,6 +156,20 @@ func cmdVersion(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BuildDetails() string {
|
func BuildDetails() string {
|
||||||
|
if len(version) == 0 {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
Super Graph (unknown version)
|
||||||
|
For documentation, visit https://supergraph.dev
|
||||||
|
|
||||||
|
To build with version information please use the Makefile
|
||||||
|
> git clone https://github.com/dosco/super-graph
|
||||||
|
> cd super-graph && make install
|
||||||
|
|
||||||
|
Licensed under the Apache Public License 2.0
|
||||||
|
Copyright 2020, Vikram Rangnekar
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
Super Graph %v
|
Super Graph %v
|
||||||
For documentation, visit https://supergraph.dev
|
For documentation, visit https://supergraph.dev
|
||||||
@ -166,7 +180,7 @@ Branch : %v
|
|||||||
Go version : %v
|
Go version : %v
|
||||||
|
|
||||||
Licensed under the Apache Public License 2.0
|
Licensed under the Apache Public License 2.0
|
||||||
Copyright 2020, Vikram Rangnekar.
|
Copyright 2020, Vikram Rangnekar
|
||||||
`,
|
`,
|
||||||
version,
|
version,
|
||||||
lastCommitSHA,
|
lastCommitSHA,
|
@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dosco/super-graph/cmd/internal/serv/internal/migrate"
|
"github.com/dosco/super-graph/internal/serv/internal/migrate"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ func cmdDBReset(cmd *cobra.Command, args []string) {
|
|||||||
func cmdDBCreate(cmd *cobra.Command, args []string) {
|
func cmdDBCreate(cmd *cobra.Command, args []string) {
|
||||||
initConfOnce()
|
initConfOnce()
|
||||||
|
|
||||||
db, err := initDB(conf)
|
db, err := initDB(conf, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ERR failed to connect to database: %s", err)
|
log.Fatalf("ERR failed to connect to database: %s", err)
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func cmdDBCreate(cmd *cobra.Command, args []string) {
|
|||||||
func cmdDBDrop(cmd *cobra.Command, args []string) {
|
func cmdDBDrop(cmd *cobra.Command, args []string) {
|
||||||
initConfOnce()
|
initConfOnce()
|
||||||
|
|
||||||
db, err := initDB(conf)
|
db, err := initDB(conf, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ERR failed to connect to database: %s", err)
|
log.Fatalf("ERR failed to connect to database: %s", err)
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ func cmdDBMigrate(cmd *cobra.Command, args []string) {
|
|||||||
initConfOnce()
|
initConfOnce()
|
||||||
dest := args[0]
|
dest := args[0]
|
||||||
|
|
||||||
conn, err := initDB(conf)
|
conn, err := initDB(conf, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ERR failed to connect to database: %s", err)
|
log.Fatalf("ERR failed to connect to database: %s", err)
|
||||||
}
|
}
|
||||||
@ -223,7 +223,7 @@ func cmdDBMigrate(cmd *cobra.Command, args []string) {
|
|||||||
func cmdDBStatus(cmd *cobra.Command, args []string) {
|
func cmdDBStatus(cmd *cobra.Command, args []string) {
|
||||||
initConfOnce()
|
initConfOnce()
|
||||||
|
|
||||||
db, err := initDB(conf)
|
db, err := initDB(conf, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ERR failed to connect to database: %s", err)
|
log.Fatalf("ERR failed to connect to database: %s", err)
|
||||||
}
|
}
|
@ -28,7 +28,7 @@ func cmdDBSeed(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
conf.Production = false
|
conf.Production = false
|
||||||
|
|
||||||
db, err = initDB(conf)
|
db, err = initDB(conf, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ERR failed to connect to database: %s", err)
|
log.Fatalf("ERR failed to connect to database: %s", err)
|
||||||
}
|
}
|
@ -19,7 +19,7 @@ func cmdServ(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
initWatcher()
|
initWatcher()
|
||||||
|
|
||||||
db, err = initDB(conf)
|
db, err = initDB(conf, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalInProd(err, "failed to connect to database")
|
fatalInProd(err, "failed to connect to database")
|
||||||
}
|
}
|
@ -8,8 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dosco/super-graph/cmd/internal/serv/internal/auth"
|
|
||||||
"github.com/dosco/super-graph/core"
|
"github.com/dosco/super-graph/core"
|
||||||
|
"github.com/dosco/super-graph/internal/serv/internal/auth"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
@ -1,8 +1,14 @@
|
|||||||
package serv
|
package serv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
@ -10,6 +16,10 @@ import (
|
|||||||
//_ "github.com/jackc/pgx/v4/stdlib"
|
//_ "github.com/jackc/pgx/v4/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PEM_SIG = "--BEGIN "
|
||||||
|
)
|
||||||
|
|
||||||
func initConf() (*Config, error) {
|
func initConf() (*Config, error) {
|
||||||
c, err := ReadInConfig(path.Join(confPath, GetConfigName()))
|
c, err := ReadInConfig(path.Join(confPath, GetConfigName()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -79,35 +89,13 @@ func initConf() (*Config, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDB(c *Config) (*sql.DB, error) {
|
func initDB(c *Config, useDB bool) (*sql.DB, error) {
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// cs := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s",
|
|
||||||
// c.DB.Host, c.DB.Port,
|
|
||||||
// c.DB.User, c.DB.Password,
|
|
||||||
// c.DB.DBName)
|
|
||||||
|
|
||||||
// fmt.Println(">>", cs)
|
|
||||||
|
|
||||||
// for i := 1; i < 10; i++ {
|
|
||||||
// db, err = sql.Open("pgx", cs)
|
|
||||||
// if err == nil {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// time.Sleep(time.Duration(i*100) * time.Millisecond)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return db, nil
|
|
||||||
|
|
||||||
config, _ := pgx.ParseConfig("")
|
config, _ := pgx.ParseConfig("")
|
||||||
config.Host = c.DB.Host
|
config.Host = c.DB.Host
|
||||||
config.Port = c.DB.Port
|
config.Port = c.DB.Port
|
||||||
config.Database = c.DB.DBName
|
|
||||||
config.User = c.DB.User
|
config.User = c.DB.User
|
||||||
config.Password = c.DB.Password
|
config.Password = c.DB.Password
|
||||||
config.RuntimeParams = map[string]string{
|
config.RuntimeParams = map[string]string{
|
||||||
@ -115,6 +103,63 @@ func initDB(c *Config) (*sql.DB, error) {
|
|||||||
"search_path": c.DB.Schema,
|
"search_path": c.DB.Schema,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if useDB {
|
||||||
|
config.Database = c.DB.DBName
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.DB.EnableTLS {
|
||||||
|
if len(c.DB.ServerName) == 0 {
|
||||||
|
return nil, errors.New("server_name is required")
|
||||||
|
}
|
||||||
|
if len(c.DB.ServerCert) == 0 {
|
||||||
|
return nil, errors.New("server_cert is required")
|
||||||
|
}
|
||||||
|
if len(c.DB.ClientCert) == 0 {
|
||||||
|
return nil, errors.New("client_cert is required")
|
||||||
|
}
|
||||||
|
if len(c.DB.ClientKey) == 0 {
|
||||||
|
return nil, errors.New("client_key is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCertPool := x509.NewCertPool()
|
||||||
|
var pem []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if strings.Contains(c.DB.ServerCert, PEM_SIG) {
|
||||||
|
pem = []byte(c.DB.ServerCert)
|
||||||
|
} else {
|
||||||
|
pem, err = ioutil.ReadFile(c.DB.ServerCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("db tls: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
|
||||||
|
return nil, errors.New("db tls: failed to append pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCert := make([]tls.Certificate, 0, 1)
|
||||||
|
var certs tls.Certificate
|
||||||
|
|
||||||
|
if strings.Contains(c.DB.ClientCert, PEM_SIG) {
|
||||||
|
certs, err = tls.X509KeyPair([]byte(c.DB.ClientCert), []byte(c.DB.ClientKey))
|
||||||
|
} else {
|
||||||
|
certs, err = tls.LoadX509KeyPair(c.DB.ClientCert, c.DB.ClientKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("db tls: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCert = append(clientCert, certs)
|
||||||
|
config.TLSConfig = &tls.Config{
|
||||||
|
RootCAs: rootCertPool,
|
||||||
|
Certificates: clientCert,
|
||||||
|
ServerName: c.DB.ServerName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// switch c.LogLevel {
|
// switch c.LogLevel {
|
||||||
// case "debug":
|
// case "debug":
|
||||||
// config.LogLevel = pgx.LogLevelDebug
|
// config.LogLevel = pgx.LogLevelDebug
|
@ -9,8 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bradfitz/gomemcache/memcache"
|
"github.com/bradfitz/gomemcache/memcache"
|
||||||
"github.com/dosco/super-graph/cmd/internal/serv/internal/rails"
|
|
||||||
"github.com/dosco/super-graph/core"
|
"github.com/dosco/super-graph/core"
|
||||||
|
"github.com/dosco/super-graph/internal/serv/internal/rails"
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/garyburd/redigo/redis"
|
||||||
)
|
)
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
rice "github.com/GeertJohan/go.rice"
|
rice "github.com/GeertJohan/go.rice"
|
||||||
"github.com/NYTimes/gziphandler"
|
"github.com/NYTimes/gziphandler"
|
||||||
"github.com/dosco/super-graph/cmd/internal/serv/internal/auth"
|
"github.com/dosco/super-graph/internal/serv/internal/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initWatcher() {
|
func initWatcher() {
|
@ -133,20 +133,36 @@ database:
|
|||||||
# database ping timeout is used for db health checking
|
# database ping timeout is used for db health checking
|
||||||
ping_timeout: 1m
|
ping_timeout: 1m
|
||||||
|
|
||||||
# Define additional variables here to be used with filters
|
# Set up an secure tls encrypted db connection
|
||||||
variables:
|
enable_tls: false
|
||||||
#admin_account_id: "5"
|
|
||||||
admin_account_id: "sql:select id from users where admin = true limit 1"
|
# Required for tls. For example with Google Cloud SQL it's
|
||||||
|
# <gcp-project-id>:<cloud-sql-instance>"
|
||||||
|
# server_name: blah
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# server_cert: ./server-ca.pem
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# client_cert: ./client-cert.pem
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# client_key: ./client-key.pem
|
||||||
|
|
||||||
|
# Define additional variables here to be used with filters
|
||||||
|
variables:
|
||||||
|
#admin_account_id: "5"
|
||||||
|
admin_account_id: "sql:select id from users where admin = true limit 1"
|
||||||
|
|
||||||
|
|
||||||
# Field and table names that you wish to block
|
# Field and table names that you wish to block
|
||||||
blocklist:
|
blocklist:
|
||||||
- ar_internal_metadata
|
- ar_internal_metadata
|
||||||
- schema_migrations
|
- schema_migrations
|
||||||
- secret
|
- secret
|
||||||
- password
|
- password
|
||||||
- encrypted
|
- encrypted
|
||||||
- token
|
- token
|
||||||
|
|
||||||
# Create custom actions with their own api endpoints
|
# Create custom actions with their own api endpoints
|
||||||
# For example the below action will be available at /api/v1/actions/refresh_leaderboard_users
|
# For example the below action will be available at /api/v1/actions/refresh_leaderboard_users
|
@ -77,4 +77,20 @@ database:
|
|||||||
set_user_id: false
|
set_user_id: false
|
||||||
|
|
||||||
# database ping timeout is used for db health checking
|
# database ping timeout is used for db health checking
|
||||||
ping_timeout: 5m
|
ping_timeout: 5m
|
||||||
|
|
||||||
|
# Set up an secure tls encrypted db connection
|
||||||
|
enable_tls: false
|
||||||
|
|
||||||
|
# Required for tls. For example with Google Cloud SQL it's
|
||||||
|
# <gcp-project-id>:<cloud-sql-instance>"
|
||||||
|
# server_name: blah
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# server_cert: ./server-ca.pem
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# client_cert: ./client-cert.pem
|
||||||
|
|
||||||
|
# Required for tls. Can be a file path or the contents of the pem file
|
||||||
|
# client_key: ./client-key.pem
|
@ -10,20 +10,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cespare/xxhash/v2"
|
|
||||||
"github.com/dosco/super-graph/jsn"
|
"github.com/dosco/super-graph/jsn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: errcheck
|
|
||||||
func mkkey(h *xxhash.Digest, k1 string, k2 string) uint64 {
|
|
||||||
h.WriteString(k1)
|
|
||||||
h.WriteString(k2)
|
|
||||||
v := h.Sum64()
|
|
||||||
h.Reset()
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint: errcheck
|
// nolint: errcheck
|
||||||
func gqlHash(b string, vars []byte, role string) string {
|
func gqlHash(b string, vars []byte, role string) string {
|
||||||
b = strings.TrimSpace(b)
|
b = strings.TrimSpace(b)
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user