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  }