diff --git a/supernode/server/api/api.go b/supernode/server/api/api.go new file mode 100644 index 000000000..9873e723d --- /dev/null +++ b/supernode/server/api/api.go @@ -0,0 +1,63 @@ +/* + * Copyright The Dragonfly Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +var handlerCategories map[string][]*HandlerSpec + +const ( + // VersionedPrefix is recommended, any new API should start with this prefix. + VersionedPrefix = "/api/v1" + + // LegacyPrefix is deprecated, just for compatibility with the old version. + LegacyPrefix = "" +) + +var ( + VersionedCategory = &Category{Name: "versioned api", Prefix: VersionedPrefix} + LegacyCategory = &Category{Name: "legacy api", Prefix: LegacyPrefix} +) + +// Category groups the APIs. +type Category struct { + Name string + Prefix string +} + +// Register add an API into supernode server with current api version. +func Register(handlers ...*HandlerSpec) { + for _, h := range handlers { + registerAPI(VersionedCategory, h) + } +} + +// RegisterLegacy is deprecated, just for compatibility with the old version, +// please do not use it to add new API. +func RegisterLegacy(h *HandlerSpec) { + registerAPI(LegacyCategory, h) +} + +func registerAPI(c *Category, h *HandlerSpec) { + if c == nil || validate(h) { + return + } + handlers := handlerCategories[c.Name] + handlerCategories[c.Name] = append(handlers, h) +} + +func validate(h *HandlerSpec) bool { + return h != nil && h.HandlerFunc != nil && h.Method != "" +} diff --git a/supernode/server/api/handler.go b/supernode/server/api/handler.go new file mode 100644 index 000000000..84f7615ae --- /dev/null +++ b/supernode/server/api/handler.go @@ -0,0 +1,32 @@ +/* + * Copyright The Dragonfly Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +import ( + "context" + "net/http" +) + +// HandlerSpec describes a HTTP api +type HandlerSpec struct { + Method string + Path string + HandlerFunc HandlerFunc +} + +// HandlerFunc is the http request handler. +type HandlerFunc func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error diff --git a/supernode/server/api/utils.go b/supernode/server/api/utils.go new file mode 100644 index 000000000..f753b541f --- /dev/null +++ b/supernode/server/api/utils.go @@ -0,0 +1,74 @@ +/* + * Copyright The Dragonfly Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +import ( + "encoding/json" + "io" + "net/http" + + "github.com/dragonflyoss/Dragonfly/apis/types" + "github.com/dragonflyoss/Dragonfly/pkg/errortypes" + + "github.com/go-openapi/strfmt" +) + +// ValidateFunc validates the request parameters. +type ValidateFunc func(registry strfmt.Registry) error + +// ParseJSONRequest parses the request parameter formed by JSON to a object. +func ParseJSONRequest(body io.Reader, request interface{}, validator ValidateFunc) error { + if err := json.NewDecoder(body).Decode(request); err != nil { + if err == io.EOF { + return errortypes.New(http.StatusBadRequest, "empty body") + } + return errortypes.New(http.StatusBadRequest, err.Error()) + } + if validator != nil { + if err := validator(strfmt.NewFormats()); err != nil { + return errortypes.New(http.StatusBadRequest, err.Error()) + } + } + return nil +} + +// EncodeResponse encodes response in json. +func EncodeResponse(rw http.ResponseWriter, statusCode int, data interface{}) error { + rw.Header().Set("Content-Type", "application/json") + rw.WriteHeader(statusCode) + return json.NewEncoder(rw).Encode(data) +} + +// HandleErrorResponse handles err from daemon side and constructs response for client side. +func HandleErrorResponse(w http.ResponseWriter, err error) { + var ( + code int + errMsg string + ) + + // By default, daemon side returns code 500 if error happens. + code = http.StatusInternalServerError + if e, ok := err.(*errortypes.DfError); ok { + code = e.Code + errMsg = e.Msg + } + + _ = EncodeResponse(w, code, types.ErrorResponse{ + Code: int64(code), + Message: errMsg, + }) +} diff --git a/supernode/server/router.go b/supernode/server/router.go index fc34c272a..b2660c3f0 100644 --- a/supernode/server/router.go +++ b/supernode/server/router.go @@ -22,11 +22,12 @@ import ( "net/http" "net/http/pprof" - "github.com/dragonflyoss/Dragonfly/apis/types" - "github.com/dragonflyoss/Dragonfly/version" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/dragonflyoss/Dragonfly/apis/types" + "github.com/dragonflyoss/Dragonfly/version" ) // versionMatcher defines to parse version url path.