From 660c1be5f0133c687df5e2299ad204ef9b181061 Mon Sep 17 00:00:00 2001 From: dosco <832235+dosco@users.noreply.github.com> Date: Wed, 14 Dec 2022 23:36:02 -0800 Subject: [PATCH] feat!: add install script to npm package to setup config folder - [BREAKING] removed action endpoints from serve - [BREAKING] removed js script as a default in core - added SubscribeByName api --- Makefile | 4 +- README.md | 2 - core/api.go | 15 ++---- core/internal/qcode/qcode.go | 4 +- core/subs.go | 42 +++++++++++---- core/wasm.go | 7 --- core/wasm_not.go | 12 ----- examples/nodejs/package-lock.json | 78 +++++++++++++++++++++++++-- examples/nodejs/package.json | 5 +- package-lock.json | 90 +++++++++++++++++++++++-------- package.json | 21 +++----- serv/actions.go | 53 ------------------ serv/api.go | 41 +++++++------- serv/config.go | 12 ----- serv/http.go | 14 ++--- serv/init.go | 28 ---------- serv/routes.go | 41 +------------- serv/serv.go | 15 +----- wasm/httpd.go | 21 -------- wasm/{ => js}/deno.js.bak | 2 +- wasm/{ => js}/globals.js | 0 wasm/{ => js}/graphjin.js | 5 +- wasm/js/install.js | 27 ++++++++++ wasm/{runtime => js}/wasm_exec.js | 0 wasm/main.go | 6 ++- wasm/make.sh | 2 +- wasm/query.go | 28 ++++++++-- 27 files changed, 285 insertions(+), 290 deletions(-) delete mode 100644 core/wasm.go delete mode 100644 core/wasm_not.go delete mode 100644 serv/actions.go delete mode 100644 wasm/httpd.go rename wasm/{ => js}/deno.js.bak (89%) rename wasm/{ => js}/globals.js (100%) rename wasm/{ => js}/graphjin.js (86%) create mode 100644 wasm/js/install.js rename wasm/{runtime => js}/wasm_exec.js (100%) diff --git a/Makefile b/Makefile index 9b67b38b..d3f93e4a 100644 --- a/Makefile +++ b/Makefile @@ -78,9 +78,7 @@ $(BINARY): @CGO_ENABLED=0 go build $(BUILD_FLAGS) -o $(BINARY) main.go $(WASM): - @rm -rf ./wasm/runtime - @mkdir -p ./wasm/runtime - @cp $(GOROOT)/misc/wasm/wasm_exec.js ./wasm/runtime/ + @cp $(GOROOT)/misc/wasm/wasm_exec.js ./wasm/js/ @GOOS=js GOARCH=wasm go build -o ./wasm/graphjin.wasm ./wasm/*.go clean: diff --git a/README.md b/README.md index ad29eb52..b0f20dd9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ GraphJin gives you an instant secure and fast GraphQL API without code. Just use a GraphQL query to define your API and GraphJin automagically converts it into a full featured API. Build your backend APIs **100X** faster. Works with **NodeJS** and **GO**. Supports several databases, **Postgres**, **MySQL**, **YugabyteDB**, **Cockroach**, etc. - ## Secure out of the box In production all queries are always read from locally saved copies not from what the client sends hence clients cannot modify the query. This makes @@ -84,7 +83,6 @@ console.log("Express server started on port %s", server.address().port); ### Quick install - Mac (Homebrew) ``` diff --git a/core/api.go b/core/api.go index 42d5cca4..c9cdbd04 100644 --- a/core/api.go +++ b/core/api.go @@ -167,10 +167,6 @@ func newGraphJin(conf *Config, db *sql.DB, dbinfo *sdata.DBInfo, options ...Opti // ordering of these initializer matter, do not re-order! - if err := gj.initScript(); err != nil { - return nil, err - } - if err := gj.initAPQCache(); err != nil { return nil, err } @@ -529,13 +525,12 @@ type Header struct { // Operation function return the operation type and name from the query. // It uses a very fast algorithm to extract the operation without having to parse the query. -func Operation(query string) (Header, error) { - if h, err := graph.FastParse(query); err == nil { - t := OpType(qcode.GetQTypeByName(h.Operation)) - return Header{t, h.Name}, nil - } else { - return Header{}, err +func Operation(query string) (h Header, err error) { + if v, err := graph.FastParse(query); err == nil { + h.Type = OpType(qcode.GetQTypeByName(v.Operation)) + h.Name = v.Name } + return } func errResult(name string, err error) *Result { diff --git a/core/internal/qcode/qcode.go b/core/internal/qcode/qcode.go index b8096fbf..d5517ec5 100644 --- a/core/internal/qcode/qcode.go +++ b/core/internal/qcode/qcode.go @@ -440,7 +440,7 @@ func (co *Compiler) compileQuery(qc *QCode, op *graph.Operation, role string) er sel.Children = make([]int32, 0, 5) - if err := co.compileDirectives(qc, sel, field.Directives); err != nil { + if err := co.compileSelectorDirectives(qc, sel, field.Directives); err != nil { return err } @@ -949,7 +949,7 @@ func (co *Compiler) compileFieldDirectives(f *Field, dirs []graph.Directive) err return nil } -func (co *Compiler) compileDirectives(qc *QCode, sel *Select, dirs []graph.Directive) error { +func (co *Compiler) compileSelectorDirectives(qc *QCode, sel *Select, dirs []graph.Directive) error { var err error for i := range dirs { diff --git a/core/subs.go b/core/subs.go index efef9387..9dd83b9c 100644 --- a/core/subs.go +++ b/core/subs.go @@ -82,8 +82,6 @@ func (g *GraphJin) Subscribe( rc *ReqConfig) (*Member, error) { var err error - gj := g.Load().(*graphjin) - h, err := graph.FastParse(query) if err != nil { panic(err) @@ -91,20 +89,46 @@ func (g *GraphJin) Subscribe( op := qcode.GetQTypeByName(h.Operation) name := h.Name - if op != qcode.QTSubscription { + gj := g.Load().(*graphjin) + return gj.subscribeWithOpName(c, op, name, query, vars, rc) +} + +func (g *GraphJin) SubscribeByName( + c context.Context, + name string, + vars json.RawMessage, + rc *ReqConfig) (*Member, error) { + + gj := g.Load().(*graphjin) + item, err := gj.allowList.GetByName(name, gj.prod) + if err != nil { + return nil, err + } + op := qcode.GetQTypeByName(item.Operation) + query := item.Query + + return gj.subscribeWithOpName(c, op, name, query, vars, rc) +} + +func (gj *graphjin) subscribeWithOpName( + c context.Context, + op qcode.QType, + name string, + query string, + vars json.RawMessage, + rc *ReqConfig) (*Member, error) { + + if op != qcode.QTSubscription && op != qcode.QTQuery { return nil, errors.New("subscription: not a subscription query") } if name == "" { - if gj.prod { - return nil, errors.New("subscription: query name is required") - } else { - h := sha256.Sum256([]byte(query)) - name = hex.EncodeToString(h[:]) - } + h := sha256.Sum256([]byte(query)) + name = hex.EncodeToString(h[:]) } var role string + var err error if v, ok := c.Value(UserRoleKey).(string); ok { role = v diff --git a/core/wasm.go b/core/wasm.go deleted file mode 100644 index 5c6908f0..00000000 --- a/core/wasm.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build js && wasm - -package core - -func (gj *graphjin) initScript() (err error) { - return -} diff --git a/core/wasm_not.go b/core/wasm_not.go deleted file mode 100644 index afcc222a..00000000 --- a/core/wasm_not.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !wasm - -package core - -import ( - "github.com/dosco/graphjin/plugin/js" -) - -func (gj *graphjin) initScript() (err error) { - gj.scriptMap[".js"] = js.New() - return -} diff --git a/examples/nodejs/package-lock.json b/examples/nodejs/package-lock.json index e3e35b45..c13fc7f6 100644 --- a/examples/nodejs/package-lock.json +++ b/examples/nodejs/package-lock.json @@ -11,14 +11,17 @@ "express": "^4.18.2", "graphjin": "file:../..", "pg": "^8.8.0" + }, + "devDependencies": { + "path": "^0.12.7" } }, "../..": { - "version": "0.21.9", + "version": "2.0.3", "hasInstallScript": true, "license": "Apache-2.0", - "dependencies": { - "@go-task/cli": "^3.16.0" + "devDependencies": { + "fs-extra": "^11.1.0" } }, "../../wasm": { @@ -428,6 +431,16 @@ "node": ">= 0.8" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dev": true, + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -542,6 +555,15 @@ "node": ">=0.10.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -718,6 +740,21 @@ "node": ">= 0.8" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -937,7 +974,7 @@ "graphjin": { "version": "file:../..", "requires": { - "@go-task/cli": "^3.16.0" + "fs-extra": "^11.1.0" } }, "has": { @@ -1049,6 +1086,16 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dev": true, + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -1132,6 +1179,12 @@ "xtend": "^4.0.0" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1257,6 +1310,23 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + } + } + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/examples/nodejs/package.json b/examples/nodejs/package.json index 518546fa..4516c2e5 100644 --- a/examples/nodejs/package.json +++ b/examples/nodejs/package.json @@ -10,8 +10,11 @@ "dev": "node index.js" }, "dependencies": { - "graphjin": "file:../..", "express": "^4.18.2", + "graphjin": "file:../..", "pg": "^8.8.0" + }, + "devDependencies": { + "path": "^0.12.7" } } diff --git a/package-lock.json b/package-lock.json index 491086eb..72576c34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,47 +1,93 @@ { "name": "graphjin", + "version": "2.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "graphjin", + "version": "2.0.3", "hasInstallScript": true, "license": "Apache-2.0", + "devDependencies": { + "fs-extra": "^11.1.0" + } + }, + "node_modules/fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, "dependencies": { - "@go-task/cli": "^3.16.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" } }, - "node_modules/@go-task/cli": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@go-task/cli/-/cli-3.16.0.tgz", - "integrity": "sha512-y+F7+cxt7It///0iv4ogkCyKXfWRGB+XB0bKapjVUjHX94dQKyNLzNcT3XfeOrtPG9nMsMqV9EYR9tdFyfvPJw==", - "hasInstallScript": true, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "dependencies": { - "@go-task/go-npm": "^0.1.15" + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/@go-task/go-npm": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.1.15.tgz", - "integrity": "sha512-xG+6SsSQsa6MzWML1CABWHTwHrCrBqXc/D1POoMDGIgjsRE/PB1noSBGLFhvU5DWHdPksqbAt/w9VOjbqlXpYw==", - "bin": { - "go-npm": "bin/index.js" + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" } } }, "dependencies": { - "@go-task/cli": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@go-task/cli/-/cli-3.16.0.tgz", - "integrity": "sha512-y+F7+cxt7It///0iv4ogkCyKXfWRGB+XB0bKapjVUjHX94dQKyNLzNcT3XfeOrtPG9nMsMqV9EYR9tdFyfvPJw==", + "fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "requires": { - "@go-task/go-npm": "^0.1.15" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" } }, - "@go-task/go-npm": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.1.15.tgz", - "integrity": "sha512-xG+6SsSQsa6MzWML1CABWHTwHrCrBqXc/D1POoMDGIgjsRE/PB1noSBGLFhvU5DWHdPksqbAt/w9VOjbqlXpYw==" + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true } } } diff --git a/package.json b/package.json index c38696cf..cd30072e 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { "name": "graphjin", - "version": "2.0.3", + "version": "2.0.4", "description": "GraphJin - Build APIs in 5 minutes with GraphQL. An instant GraphQL to SQL compiler.", "type": "module", - "main": "wasm/graphjin.js", + "main": "wasm/js/graphjin.js", "files": [ + "./wasm/config", + "./wasm/runtime", "./wasm/graphjin.wasm", - "./wasm/*.js", - "./wasm/runtime" + "./wasm/js/*.js" ], "scripts": { - "postinstall": "go-npm install", - "preuninstall": "go-npm uninstall" + "postinstall": "node ./wasm/js/install.js" }, "repository": { "type": "git", @@ -29,12 +29,7 @@ "url": "https://github.com/dosco/graphjin/issues" }, "homepage": "https://github.com/dosco/graphjin#readme", - "goBinary": { - "name": "graphjin", - "path": "./bin", - "url": "https://github.com/dosco/graphjin/releases/download/v{{version}}/graphjin_{{version}}_{{platform}}_{{arch}}.tar.gz" - }, - "dependencies": { - "@go-task/cli": "^3.16.0" + "devDependencies": { + "fs-extra": "^11.1.0" } } diff --git a/serv/actions.go b/serv/actions.go deleted file mode 100644 index 288301f4..00000000 --- a/serv/actions.go +++ /dev/null @@ -1,53 +0,0 @@ -package serv - -import ( - "fmt" - "net/http" - - "go.opentelemetry.io/otel/trace" -) - -type actionFn func(w http.ResponseWriter, r *http.Request) error - -func newAction(s *Service, a *Action) (http.Handler, error) { - var fn actionFn - var err error - - if a.SQL != "" { - fn, err = newSQLAction(s, a) - } else { - return nil, fmt.Errorf("invalid config for action '%s'", a.Name) - } - - if err != nil { - return nil, err - } - - httpFn := func(w http.ResponseWriter, r *http.Request) { - if err := fn(w, r); err != nil { - renderErr(w, err) - } - } - - return http.HandlerFunc(httpFn), nil -} - -func newSQLAction(s1 *Service, a *Action) (actionFn, error) { - fn := func(w http.ResponseWriter, r *http.Request) error { - var span trace.Span - - s := s1.Load().(*service) - c := r.Context() - - c, span = s.spanStart(c, "Action Request") - _, err := s.db.ExecContext(c, a.SQL) - if err != nil { - spanError(span, err) - } - span.End() - - return err - } - - return fn, nil -} diff --git a/serv/api.go b/serv/api.go index 8fe6b8a9..d110746a 100644 --- a/serv/api.go +++ b/serv/api.go @@ -49,6 +49,7 @@ import ( "github.com/dosco/graphjin/internal/util" "github.com/dosco/graphjin/plugin" "github.com/dosco/graphjin/plugin/fs" + "github.com/dosco/graphjin/plugin/js" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -88,15 +89,10 @@ type service struct { prod bool deployActive bool adminCount int32 - namespace nspace + namespace *string tracer trace.Tracer } -type nspace struct { - name string - set bool -} - type Option func(*service) error func NewGraphJinService(conf *Config, options ...Option) (*Service, error) { @@ -139,7 +135,7 @@ func OptionSetHookFunc(fn HookFn) Option { func OptionSetNamespace(namespace string) Option { return func(s *service) error { - s.namespace = nspace{namespace, true} + s.namespace = &namespace return nil } } @@ -148,10 +144,6 @@ func OptionSetFS(fs plugin.FS) Option { return func(s *service) error { s.fs = fs s.conf.Auth.JWT.SetFS(s.fs) - - for i := range s.conf.Auths { - s.conf.Auths[i].JWT.SetFS(s.fs) - } return nil } } @@ -227,9 +219,12 @@ func newGraphJinService(conf *Config, db *sql.DB, options ...Option) (*service, } func (s *service) normalStart() error { - opts := []core.Option{core.OptionSetFS(s.fs)} - if s.namespace.set { - opts = append(opts, core.OptionSetNamespace(s.namespace.name)) + opts := []core.Option{ + core.OptionSetFS(s.fs), + core.OptionSetScriptCompiler([]string{".js"}, js.New()), + } + if s.namespace != nil { + opts = append(opts, core.OptionSetNamespace(*s.namespace)) } var err error @@ -268,11 +263,13 @@ func (s *service) hotStart() error { } opts := []core.Option{ - core.OptionSetFS(fs.NewAferoFS(bfs.fs))} + core.OptionSetFS(fs.NewAferoFS(bfs.fs)), + core.OptionSetScriptCompiler([]string{".js"}, js.New()), + } - if s.namespace.set { + if s.namespace != nil { opts = append(opts, - core.OptionSetNamespace(s.namespace.name)) + core.OptionSetNamespace(*s.namespace)) } s.gj, err = core.NewGraphJin(&s.conf.Core, s.db, opts...) @@ -305,14 +302,14 @@ func (s *Service) Start() error { } func (s *Service) Attach(mux Mux) error { - return s.attach(mux, nspace{}) + return s.attach(mux, nil) } func (s *Service) AttachWithNamespace(mux Mux, namespace string) error { - return s.attach(mux, nspace{namespace, true}) + return s.attach(mux, &namespace) } -func (s *Service) attach(mux Mux, ns nspace) error { +func (s *Service) attach(mux Mux, ns *string) error { if _, err := routesHandler(s, mux, ns); err != nil { return err } @@ -336,8 +333,8 @@ func (s *Service) attach(mux Mux, ns nspace) error { zap.Bool("secrets-used", (s1.conf.Serv.SecretsFile != "")), } - if s1.namespace.set { - fields = append(fields, zap.String("namespace", s1.namespace.name)) + if s1.namespace != nil { + fields = append(fields, zap.String("namespace", *s1.namespace)) } if s1.conf.HotDeploy { diff --git a/serv/config.go b/serv/config.go index 35ed08a1..0b37fc6e 100644 --- a/serv/config.go +++ b/serv/config.go @@ -119,13 +119,8 @@ type Serv struct { // Sets the default authentication used by the service Auth Auth `jsonschema:"title=Authentication"` - // Sets multiple authentication mechanism which can be used with actions - Auths []Auth `jsonschema:"title=Other Authentication"` - // Database configuration DB Database `mapstructure:"database" jsonschema:"title=Database"` - - Actions []Action `jsonschema:"-"` } // Database configuration @@ -229,13 +224,6 @@ type Telemetry struct { } */ -// Action struct contains config values for a GraphJin service action -type Action struct { - Name string - SQL string - AuthName string `mapstructure:"auth_name"` -} - // ReadInConfig function reads in the config file for the environment specified in the GO_ENV // environment variable. This is the best way to create a new GraphJin config. func ReadInConfig(configFile string) (*Config, error) { diff --git a/serv/http.go b/serv/http.go index bcef394a..63aed1ec 100644 --- a/serv/http.go +++ b/serv/http.go @@ -56,7 +56,7 @@ type errorResp struct { Errors []string `json:"errors"` } -func apiV1Handler(s1 *Service, ns nspace, h http.Handler, ah auth.HandlerFunc) http.Handler { +func apiV1Handler(s1 *Service, ns *string, h http.Handler, ah auth.HandlerFunc) http.Handler { var zlog *zap.Logger s := s1.Load().(*service) @@ -94,7 +94,7 @@ func apiV1Handler(s1 *Service, ns nspace, h http.Handler, ah auth.HandlerFunc) h return h } -func (s1 *Service) apiV1GraphQL(ns nspace, ah auth.HandlerFunc) http.Handler { +func (s1 *Service) apiV1GraphQL(ns *string, ah auth.HandlerFunc) http.Handler { dtrace := otel.GetTextMapPropagator() h := func(w http.ResponseWriter, r *http.Request) { @@ -152,8 +152,8 @@ func (s1 *Service) apiV1GraphQL(ns nspace, ah auth.HandlerFunc) http.Handler { rc.Vars = s.setHeaderVars(r) } - if ns.set { - rc.SetNamespace(ns.name) + if ns != nil { + rc.SetNamespace(*ns) } if req.OpName == "subscription" { @@ -192,7 +192,7 @@ func (s1 *Service) apiV1GraphQL(ns nspace, ah auth.HandlerFunc) http.Handler { return http.HandlerFunc(h) } -func (s1 *Service) apiV1Rest(ns nspace, ah auth.HandlerFunc) http.Handler { +func (s1 *Service) apiV1Rest(ns *string, ah auth.HandlerFunc) http.Handler { rLen := len(routeREST) dtrace := otel.GetTextMapPropagator() @@ -248,8 +248,8 @@ func (s1 *Service) apiV1Rest(ns nspace, ah auth.HandlerFunc) http.Handler { rc.Vars = s.setHeaderVars(r) } - if ns.set { - rc.SetNamespace(ns.name) + if ns != nil { + rc.SetNamespace(*ns) } res, err := s.gj.GraphQLByName(ctx, queryName, vars, &rc) diff --git a/serv/init.go b/serv/init.go index 50b620f3..e72d0272 100644 --- a/serv/init.go +++ b/serv/init.go @@ -71,18 +71,6 @@ func (s *service) initConfig() error { c.Core.DBType = c.DB.Type } - // Auths: validate and sanitize - am := make(map[string]struct{}) - - for i := 0; i < len(c.Auths); i++ { - a := &c.Auths[i] - - if _, ok := am[a.Name]; ok { - return fmt.Errorf("duplicate auth found: %s", a.Name) - } - am[a.Name] = struct{}{} - } - if c.HotDeploy { if c.AdminSecretKey != "" { s.asec = sha256.Sum256([]byte(s.conf.AdminSecretKey)) @@ -91,22 +79,6 @@ func (s *service) initConfig() error { } } - // Actions: validate and sanitize - axm := make(map[string]struct{}) - - for i := 0; i < len(c.Actions); i++ { - a := &c.Actions[i] - - if _, ok := axm[a.Name]; ok { - return fmt.Errorf("duplicate action found: %s", a.Name) - } - - if _, ok := am[a.AuthName]; !ok { - return fmt.Errorf("invalid auth name: %s, For auth: %s", a.AuthName, a.Name) - } - axm[a.Name] = struct{}{} - } - if c.Auth.Type == "" || c.Auth.Type == "none" { c.Core.DefaultBlock = false } diff --git a/serv/routes.go b/serv/routes.go index 7c936513..84be4aaf 100644 --- a/serv/routes.go +++ b/serv/routes.go @@ -3,13 +3,10 @@ package serv import ( "io/fs" "net/http" - "path" - "strings" "github.com/dosco/graphjin/internal/common" "github.com/dosco/graphjin/serv/auth" "github.com/klauspost/compress/gzhttp" - "go.uber.org/zap" ) const ( @@ -31,7 +28,7 @@ type Mux interface { ServeHTTP(http.ResponseWriter, *http.Request) } -func routesHandler(s1 *Service, mux Mux, ns nspace) (http.Handler, error) { +func routesHandler(s1 *Service, mux Mux, ns *string) (http.Handler, error) { s := s1.Load().(*service) // Healthcheck API @@ -44,10 +41,6 @@ func routesHandler(s1 *Service, mux Mux, ns nspace) (http.Handler, error) { mux.Handle(common.DeployRoute, adminDeployHandler(s1)) } - if err := setActionRoutes(s1, mux); err != nil { - return nil, err - } - if s.conf.WebUI { if webRoot, err := fs.Sub(webBuild, "web/build"); err != nil { return nil, err @@ -94,35 +87,3 @@ func routesHandler(s1 *Service, mux Mux, ns nspace) (http.Handler, error) { return setServerHeader(mux), nil } - -func setActionRoutes(s1 *Service, mux Mux) error { - var zlog *zap.Logger - var err error - s := s1.Load().(*service) - - if s.conf.Core.Debug { - zlog = s.zlog - } - - for i, a := range s.conf.Serv.Actions { - var h http.Handler - - h, err = newAction(s1, &s.conf.Serv.Actions[i]) - if err != nil { - break - } - p := path.Join(actionRoute, strings.ToLower(a.Name)) - - if ac, ok := findAuth(s, a.AuthName); ok { - authOpt := auth.Options{AuthFailBlock: s.conf.Serv.AuthFailBlock} - useAuth, err := auth.NewAuth(ac, zlog, authOpt) - if err == nil { - h = useAuth(h) - } else if err != nil && err != auth.ErrNoAuthDefined { - s.log.Fatalf("actions: error initializing auth: %s", err) - } - } - mux.Handle(p, h) - } - return nil -} diff --git a/serv/serv.go b/serv/serv.go index 2975cbe1..abfb06fd 100644 --- a/serv/serv.go +++ b/serv/serv.go @@ -7,10 +7,8 @@ import ( "net/http" "os" "os/signal" - "strings" "time" - "github.com/dosco/graphjin/serv/auth" "github.com/go-chi/chi" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -112,8 +110,8 @@ func startHTTP(s1 *Service) { zap.Bool("secrets-used", (s.conf.Serv.SecretsFile != "")), } - if s.namespace.set { - fields = append(fields, zap.String("namespace", s.namespace.name)) + if s.namespace != nil { + fields = append(fields, zap.String("namespace", *s.namespace)) } if s.conf.HotDeploy { @@ -144,15 +142,6 @@ func setServerHeader(h http.Handler) http.Handler { return http.HandlerFunc(fn) } -func findAuth(s *service, name string) (auth.Auth, bool) { - for _, a := range s.conf.Auths { - if strings.EqualFold(a.Name, name) { - return a, true - } - } - return auth.Auth{}, false -} - type BuildInfo struct { Version string Commit string diff --git a/wasm/httpd.go b/wasm/httpd.go deleted file mode 100644 index 329df9cb..00000000 --- a/wasm/httpd.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build !js && !wasm - -package main - -// super-simple debug server to test our Go WASM files -// func main() { -// http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { -// if req.RequestURI == "/" { -// req.RequestURI = "/index.html" -// } -// file, err := os.Open(filepath.Join("./site", req.RequestURI)) -// if err != nil { -// log.Fatal(err) -// } -// if _, err := io.Copy(w, file); err != nil { -// log.Fatal(err) -// } -// }) -// log.Println("WARM testing server started on port 8080") -// log.Println(http.ListenAndServe(":8080", nil)) //nolint:gosec -// } diff --git a/wasm/deno.js.bak b/wasm/js/deno.js.bak similarity index 89% rename from wasm/deno.js.bak rename to wasm/js/deno.js.bak index 9bd660c3..a660cfe1 100644 --- a/wasm/deno.js.bak +++ b/wasm/js/deno.js.bak @@ -1,4 +1,4 @@ -// import * as _ from "./runtime/wasm_exec.js"; +// import * as _ from "./wasm_exec.js"; // const go = new window.Go(); // const f = await Deno.readFileSync("graphjin.wasm"); // const inst = await WebAssembly.instantiate(f, go.importObject); diff --git a/wasm/globals.js b/wasm/js/globals.js similarity index 100% rename from wasm/globals.js rename to wasm/js/globals.js diff --git a/wasm/graphjin.js b/wasm/js/graphjin.js similarity index 86% rename from wasm/graphjin.js rename to wasm/js/graphjin.js index 2213fe50..bc853ccd 100644 --- a/wasm/graphjin.js +++ b/wasm/js/graphjin.js @@ -1,6 +1,7 @@ import "./globals.js" +import * as _ from "./wasm_exec.js"; + import fs from "fs" -import * as _ from "./runtime/wasm_exec.js"; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; @@ -9,7 +10,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const go = new Go(); -const f = fs.readFileSync(join(__dirname,"./graphjin.wasm")); +const f = fs.readFileSync(join(__dirname,"../graphjin.wasm")); const inst = await WebAssembly.instantiate(f, go.importObject); go.run(inst.instance); diff --git a/wasm/js/install.js b/wasm/js/install.js new file mode 100644 index 00000000..db4eeaf6 --- /dev/null +++ b/wasm/js/install.js @@ -0,0 +1,27 @@ +import fs from "fs-extra" + +import { fileURLToPath } from 'url'; +import { basename, dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const src = join(__dirname,"../config") +const dst = process.env.INIT_CWD + +const newDst = join(dst, basename(src)); + +const opt = { + overwrite: false, + errorOnExist: false, + dereference: true +} + +try { + await fs.emptyDir(newDst) + await fs.copy(src, newDst, opt) +} catch (err) { + console.error(err) + process.exit(1); +} + diff --git a/wasm/runtime/wasm_exec.js b/wasm/js/wasm_exec.js similarity index 100% rename from wasm/runtime/wasm_exec.js rename to wasm/js/wasm_exec.js diff --git a/wasm/main.go b/wasm/main.go index 9355543a..daa6e6ac 100644 --- a/wasm/main.go +++ b/wasm/main.go @@ -80,8 +80,10 @@ func graphjinFunc() js.Func { func newGraphJinObj(gj *core.GraphJin) map[string]interface{} { return map[string]interface{}{ - "query": query(gj), - "subscribe": subscribe(gj), + "query": query(gj), + "subscribe": subscribe(gj), + "queryByName": queryByName(gj), + "subscribeByName": subscribeByName(gj), } } diff --git a/wasm/make.sh b/wasm/make.sh index c6ee5adb..e366aacf 100755 --- a/wasm/make.sh +++ b/wasm/make.sh @@ -1,3 +1,3 @@ #!/bin/sh -cp $(go env GOROOT)/misc/wasm/wasm_exec.js ./runtime/ && \ +cp $(go env GOROOT)/misc/wasm/wasm_exec.js ./js/ && \ GOOS=js GOARCH=wasm go build -o graphjin.wasm *.go diff --git a/wasm/query.go b/wasm/query.go index f780b30e..0744ecee 100644 --- a/wasm/query.go +++ b/wasm/query.go @@ -9,7 +9,6 @@ import ( "syscall/js" "github.com/dosco/graphjin/core" - "github.com/dosco/graphjin/serv" ) func query(gj *core.GraphJin) js.Func { @@ -36,14 +35,13 @@ func query(gj *core.GraphJin) js.Func { }) } -func querybyName(gjs *serv.Service) js.Func { +func queryByName(gj *core.GraphJin) js.Func { return js.FuncOf(func(this js.Value, args []js.Value) interface{} { qa, err := newQueryByNameArgs(args) if !err.IsNull() { return err } - gj := gjs.GetGraphJin() c := context.TODO() if qa.userID != nil { @@ -86,6 +84,30 @@ func subscribe(gj *core.GraphJin) js.Func { }) } +func subscribeByName(gj *core.GraphJin) js.Func { + return js.FuncOf(func(this js.Value, args []js.Value) interface{} { + qa, err := newQueryByNameArgs(args) + if !err.IsNull() { + return err + } + + c := context.TODO() + if qa.userID != nil { + c = context.WithValue(c, core.UserIDKey, qa.userID) + } + + fn := func(resolve, reject js.Value) { + res, err := gj.SubscribeByName(c, qa.query, qa.vars, nil) + if err != nil { + reject.Invoke(toJSError(err)) + } else { + resolve.Invoke(fromMember(res)) + } + } + return toAwait(fn) + }) +} + func fromResult(res *core.Result) map[string]interface{} { sql := res.SQL() sqlFn := js.FuncOf(func(this js.Value, args []js.Value) interface{} {