Skip to content

Commit 19e4196

Browse files
committed
Move DDL to "_schema" sub-path
1 parent bc4e70d commit 19e4196

13 files changed

+204
-143
lines changed

cmd/server.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import (
1515
log2 "log"
1616
"net/http"
1717
"os"
18+
"path"
1819
)
1920

2021
const defaultGraphQLPath = "/graphql"
22+
const defaultGraphQLSchemaPath = "_schema" // Sub-path under graphql-path
2123
const defaultRESTPath = "/todo"
2224

2325
// Environment variables prefixed with "ENDPOINT_" can override settings e.g. "ENDPOINT_HOSTS"
@@ -116,6 +118,7 @@ func Execute() {
116118
// GraphQL specific flags
117119
flags.Bool("start-graphql", true, "start the GraphQL endpoint")
118120
flags.String("graphql-path", defaultGraphQLPath, "path for the GraphQL endpoint")
121+
flags.String("graphql-schema-path", defaultGraphQLSchemaPath, "sub-path for the GraphQL schema management")
119122
flags.Int("graphql-port", 8080, "port for the GraphQL endpoint")
120123

121124
// REST specific flags
@@ -154,13 +157,6 @@ func createEndpoint() *endpoint.DataEndpoint {
154157
WithExcludedKeyspaces(viper.GetStringSlice("excluded-keyspaces")).
155158
WithSchemaUpdateInterval(updateInterval)
156159

157-
supportedOps := viper.GetStringSlice("operations")
158-
ops, err := config.Ops(supportedOps...)
159-
if err != nil {
160-
logger.Fatal("invalid supported operation", "operations", supportedOps, "error", err)
161-
}
162-
cfg.WithSupportedOperations(ops)
163-
164160
endpoint, err := cfg.NewEndpoint()
165161
if err != nil {
166162
logger.Fatal("unable create new endpoint",
@@ -175,12 +171,12 @@ func addGraphQLRoutes(router *httprouter.Router, endpoint *endpoint.DataEndpoint
175171
var err error
176172

177173
singleKeyspace := viper.GetString("keyspace")
178-
path := viper.GetString("graphql-path")
174+
rootPath := viper.GetString("graphql-path")
179175

180176
if singleKeyspace != "" {
181-
routes, err = endpoint.RoutesKeyspaceGraphQL(path, singleKeyspace)
177+
routes, err = endpoint.RoutesKeyspaceGraphQL(rootPath, singleKeyspace)
182178
} else {
183-
routes, err = endpoint.RoutesGraphQL(path)
179+
routes, err = endpoint.RoutesGraphQL(rootPath)
184180
}
185181

186182
if err != nil {
@@ -191,6 +187,24 @@ func addGraphQLRoutes(router *httprouter.Router, endpoint *endpoint.DataEndpoint
191187
for _, route := range routes {
192188
router.Handler(route.Method, route.Pattern, route.Handler)
193189
}
190+
191+
supportedOps := viper.GetStringSlice("operations")
192+
ops, err := config.Ops(supportedOps...)
193+
if err != nil {
194+
logger.Fatal("invalid supported operation", "operations", supportedOps, "error", err)
195+
}
196+
197+
routes, err = endpoint.RoutesSchemaManagementGraphQL(
198+
path.Join(rootPath, viper.GetString("graphql-schema-path")), ops)
199+
200+
if err != nil {
201+
logger.Fatal("unable to generate graphql schema routes",
202+
"error", err)
203+
}
204+
205+
for _, route := range routes {
206+
router.Handler(route.Method, route.Pattern, route.Handler)
207+
}
194208
}
195209

196210
func addRESTRoutes(router *httprouter.Router, endpoint *endpoint.DataEndpoint) {

config/config.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ type Config interface {
99
ExcludedKeyspaces() []string
1010
SchemaUpdateInterval() time.Duration
1111
Naming() NamingConventionFn
12-
SupportedOperations() Operations
1312
UseUserOrRoleAuth() bool
1413
Logger() log.Logger
1514
}

config/mocks.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ func (o *ConfigMock) Default() *ConfigMock {
1919
o.On("ExcludedKeyspaces").Return([]string{"system"})
2020
o.On("SchemaUpdateInterval").Return(10 * time.Second)
2121
o.On("Naming").Return(NamingConventionFn(NewDefaultNaming))
22-
o.On("SupportedOperations").Return(Operations(0))
2322
o.On("UseUserOrRoleAuth").Return(false)
2423
o.On("Logger").Return(log.NewZapLogger(zap.NewExample()))
2524
return o
@@ -40,11 +39,6 @@ func (o *ConfigMock) Naming() NamingConventionFn {
4039
return args.Get(0).(NamingConventionFn)
4140
}
4241

43-
func (o *ConfigMock) SupportedOperations() Operations {
44-
args := o.Called()
45-
return args.Get(0).(Operations)
46-
}
47-
4842
func (o *ConfigMock) UseUserOrRoleAuth() bool {
4943
args := o.Called()
5044
return args.Get(0).(bool)

config/operations.go renamed to config/schema_operations.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ import (
44
"fmt"
55
)
66

7-
type Operations int
7+
type SchemaOperations int
88

99
const (
10-
TableCreate Operations = 1 << iota
10+
TableCreate SchemaOperations = 1 << iota
1111
TableDrop
1212
TableAlterAdd
1313
TableAlterDrop
1414
KeyspaceCreate
1515
KeyspaceDrop
1616
)
1717

18-
func Ops(ops ...string) (Operations, error) {
19-
var o Operations
18+
func Ops(ops ...string) (SchemaOperations, error) {
19+
var o SchemaOperations
2020
err := o.Add(ops...)
2121
return o, err
2222
}
2323

24-
func (o *Operations) Set(ops Operations) { *o |= ops; }
25-
func (o *Operations) Clear(ops Operations) { *o &= ^ops; }
26-
func (o Operations) IsSupported(ops Operations) bool { return o & ops != 0; }
24+
func (o *SchemaOperations) Set(ops SchemaOperations) { *o |= ops; }
25+
func (o *SchemaOperations) Clear(ops SchemaOperations) { *o &= ^ops; }
26+
func (o SchemaOperations) IsSupported(ops SchemaOperations) bool { return o & ops != 0; }
2727

28-
func (o *Operations) Add(ops ...string) error {
28+
func (o *SchemaOperations) Add(ops ...string) error {
2929
for _, op := range ops {
3030
switch op {
3131
case "TableCreate":

config/operations_test.go renamed to config/schema_operations_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import (
66
)
77

88
func TestOperationsSetAndClear(t *testing.T) {
9-
var op Operations
9+
var op SchemaOperations
1010

11-
assert.Equal(t, op, Operations(0))
11+
assert.Equal(t, op, SchemaOperations(0))
1212
assert.False(t, op.IsSupported(TableCreate))
1313

1414
op.Set(TableCreate | TableDrop)
@@ -21,9 +21,9 @@ func TestOperationsSetAndClear(t *testing.T) {
2121
}
2222

2323
func TestOperationsAdd(t *testing.T) {
24-
var op Operations
24+
var op SchemaOperations
2525

26-
assert.Equal(t, op, Operations(0))
26+
assert.Equal(t, op, SchemaOperations(0))
2727

2828
op.Add("TableCreate", "TableDrop", "TableAlterAdd", "TableAlterDrop", "KeyspaceCreate", "KeyspaceDrop")
2929
assert.True(t, op.IsSupported(TableCreate))

db/keyspace.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ func (db *Db) CreateKeyspace(name string, dcReplicas map[string]int, options *Qu
1414
dcs += fmt.Sprintf("%s'%s': %d", comma, name, replicas)
1515
}
1616

17-
query := fmt.Sprintf("CREATE KEYSPACE %s WITH REPLICATION = { 'class': 'NetworkTopologyStrategy', %s }", name, dcs)
17+
query := fmt.Sprintf("CREATE KEYSPACE %s WITH REPLICATION = { 'class': 'NetworkTopologyStrategy', %s }",
18+
Escape(name), dcs)
1819

1920
err := db.session.Execute(query, options)
2021

db/table.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ func (db *Db) CreateTable(info *CreateTableInfo, options *QueryOptions) (bool, e
6767
}
6868
}
6969

70-
query := fmt.Sprintf("CREATE TABLE %s.%s (%sPRIMARY KEY (%s))", info.Keyspace, info.Table, columns, primaryKeys)
70+
query := fmt.Sprintf("CREATE TABLE %s.%s (%sPRIMARY KEY (%s))",
71+
Escape(info.Keyspace), Escape(info.Table), columns, primaryKeys)
7172

7273
if clusteringOrder != "" {
7374
query += fmt.Sprintf(" WITH CLUSTERING ORDER BY (%s)", clusteringOrder)
@@ -78,7 +79,8 @@ func (db *Db) CreateTable(info *CreateTableInfo, options *QueryOptions) (bool, e
7879
}
7980

8081
func (db *Db) AlterTableAdd(info *AlterTableAddInfo, options *QueryOptions) (bool, error) {
81-
query := fmt.Sprintf("ALTER TABLE %s.%s ADD(", info.Keyspace, info.Table)
82+
query := fmt.Sprintf("ALTER TABLE %s.%s ADD(",
83+
Escape(info.Keyspace), Escape(info.Table))
8284
for i, c := range info.ToAdd {
8385
if i > 0 {
8486
query += ", "
@@ -91,14 +93,16 @@ func (db *Db) AlterTableAdd(info *AlterTableAddInfo, options *QueryOptions) (boo
9193
}
9294

9395
func (db *Db) AlterTableDrop(info *AlterTableDropInfo, options *QueryOptions) (bool, error) {
94-
query := fmt.Sprintf("ALTER TABLE %s.%s DROP ", info.Keyspace, info.Table)
96+
query := fmt.Sprintf("ALTER TABLE %s.%s DROP ",
97+
Escape(info.Keyspace), Escape(info.Table))
9598
query += strings.Join(info.ToDrop, ", ")
9699
err := db.session.Execute(query, options)
97100
return err == nil, err
98101
}
99102

100103
func (db *Db) DropTable(info *DropTableInfo, options *QueryOptions) (bool, error) {
101-
query := fmt.Sprintf("DROP TABLE %s.%s", info.Table, info.Keyspace)
104+
query := fmt.Sprintf("DROP TABLE %s.%s",
105+
Escape(info.Table), Escape(info.Keyspace))
102106
err := db.session.Execute(query, options)
103107
return err == nil, err
104108
}

db/utils.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package db
2+
3+
import (
4+
"strings"
5+
"unicode/utf8"
6+
)
7+
8+
var reservedKeywords = map[string]bool{
9+
"add": true,
10+
"allow": true,
11+
"alter": true,
12+
"and": true,
13+
"any": true,
14+
"apply": true,
15+
"asc": true,
16+
"authorize": true,
17+
"batch": true,
18+
"begin": true,
19+
"by": true,
20+
"columnfamily": true,
21+
"create": true,
22+
"delete": true,
23+
"desc": true,
24+
"drop": true,
25+
"each_quorum": true,
26+
"from": true,
27+
"grant": true,
28+
"in": true,
29+
"index": true,
30+
"inet": true,
31+
"infinity": true,
32+
"insert": true,
33+
"into": true,
34+
"keyspace": true,
35+
"keyspaces": true,
36+
"limit": true,
37+
"local_one": true,
38+
"local_quorum": true,
39+
"modify": true,
40+
"nan": true,
41+
"norecursive": true,
42+
"of": true,
43+
"on": true,
44+
"one": true,
45+
"order": true,
46+
"password": true,
47+
"primary": true,
48+
"quorum": true,
49+
"rename": true,
50+
"revoke": true,
51+
"schema": true,
52+
"select": true,
53+
"set": true,
54+
"table": true,
55+
"to": true,
56+
"token": true,
57+
"three": true,
58+
"truncate": true,
59+
"two": true,
60+
"unlogged": true,
61+
"update": true,
62+
"use": true,
63+
"using": true,
64+
"where": true,
65+
"with": true,
66+
}
67+
68+
func Escape(id string) string {
69+
if NeedsDoubleQuotes(id) {
70+
return Quote(id)
71+
}
72+
return id
73+
}
74+
75+
func Quote(s string) string {
76+
return "\"" + s + "\""
77+
}
78+
79+
func NeedsDoubleQuotes(s string) bool {
80+
if s == "" {
81+
return false
82+
}
83+
c, _ := utf8.DecodeRuneInString(s)
84+
if !(c >= 'a' && c <= 'z') {
85+
return true
86+
}
87+
for _, c = range s[1:] {
88+
if !((c >= '0' && c <= '9') || (c == '_') || (c >= 'a' && c <= 'z')) {
89+
return true
90+
}
91+
}
92+
return IsReservedCQLKeyword(s)
93+
}
94+
95+
func IsReservedCQLKeyword(s string) bool {
96+
return reservedKeywords[strings.ToLower(s)]
97+
}

endpoint/endpoint.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ import (
1010
)
1111

1212
const DefaultSchemaUpdateDuration = 10 * time.Second
13+
const DefaultSchemaManagementPath = "_schema"
1314

1415
type DataEndpointConfig struct {
1516
dbHosts []string
1617
dbUsername string
1718
dbPassword string
1819
ksExcluded []string
20+
managementPath string
1921
updateInterval time.Duration
2022
naming config.NamingConventionFn
21-
supportedOps config.Operations
23+
supportedOps config.SchemaOperations
2224
useUserOrRoleAuth bool
2325
logger log.Logger
2426
}
@@ -35,10 +37,6 @@ func (cfg DataEndpointConfig) Naming() config.NamingConventionFn {
3537
return cfg.naming
3638
}
3739

38-
func (cfg DataEndpointConfig) SupportedOperations() config.Operations {
39-
return cfg.supportedOps
40-
}
41-
4240
func (cfg DataEndpointConfig) UseUserOrRoleAuth() bool {
4341
return cfg.useUserOrRoleAuth
4442
}
@@ -62,11 +60,6 @@ func (cfg *DataEndpointConfig) WithNaming(naming config.NamingConventionFn) *Dat
6260
return cfg
6361
}
6462

65-
func (cfg *DataEndpointConfig) WithSupportedOperations(supportedOps config.Operations) *DataEndpointConfig {
66-
cfg.supportedOps = supportedOps
67-
return cfg
68-
}
69-
7063
func (cfg *DataEndpointConfig) WithUseUserOrRoleAuth(useUserOrRowAuth bool) *DataEndpointConfig {
7164
cfg.useUserOrRoleAuth = useUserOrRowAuth
7265
return cfg
@@ -124,3 +117,7 @@ func (e *DataEndpoint) RoutesGraphQL(pattern string) ([]graphql.Route, error) {
124117
func (e *DataEndpoint) RoutesKeyspaceGraphQL(pattern string, ksName string) ([]graphql.Route, error) {
125118
return e.graphQLRouteGen.RoutesKeyspace(pattern, ksName)
126119
}
120+
121+
func (e *DataEndpoint) RoutesSchemaManagementGraphQL(pattern string, ops config.SchemaOperations) ([]graphql.Route, error) {
122+
return e.graphQLRouteGen.RoutesSchemaManagement(pattern, ops)
123+
}

0 commit comments

Comments
 (0)