@@ -3,23 +3,27 @@ package graphql
3
3
import (
4
4
"context"
5
5
"encoding/json"
6
+ "errors"
6
7
"fmt"
7
8
"github.com/datastax/cassandra-data-apis/config"
8
9
"github.com/datastax/cassandra-data-apis/db"
9
10
"github.com/datastax/cassandra-data-apis/log"
10
11
"github.com/graphql-go/graphql"
11
12
"net/http"
12
13
"path"
14
+ "regexp"
15
+ "strings"
13
16
"time"
14
17
)
15
18
16
- type executeQueryFunc func (query string , ctx context.Context ) * graphql.Result
19
+ type executeQueryFunc func (query string , urlPath string , ctx context.Context ) * graphql.Result
17
20
18
21
type RouteGenerator struct {
19
22
dbClient * db.Db
20
23
updateInterval time.Duration
21
24
logger log.Logger
22
25
schemaGen * SchemaGenerator
26
+ urlPattern config.UrlPattern
23
27
}
24
28
25
29
type Route struct {
@@ -42,60 +46,94 @@ func NewRouteGenerator(dbClient *db.Db, cfg config.Config) *RouteGenerator {
42
46
updateInterval : cfg .SchemaUpdateInterval (),
43
47
logger : cfg .Logger (),
44
48
schemaGen : NewSchemaGenerator (dbClient , cfg ),
49
+ urlPattern : cfg .UrlPattern (),
45
50
}
46
51
}
47
52
48
- func (rg * RouteGenerator ) Routes (prefixPattern string ) ([]Route , error ) {
49
- ksNames , err := rg .dbClient .Keyspaces ()
50
- if err != nil {
51
- return nil , fmt .Errorf ("unable to retrieve keyspace names: %s" , err )
52
- }
53
-
54
- routes := make ([]Route , 0 , len (ksNames ))
55
-
56
- for _ , ksName := range ksNames {
57
- if rg .schemaGen .isKeyspaceExcluded (ksName ) {
58
- continue
59
- }
60
- ksRoutes , err := rg .RoutesKeyspace (path .Join (prefixPattern , ksName ), ksName )
61
- if err != nil {
62
- return nil , err
63
- }
64
- routes = append (routes , ksRoutes ... )
65
- }
66
-
67
- return routes , nil
68
- }
69
-
70
53
func (rg * RouteGenerator ) RoutesSchemaManagement (pattern string , ops config.SchemaOperations ) ([]Route , error ) {
71
54
schema , err := rg .schemaGen .BuildKeyspaceSchema (ops )
72
55
if err != nil {
73
56
return nil , fmt .Errorf ("unable to build graphql schema for schema management: %s" , err )
74
57
}
75
- return routesForSchema (pattern , func (query string , ctx context.Context ) * graphql.Result {
58
+ return routesForSchema (pattern , func (query string , urlPath string , ctx context.Context ) * graphql.Result {
76
59
return rg .executeQuery (query , ctx , schema )
77
60
}), nil
78
61
}
79
62
80
- func (rg * RouteGenerator ) RoutesKeyspace (pattern string , ksName string ) ([]Route , error ) {
81
- updater , err := NewUpdater (rg .schemaGen , ksName , rg .updateInterval , rg .logger )
63
+ func (rg * RouteGenerator ) Routes (pattern string , singleKeyspace string ) ([]Route , error ) {
64
+ updater , err := NewUpdater (rg .schemaGen , singleKeyspace , rg .updateInterval , rg .logger )
82
65
if err != nil {
83
- return nil , fmt .Errorf ("unable to build graphql schema for keyspace '%s' : %s" , ksName , err )
66
+ return nil , fmt .Errorf ("unable to build graphql schema: %s" , err )
84
67
}
68
+
85
69
go updater .Start ()
86
- return routesForSchema (pattern , func (query string , ctx context.Context ) * graphql.Result {
87
- return rg .executeQuery (query , ctx , * updater .Schema ())
70
+
71
+ pathParser := getPathParser (pattern )
72
+ if singleKeyspace == "" {
73
+ // Use a single route with keyspace as dynamic parameter
74
+ switch rg .urlPattern {
75
+ case config .UrlPatternColon :
76
+ pattern = path .Join (pattern , ":keyspace" )
77
+ case config .UrlPatternBrackets :
78
+ pattern = path .Join (pattern , "{keyspace}" )
79
+ default :
80
+ return nil , errors .New ("URL pattern not supported" )
81
+ }
82
+ }
83
+
84
+ return routesForSchema (pattern , func (query string , urlPath string , ctx context.Context ) * graphql.Result {
85
+ ksName := singleKeyspace
86
+ if ksName == "" {
87
+ // Multiple keyspace support
88
+ // The keyspace is part of the url path
89
+ ksName = pathParser (urlPath )
90
+ if ksName == "" {
91
+ // Invalid url parameter
92
+ return nil
93
+ }
94
+ }
95
+ schema := updater .Schema (ksName )
96
+
97
+ if schema == nil {
98
+ // The keyspace was not found or is invalid
99
+ return nil
100
+ }
101
+
102
+ return rg .executeQuery (query , ctx , * schema )
88
103
}), nil
89
104
}
90
105
106
+ func getPathParser (root string ) func (string ) string {
107
+ if ! strings .HasSuffix (root , "/" ) {
108
+ root += "/"
109
+ }
110
+ regexString := fmt .Sprintf (`^%s([\w-]+)/?(?:\?.*)?$` , root )
111
+ r := regexp .MustCompile (regexString )
112
+ return func (urlPath string ) string {
113
+ subMatches := r .FindStringSubmatch (urlPath )
114
+ if len (subMatches ) != 2 {
115
+ return ""
116
+ }
117
+ return subMatches [1 ]
118
+ }
119
+ }
120
+
91
121
func routesForSchema (pattern string , execute executeQueryFunc ) []Route {
92
122
return []Route {
93
123
{
94
124
Method : http .MethodGet ,
95
125
Pattern : pattern ,
96
126
Handler : http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
97
- result := execute (r .URL .Query ().Get ("query" ), r .Context ())
98
- json .NewEncoder (w ).Encode (result )
127
+ result := execute (r .URL .Query ().Get ("query" ), r .URL .Path , r .Context ())
128
+ if result == nil {
129
+ // The execution function is signaling that it shouldn't be processing this request
130
+ http .NotFound (w , r )
131
+ return
132
+ }
133
+ err := json .NewEncoder (w ).Encode (result )
134
+ if err != nil {
135
+ http .Error (w , "response could not be encoded: " + err .Error (), 500 )
136
+ }
99
137
}),
100
138
},
101
139
{
@@ -114,8 +152,17 @@ func routesForSchema(pattern string, execute executeQueryFunc) []Route {
114
152
return
115
153
}
116
154
117
- result := execute (body .Query , r .Context ())
118
- json .NewEncoder (w ).Encode (result )
155
+ result := execute (body .Query , r .URL .Path , r .Context ())
156
+ if result == nil {
157
+ // The execution function is signaling that it shouldn't be processing this request
158
+ http .NotFound (w , r )
159
+ return
160
+ }
161
+
162
+ err = json .NewEncoder (w ).Encode (result )
163
+ if err != nil {
164
+ http .Error (w , "response could not be encoded: " + err .Error (), 500 )
165
+ }
119
166
}),
120
167
},
121
168
}
0 commit comments