| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- // Copyright 2019 Yunion
- //
- // 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 dispatcher
- import (
- "context"
- "fmt"
- "net/http"
- "strings"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/appctx"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient/modulebase"
- )
- func AddModelDispatcher(prefix string, app *appsrv.Application, manager IModelDispatchHandler, isSlave bool) {
- metadata := map[string]interface{}{"manager": manager}
- tags := map[string]string{"resource": manager.KeywordPlural()}
- // list
- h := app.AddHandler2("GET",
- fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
- manager.Filter(listHandler), metadata, "list", tags)
- manager.CustomizeHandlerInfo(h)
- ctxss := manager.ContextKeywordPlurals()
- // list in context
- for _, ctxs := range ctxss {
- segs := make([]string, 0)
- segs = append(segs, prefix)
- for i, ctx := range ctxs {
- segs = append(segs, ctx, fmt.Sprintf("<resid_%d>", i))
- }
- segs = append(segs, manager.KeywordPlural())
- h = app.AddHandler2("GET", strings.Join(segs, "/"),
- manager.Filter(listInContextHandler), metadata, fmt.Sprintf("list_in_%s", strings.Join(ctxs, "_")), tags)
- manager.CustomizeHandlerInfo(h)
- }
- // Head
- h = app.AddHandler2("HEAD",
- fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
- manager.Filter(headHandler), metadata, "head_details", tags)
- manager.CustomizeHandlerInfo(h)
- // Get
- h = app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
- manager.Filter(getHandler), metadata, "get_details", tags)
- manager.CustomizeHandlerInfo(h)
- // get spec
- h = app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/<resid>/<spec>", prefix, manager.KeywordPlural()),
- manager.Filter(getSpecHandler), metadata, "get_specific", tags)
- manager.CustomizeHandlerInfo(h)
- if isSlave {
- // slave node only support get and head
- return
- }
- // create
- // create multi
- h = app.AddHandler2("POST",
- fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
- manager.Filter(createHandler), metadata, "create", tags)
- manager.CustomizeHandlerInfo(h)
- // create in context
- for _, ctxs := range ctxss {
- segs := make([]string, 0)
- segs = append(segs, prefix)
- for i, ctx := range ctxs {
- segs = append(segs, ctx, fmt.Sprintf("<resid_%d>", i))
- }
- segs = append(segs, manager.KeywordPlural())
- h = app.AddHandler2("POST", strings.Join(segs, "/"),
- manager.Filter(createInContextHandler), metadata, fmt.Sprintf("create_in_%s", strings.Join(ctxs, "_")), tags)
- manager.CustomizeHandlerInfo(h)
- }
- // batchPerformAction
- h = app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/<action>", prefix, manager.KeywordPlural()),
- manager.Filter(performClassActionHandler), metadata, "perform_class_action", tags)
- manager.CustomizeHandlerInfo(h)
- // performAction
- h = app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/<resid>/<action>", prefix, manager.KeywordPlural()),
- manager.Filter(performActionHandler), metadata, "perform_action", tags)
- manager.CustomizeHandlerInfo(h)
- // batchUpdate
- /* app.AddHandler2("PUT",
- fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
- manager.Filter(updateClassHandler), metadata, "update_class", tags)
- */
- // update
- h = app.AddHandler2("PUT",
- fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
- manager.Filter(updateHandler), metadata, "update", tags)
- manager.CustomizeHandlerInfo(h)
- // patch
- h = app.AddHandler2("PATCH",
- fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
- manager.Filter(updateHandler), metadata, "patch", tags)
- manager.CustomizeHandlerInfo(h)
- // update/patch in context
- for _, ctxs := range ctxss {
- segs := make([]string, 0)
- segs = append(segs, prefix)
- for i, ctx := range ctxs {
- segs = append(segs, ctx, fmt.Sprintf("<resid_%d>", i))
- }
- segs = append(segs, manager.KeywordPlural(), "<resid>")
- h = app.AddHandler2("PUT", strings.Join(segs, "/"),
- manager.Filter(updateInContextHandler), metadata, fmt.Sprintf("update_in_%s", strings.Join(ctxs, "_")), tags)
- manager.CustomizeHandlerInfo(h)
- h = app.AddHandler2("PATCH", strings.Join(segs, "/"),
- manager.Filter(updateInContextHandler), metadata, fmt.Sprintf("patch_in_%s", strings.Join(ctxs, "_")), tags)
- manager.CustomizeHandlerInfo(h)
- }
- // update spec
- h = app.AddHandler2("PUT",
- fmt.Sprintf("%s/%s/<resid>/<spec>", prefix, manager.KeywordPlural()),
- manager.Filter(updateSpecHandler), metadata, "update_spec", tags)
- manager.CustomizeHandlerInfo(h)
- // patch spec
- h = app.AddHandler2("PATCH",
- fmt.Sprintf("%s/%s/<resid>/<spec>", prefix, manager.KeywordPlural()),
- manager.Filter(updateSpecHandler), metadata, "patch_spec", tags)
- manager.CustomizeHandlerInfo(h)
- // batch Delete
- /* app.AddHandler2("DELTE",
- fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
- manager.Filter(batachDeleteHandler), metadata, "batch_delete", tags)
- */
- // Delete
- h = app.AddHandler2("DELETE",
- fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
- manager.Filter(deleteHandler), metadata, "delete", tags)
- manager.CustomizeHandlerInfo(h)
- // delete in context
- for _, ctxs := range ctxss {
- segs := make([]string, 0)
- segs = append(segs, prefix)
- for i, ctx := range ctxs {
- segs = append(segs, ctx, fmt.Sprintf("<resid_%d>", i))
- }
- segs = append(segs, manager.KeywordPlural(), "<resid>")
- h = app.AddHandler2("DELETE", strings.Join(segs, "/"),
- manager.Filter(deleteInContextHandler), metadata, fmt.Sprintf("delete_in_%s", strings.Join(ctxs, "_")), tags)
- manager.CustomizeHandlerInfo(h)
- }
- // Delete Spec
- h = app.AddHandler2("DELETE",
- fmt.Sprintf("%s/%s/<resid>/<spec>", prefix, manager.KeywordPlural()),
- manager.Filter(deleteSpecHandler), metadata, "delete_spec", tags)
- manager.CustomizeHandlerInfo(h)
- }
- func fetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (IModelDispatchHandler, map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) {
- params, query, body := appsrv.FetchEnv(ctx, w, r)
- metadata := appctx.AppContextMetadata(ctx)
- manager, ok := metadata["manager"].(IModelDispatchHandler)
- if !ok {
- log.Fatalf("No manager found for URL: %s", r.URL)
- }
- return manager, params, query, body
- }
- func mergeQueryParams(params map[string]string, query jsonutils.JSONObject, excludes ...string) jsonutils.JSONObject {
- if query == nil {
- query = jsonutils.NewDict()
- }
- queryDict := query.(*jsonutils.JSONDict)
- for k, v := range params {
- if !utils.IsInStringArray(k, excludes) {
- queryDict.Add(jsonutils.NewString(v), k[1:len(k)-1])
- }
- }
- return queryDict
- }
- func listHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- handleList(ctx, w, manager, nil, mergeQueryParams(params, query))
- }
- func handleList(ctx context.Context, w http.ResponseWriter, manager IModelDispatchHandler, ctxIds []SResourceContext, query jsonutils.JSONObject) {
- listResult, err := manager.List(ctx, query, ctxIds)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- key := manager.KeywordPlural()
- if consts.GetDataResp() {
- key = "data"
- }
- appsrv.SendJSON(w, modulebase.ListResult2JSONWithKey(listResult, key))
- }
- func fetchContextIds(segs []string, params map[string]string) ([]SResourceContext, []string) {
- ctxIds := make([]SResourceContext, 0)
- keys := make([]string, 0)
- idx := 0
- key := fmt.Sprintf("<resid_%d>", idx)
- for i := 0; i < len(segs); i += 1 {
- if segs[i] == key {
- ctxIds = append(ctxIds, SResourceContext{Type: segs[i-1], Id: params[key]})
- keys = append(keys, key)
- idx += 1
- key = fmt.Sprintf("<resid_%d>", idx)
- }
- }
- return ctxIds, keys
- }
- func listInContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- ctxIds, ctxKeys := fetchContextIds(appctx.AppContextCurrentRoot(ctx), params)
- handleList(ctx, w, manager, ctxIds, mergeQueryParams(params, query, ctxKeys...))
- }
- func wrapBody(body jsonutils.JSONObject, key string) jsonutils.JSONObject {
- if body != nil {
- ret := jsonutils.NewDict()
- ret.Add(body, key)
- return ret
- } else {
- return nil
- }
- }
- func headHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- defer func() {
- w.Header().Set("Content-Length", "0")
- w.Write([]byte{})
- }()
- manager, params, query, _ := fetchEnv(ctx, w, r)
- _, err := manager.Get(ctx, params["<resid>"], mergeQueryParams(params, query, "<resid>"), true)
- if err != nil {
- jsonErr := httperrors.NewGeneralError(err)
- httperrors.SendHTTPErrorHeader(w, jsonErr.Code)
- return
- }
- }
- func sendJSON(ctx context.Context, w http.ResponseWriter, result jsonutils.JSONObject, keyword string) {
- appParams := appsrv.AppContextGetParams(ctx)
- var body jsonutils.JSONObject
- if consts.GetDataResp() || appParams != nil && appParams.OverrideResponseBodyWrapper {
- body = result
- } else {
- body = wrapBody(result, keyword)
- }
- appsrv.SendJSON(w, body)
- }
- func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- result, err := manager.Get(ctx, params["<resid>"], mergeQueryParams(params, query, "<resid>"), false)
- if err != nil {
- e := httperrors.NewGeneralError(err)
- httperrors.JsonClientError(ctx, w, e)
- return
- }
- if result != nil {
- sendJSON(ctx, w, result, manager.Keyword())
- }
- }
- func getSpecHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- result, err := manager.GetSpecific(ctx, params["<resid>"], params["<spec>"], mergeQueryParams(params, query, "<resid>", "<spec>"))
- if err != nil {
- e := httperrors.NewGeneralError(err)
- httperrors.JsonClientError(ctx, w, e)
- return
- }
- if result != nil {
- sendJSON(ctx, w, result, manager.Keyword())
- }
- }
- func writeErrNoRequestKey(ctx context.Context, w http.ResponseWriter, r *http.Request, key string) {
- ctx = appctx.WithRequestLang(ctx, r)
- httperrors.InvalidInputError(ctx, w,
- "No request key: %s", key)
- }
- func writeErrInvalidRequestHeader(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
- ctx = appctx.WithRequestLang(ctx, r)
- httperrors.InvalidInputError(ctx, w,
- "Invalid request header: %v", err)
- }
- func createHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- handleCreate(ctx, w, manager, nil, mergeQueryParams(params, query), body, r)
- }
- func handleCreate(ctx context.Context, w http.ResponseWriter, manager IModelDispatchHandler, ctxIds []SResourceContext, query jsonutils.JSONObject, body jsonutils.JSONObject, r *http.Request) {
- count := int64(1)
- var data jsonutils.JSONObject
- var err error
- if body != nil {
- count, _ = body.Int("count")
- data, err = body.Get(manager.Keyword())
- if err != nil {
- writeErrNoRequestKey(ctx, w, r, manager.Keyword())
- return
- }
- } else {
- data, err = manager.FetchCreateHeaderData(ctx, r.Header)
- if err != nil {
- writeErrInvalidRequestHeader(ctx, w, r, err)
- return
- }
- }
- if count <= 1 {
- result, err := manager.Create(ctx, query, data, ctxIds)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- if !consts.GetDataResp() {
- result = wrapBody(result, manager.Keyword())
- }
- appsrv.SendJSON(w, result)
- } else {
- results, err := manager.BatchCreate(ctx, query, data, int(count), ctxIds)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- ret := jsonutils.NewArray()
- for i := 0; i < len(results); i++ {
- res := jsonutils.NewDict()
- res.Add(jsonutils.NewInt(int64(results[i].Status)), "status")
- res.Add(results[i].Data, "body")
- ret.Add(res)
- }
- key := manager.KeywordPlural()
- if consts.GetDataResp() {
- key = "data"
- }
- appsrv.SendJSON(w, wrapBody(ret, key))
- }
- }
- func createInContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- ctxIds, ctxKeys := fetchContextIds(appctx.AppContextCurrentRoot(ctx), params)
- handleCreate(ctx, w, manager, ctxIds, mergeQueryParams(params, query, ctxKeys...), body, r)
- }
- func performClassActionHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- var data jsonutils.JSONObject
- if body != nil {
- if body.Contains(manager.KeywordPlural()) {
- data, _ = body.Get(manager.KeywordPlural())
- if data == nil {
- data = body.(*jsonutils.JSONDict)
- }
- } else {
- data = body
- }
- } else {
- data = jsonutils.NewDict()
- }
- results, err := manager.PerformClassAction(ctx, params["<action>"], mergeQueryParams(params, query, "<action>"), data)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- if results == nil {
- results = jsonutils.NewDict()
- }
- sendJSON(ctx, w, results, manager.KeywordPlural())
- // appsrv.SendJSON(w, wrapBody(results, manager.KeywordPlural()))
- }
- func performActionHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- var data jsonutils.JSONObject
- if body != nil {
- if body.Contains(manager.Keyword()) {
- data, _ = body.Get(manager.Keyword())
- if data == nil {
- data = body.(*jsonutils.JSONDict)
- }
- } else {
- data = body
- }
- } else {
- data = jsonutils.NewDict()
- }
- result, err := manager.PerformAction(ctx, params["<resid>"], params["<action>"], mergeQueryParams(params, query, "<resid>", "<action>"), data)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- sendJSON(ctx, w, result, manager.Keyword())
- // appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
- }
- func updateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- handleUpdate(ctx, w, manager, params["<resid>"], nil, mergeQueryParams(params, query, "<resid>"), body, r)
- }
- func handleUpdate(ctx context.Context, w http.ResponseWriter, manager IModelDispatchHandler, resId string, ctxIds []SResourceContext, query jsonutils.JSONObject, body jsonutils.JSONObject, r *http.Request) {
- var data jsonutils.JSONObject
- var err error
- if body != nil {
- if body.Contains(manager.Keyword()) {
- data, err = body.Get(manager.Keyword())
- if err != nil {
- writeErrNoRequestKey(ctx, w, r, manager.Keyword())
- return
- }
- } else {
- data = body
- }
- } else {
- data, err = manager.FetchUpdateHeaderData(ctx, r.Header)
- if err != nil {
- writeErrInvalidRequestHeader(ctx, w, r, err)
- return
- }
- }
- result, err := manager.Update(ctx, resId, query, data, ctxIds)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- sendJSON(ctx, w, result, manager.Keyword())
- // appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
- }
- func updateInContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- ctxIds, ctxKeys := fetchContextIds(appctx.AppContextCurrentRoot(ctx), params)
- ctxKeys = append(ctxKeys, "<resid>")
- handleUpdate(ctx, w, manager, params["<resid>"], ctxIds, mergeQueryParams(params, query, ctxKeys...), body, r)
- }
- func updateSpecHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- var data jsonutils.JSONObject
- var err error
- if body != nil {
- if body.Contains(manager.Keyword()) {
- data, err = body.Get(manager.Keyword())
- if err != nil {
- writeErrNoRequestKey(ctx, w, r, manager.Keyword())
- return
- }
- } else {
- data = body
- }
- } else {
- data, err = manager.FetchUpdateHeaderData(ctx, r.Header)
- if err != nil {
- writeErrInvalidRequestHeader(ctx, w, r, err)
- return
- }
- }
- result, err := manager.UpdateSpec(ctx, params["<resid>"], params["<spec>"], mergeQueryParams(params, query, "<resid>", "<spec>"), data)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- sendJSON(ctx, w, result, manager.Keyword())
- // appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
- }
- func deleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- handleDelete(ctx, w, manager, params["<resid>"], nil, mergeQueryParams(params, query, "<resid>"), body, r)
- }
- func handleDelete(ctx context.Context, w http.ResponseWriter, manager IModelDispatchHandler, resId string, ctxIds []SResourceContext, query jsonutils.JSONObject, body jsonutils.JSONObject, r *http.Request) {
- var data jsonutils.JSONObject
- var err error
- if body != nil {
- if body.Contains(manager.Keyword()) {
- data, err = body.Get(manager.Keyword())
- if err != nil {
- writeErrNoRequestKey(ctx, w, r, manager.Keyword())
- return
- }
- } else {
- data = body
- }
- } else {
- data = jsonutils.NewDict()
- }
- // doDelete
- result, err := manager.Delete(ctx, resId, query, data, ctxIds)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- sendJSON(ctx, w, result, manager.Keyword())
- // appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
- }
- func deleteInContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- ctxIds, ctxKeys := fetchContextIds(appctx.AppContextCurrentRoot(ctx), params)
- ctxKeys = append(ctxKeys, "<resid>")
- handleDelete(ctx, w, manager, params["<resid>"], ctxIds, mergeQueryParams(params, query, ctxKeys...), body, r)
- }
- func deleteSpecHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- var data jsonutils.JSONObject
- var err error
- if body != nil {
- if body.Contains(manager.Keyword()) {
- data, err = body.Get(manager.Keyword())
- if err != nil {
- writeErrNoRequestKey(ctx, w, r, manager.Keyword())
- return
- }
- } else {
- data = body
- }
- } else {
- data = jsonutils.NewDict()
- }
- result, err := manager.DeleteSpec(ctx, params["<resid>"], params["<spec>"], mergeQueryParams(params, query, "<resid>", "<spec>"), data)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- sendJSON(ctx, w, result, manager.Keyword())
- // appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
- }
|