github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/api/schema_handler.go (about) 1 // Copyright (c) 2017-2018 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package api 16 17 import ( 18 "encoding/json" 19 "net/http" 20 21 "github.com/uber/aresdb/metastore" 22 "github.com/uber/aresdb/utils" 23 24 "github.com/gorilla/mux" 25 ) 26 27 // SchemaHandler handles schema http requests. 28 type SchemaHandler struct { 29 // all write requests will go to metaStore. 30 metaStore metastore.MetaStore 31 } 32 33 // NewSchemaHandler will create a new SchemaHandler with memStore and metaStore. 34 func NewSchemaHandler(metaStore metastore.MetaStore) *SchemaHandler { 35 return &SchemaHandler{ 36 metaStore: metaStore, 37 } 38 } 39 40 // Register registers http handlers. 41 func (handler *SchemaHandler) Register(router *mux.Router, wrappers ...utils.HTTPHandlerWrapper) { 42 router.HandleFunc("/tables", utils.ApplyHTTPWrappers(handler.ListTables, wrappers)).Methods(http.MethodGet) 43 router.HandleFunc("/tables", utils.ApplyHTTPWrappers(handler.AddTable, wrappers)).Methods(http.MethodPost) 44 router.HandleFunc("/tables/{table}", utils.ApplyHTTPWrappers(handler.GetTable, wrappers)).Methods(http.MethodGet) 45 router.HandleFunc("/tables/{table}", utils.ApplyHTTPWrappers(handler.DeleteTable, wrappers)).Methods(http.MethodDelete) 46 router.HandleFunc("/tables/{table}", utils.ApplyHTTPWrappers(handler.UpdateTableConfig, wrappers)).Methods(http.MethodPut) 47 router.HandleFunc("/tables/{table}/columns", utils.ApplyHTTPWrappers(handler.AddColumn, wrappers)).Methods(http.MethodPost) 48 router.HandleFunc("/tables/{table}/columns/{column}", utils.ApplyHTTPWrappers(handler.UpdateColumn, wrappers)).Methods(http.MethodPut) 49 router.HandleFunc("/tables/{table}/columns/{column}", utils.ApplyHTTPWrappers(handler.DeleteColumn, wrappers)).Methods(http.MethodDelete) 50 } 51 52 // RegisterForDebug register handlers for debug port 53 func (handler *SchemaHandler) RegisterForDebug(router *mux.Router, wrappers ...utils.HTTPHandlerWrapper) { 54 router.HandleFunc("/tables", utils.ApplyHTTPWrappers(handler.ListTables, wrappers)).Methods(http.MethodGet) 55 router.HandleFunc("/tables/{table}", utils.ApplyHTTPWrappers(handler.GetTable, wrappers)).Methods(http.MethodGet) 56 } 57 58 // ListTables swagger:route GET /schema/tables listTables 59 // List all table schemas 60 // Consumes: 61 // - application/json 62 // 63 // Produces: 64 // - application/json 65 // 66 // Responses: 67 // default: errorResponse 68 // 200: stringArrayResponse 69 func (handler *SchemaHandler) ListTables(w http.ResponseWriter, r *http.Request) { 70 71 response := NewStringArrayResponse() 72 73 tables, err := handler.metaStore.ListTables() 74 if err != nil { 75 RespondWithError(w, err) 76 return 77 } 78 for _, tableName := range tables { 79 response.Body = append(response.Body, tableName) 80 } 81 82 RespondWithJSONObject(w, response.Body) 83 } 84 85 // GetTable swagger:route GET /schema/tables/{table} getTable 86 // get the table schema for specific table name 87 // 88 // Consumes: 89 // - application/json 90 // 91 // Produces: 92 // - application/json 93 // 94 // Responses: 95 // default: errorResponse 96 // 200: getTableResponse 97 func (handler *SchemaHandler) GetTable(w http.ResponseWriter, r *http.Request) { 98 var getTableRequest GetTableRequest 99 var getTableResponse GetTableResponse 100 101 err := ReadRequest(r, &getTableRequest) 102 if err != nil { 103 RespondWithError(w, err) 104 return 105 } 106 107 table, err := handler.metaStore.GetTable(getTableRequest.TableName) 108 if err != nil { 109 if err.Error() == metastore.ErrTableDoesNotExist.Error() { 110 RespondBytesWithCode(w, http.StatusNotFound, []byte(err.Error())) 111 return 112 } 113 } 114 getTableResponse.JSONBuffer, err = json.Marshal(table) 115 116 RespondWithJSONBytes(w, getTableResponse.JSONBuffer, err) 117 } 118 119 // AddTable swagger:route POST /schema/tables addTable 120 // add table to table collections 121 // 122 // Consumes: 123 // - application/json 124 // 125 // Responses: 126 // default: errorResponse 127 // 200: noContentResponse 128 func (handler *SchemaHandler) AddTable(w http.ResponseWriter, r *http.Request) { 129 130 var addTableRequest AddTableRequest 131 // add default table configs first 132 addTableRequest.Body.Config = metastore.DefaultTableConfig 133 err := ReadRequest(r, &addTableRequest) 134 if err != nil { 135 RespondWithBadRequest(w, err) 136 return 137 } 138 139 newTable := addTableRequest.Body 140 err = handler.metaStore.CreateTable(&newTable) 141 if err != nil { 142 RespondWithError(w, err) 143 return 144 } 145 RespondWithJSONObject(w, nil) 146 } 147 148 // UpdateTableConfig swagger:route PUT /schema/tables/{table} updateTableConfig 149 // update config of the specified table 150 // 151 // Consumes: 152 // - application/json 153 // 154 // Responses: 155 // default: errorResponse 156 // 200: noContentResponse 157 func (handler *SchemaHandler) UpdateTableConfig(w http.ResponseWriter, r *http.Request) { 158 var request UpdateTableConfigRequest 159 err := ReadRequest(r, &request) 160 if err != nil { 161 RespondWithBadRequest(w, err) 162 return 163 } 164 165 err = handler.metaStore.UpdateTableConfig(request.TableName, request.Body) 166 if err != nil { 167 RespondWithError(w, err) 168 return 169 } 170 171 RespondWithJSONObject(w, nil) 172 } 173 174 // DeleteTable swagger:route DELETE /schema/tables/{table} deleteTable 175 // delete table from metaStore 176 // 177 // Responses: 178 // default: errorResponse 179 // 200: noContentResponse 180 func (handler *SchemaHandler) DeleteTable(w http.ResponseWriter, r *http.Request) { 181 var deleteTableRequest DeleteTableRequest 182 err := ReadRequest(r, &deleteTableRequest) 183 if err != nil { 184 RespondWithError(w, err) 185 return 186 } 187 188 err = handler.metaStore.DeleteTable(deleteTableRequest.TableName) 189 if err != nil { 190 // TODO: need mapping from metaStore error to api error 191 /// for metaStore error might also be user error 192 RespondWithError(w, err) 193 return 194 } 195 196 RespondWithJSONObject(w, nil) 197 } 198 199 // AddColumn swagger:route POST /schema/tables/{table}/columns addColumn 200 // add a single column to existing table 201 // 202 // Consumes: 203 // - application/json 204 // 205 // Responses: 206 // default: errorResponse 207 // 200: noContentResponse 208 func (handler *SchemaHandler) AddColumn(w http.ResponseWriter, r *http.Request) { 209 var addColumnRequest AddColumnRequest 210 err := ReadRequest(r, &addColumnRequest) 211 if err != nil { 212 RespondWithBadRequest(w, err) 213 return 214 } 215 216 err = handler.metaStore.AddColumn(addColumnRequest.TableName, addColumnRequest.Body.Column, addColumnRequest.Body.AddToArchivingSortOrder) 217 // TODO: validate column 218 // might better do in metaStore and here needs to return either user error or server error 219 if err != nil { 220 // TODO: need mapping from metaStore error to api error 221 /// for metaStore error might also be user error 222 RespondWithError(w, err) 223 return 224 } 225 226 RespondWithJSONObject(w, nil) 227 } 228 229 // UpdateColumn swagger:route PUT /schema/tables/{table}/columns/{column} updateColumn 230 // update specified column 231 // 232 // Consumes: 233 // - application/json 234 // 235 // Responses: 236 // default: errorResponse 237 // 200: noContentResponse 238 func (handler *SchemaHandler) UpdateColumn(w http.ResponseWriter, r *http.Request) { 239 var updateColumnRequest UpdateColumnRequest 240 241 err := ReadRequest(r, &updateColumnRequest) 242 if err != nil { 243 RespondWithError(w, err) 244 return 245 } 246 247 if err = handler.metaStore.UpdateColumn(updateColumnRequest.TableName, 248 updateColumnRequest.ColumnName, updateColumnRequest.Body); err != nil { 249 // TODO: need mapping from metaStore error to api error 250 // for metaStore error might also be user error 251 RespondWithError(w, err) 252 return 253 } 254 255 RespondWithJSONObject(w, nil) 256 } 257 258 // DeleteColumn swagger:route DELETE /schema/tables/{table}/columns/{column} deleteColumn 259 // delete columns from existing table 260 // 261 // Responses: 262 // default: errorResponse 263 // 200: noContentResponse 264 func (handler *SchemaHandler) DeleteColumn(w http.ResponseWriter, r *http.Request) { 265 var deleteColumnRequest DeleteColumnRequest 266 267 err := ReadRequest(r, &deleteColumnRequest) 268 if err != nil { 269 RespondWithError(w, err) 270 return 271 } 272 273 err = handler.metaStore.DeleteColumn(deleteColumnRequest.TableName, deleteColumnRequest.ColumnName) 274 // TODO: validate whether table exists and specified columns does not belong to primary key or time column 275 // might be better for metaStore to do this and return specified error type 276 if err != nil { 277 // TODO: need mapping from metaStore error to api error 278 // for metaStore error might also be user error 279 RespondWithError(w, err) 280 return 281 } 282 283 RespondWithJSONObject(w, nil) 284 }