caller.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. // Copyright 2019 Yunion
  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. package db
  15. import (
  16. "context"
  17. "reflect"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/pkg/gotypes"
  22. "yunion.io/x/pkg/util/version"
  23. "yunion.io/x/sqlchemy"
  24. "yunion.io/x/onecloud/pkg/apis"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. "yunion.io/x/onecloud/pkg/util/stringutils2"
  28. )
  29. type Caller struct {
  30. modelVal reflect.Value
  31. funcName string
  32. inputs []interface{}
  33. funcVal reflect.Value
  34. }
  35. func NewCaller(model interface{}, fName string) *Caller {
  36. return &Caller{
  37. modelVal: reflect.ValueOf(model),
  38. funcName: fName,
  39. }
  40. }
  41. func (c *Caller) Inputs(inputs ...interface{}) *Caller {
  42. c.inputs = inputs
  43. return c
  44. }
  45. func (c *Caller) Call() ([]reflect.Value, error) {
  46. return callObject(c.modelVal, c.funcName, c.inputs...)
  47. }
  48. func call(obj interface{}, fName string, inputs ...interface{}) ([]reflect.Value, error) {
  49. return callObject(reflect.ValueOf(obj), fName, inputs...)
  50. }
  51. func findFunc(modelVal reflect.Value, fName string) (reflect.Value, error) {
  52. funcVal := modelVal.MethodByName(fName)
  53. if !funcVal.IsValid() || funcVal.IsNil() {
  54. log.Debugf("find method %s for %s", fName, modelVal.Type())
  55. if modelVal.Kind() != reflect.Ptr {
  56. return funcVal, errors.Wrapf(httperrors.ErrNotImplemented, "%s not implemented", fName)
  57. }
  58. modelVal = modelVal.Elem()
  59. if modelVal.Kind() != reflect.Struct {
  60. return funcVal, errors.Wrapf(httperrors.ErrNotImplemented, "%s not implemented", fName)
  61. }
  62. modelType := modelVal.Type()
  63. for i := 0; i < modelType.NumField(); i += 1 {
  64. fieldType := modelType.Field(i)
  65. if fieldType.Anonymous {
  66. fieldValue := modelVal.Field(i)
  67. if fieldValue.Kind() != reflect.Ptr && fieldValue.CanAddr() {
  68. newFuncVal, err := findFunc(fieldValue.Addr(), fName)
  69. if err == nil {
  70. if !funcVal.IsValid() || funcVal.IsNil() {
  71. funcVal = newFuncVal
  72. } else {
  73. return funcVal, errors.Wrapf(httperrors.ErrConflict, "%s is ambiguous", fName)
  74. }
  75. }
  76. } else if fieldValue.Kind() == reflect.Ptr {
  77. newFuncVal, err := findFunc(fieldValue, fName)
  78. if err == nil {
  79. if !funcVal.IsValid() || funcVal.IsNil() {
  80. funcVal = newFuncVal
  81. } else {
  82. return funcVal, errors.Wrapf(httperrors.ErrConflict, "%s is ambiguous", fName)
  83. }
  84. }
  85. }
  86. }
  87. }
  88. if !funcVal.IsValid() || funcVal.IsNil() {
  89. return funcVal, errors.Wrapf(httperrors.ErrNotImplemented, "%s is not implemented", fName)
  90. }
  91. }
  92. return funcVal, nil
  93. }
  94. const (
  95. MethodNotFoundError = errors.Error("MethodNotFoundError")
  96. )
  97. func callObject(modelVal reflect.Value, fName string, inputs ...interface{}) ([]reflect.Value, error) {
  98. funcVal := modelVal.MethodByName(fName)
  99. if !funcVal.IsValid() || funcVal.IsNil() {
  100. return nil, errors.Wrapf(MethodNotFoundError, "%s method not found, please check service version, current version: %s", fName, version.GetShortString())
  101. }
  102. return callFunc(funcVal, fName, inputs...)
  103. }
  104. func callFunc(funcVal reflect.Value, fName string, inputs ...interface{}) ([]reflect.Value, error) {
  105. funcType := funcVal.Type()
  106. paramLen := funcType.NumIn()
  107. if paramLen != len(inputs) {
  108. return nil, httperrors.NewInternalServerError("%s method params length not match, expected %d, input %d", fName, paramLen, len(inputs))
  109. }
  110. params := make([]*param, paramLen)
  111. for i := range inputs {
  112. params[i] = newParam(funcType.In(i), inputs[i])
  113. }
  114. args, err := convertParams(params)
  115. if err != nil {
  116. return nil, err
  117. }
  118. return funcVal.Call(args), nil
  119. }
  120. func convertParams(params []*param) ([]reflect.Value, error) {
  121. ret := make([]reflect.Value, 0)
  122. for _, p := range params {
  123. val, err := p.convert()
  124. if err != nil {
  125. return ret, err
  126. }
  127. ret = append(ret, val)
  128. }
  129. return ret, nil
  130. }
  131. type param struct {
  132. pType reflect.Type
  133. input interface{}
  134. }
  135. func newParam(pType reflect.Type, input interface{}) *param {
  136. return &param{
  137. pType: pType,
  138. input: input,
  139. }
  140. }
  141. func isJSONObject(input interface{}) (jsonutils.JSONObject, bool) {
  142. val := reflect.ValueOf(input)
  143. obj, ok := val.Interface().(jsonutils.JSONObject)
  144. if !ok {
  145. return nil, false
  146. }
  147. return obj, true
  148. }
  149. func (p *param) convert() (reflect.Value, error) {
  150. if p.input == nil {
  151. return reflect.New(p.pType).Elem(), nil
  152. }
  153. obj, ok := isJSONObject(p.input)
  154. if !ok {
  155. return reflect.ValueOf(p.input), nil
  156. }
  157. // generate object by type
  158. val := reflect.New(p.pType)
  159. err := obj.Unmarshal(val.Interface())
  160. if err != nil {
  161. return reflect.Value{}, errors.Wrapf(err, "unable to convert '%v' to Type %q", p.input, p.pType.Name())
  162. }
  163. return val.Elem(), nil
  164. }
  165. func ValueToJSONObject(out reflect.Value) jsonutils.JSONObject {
  166. return _valueToJSONObject(out, false)
  167. }
  168. func _valueToJSONObject(out reflect.Value, allFields bool) jsonutils.JSONObject {
  169. if gotypes.IsNil(out.Interface()) {
  170. return nil
  171. }
  172. if obj, ok := isJSONObject(out); ok {
  173. return obj
  174. }
  175. if allFields {
  176. return jsonutils.MarshalAll(out.Interface())
  177. } else {
  178. return jsonutils.Marshal(out.Interface())
  179. }
  180. }
  181. func ValueToJSONDict(out reflect.Value) *jsonutils.JSONDict {
  182. return _valueToJSONDict(out, false)
  183. }
  184. func _valueToJSONDict(out reflect.Value, allFields bool) *jsonutils.JSONDict {
  185. jsonObj := _valueToJSONObject(out, allFields)
  186. if jsonObj == nil {
  187. return nil
  188. }
  189. return jsonObj.(*jsonutils.JSONDict)
  190. }
  191. func ValueToError(out reflect.Value) error {
  192. errVal := out.Interface()
  193. if !gotypes.IsNil(errVal) {
  194. return errVal.(error)
  195. }
  196. return nil
  197. }
  198. func mergeInputOutputData(input *jsonutils.JSONDict, resVal reflect.Value) *jsonutils.JSONDict {
  199. output := _valueToJSONDict(resVal, true)
  200. // preserve the input info not returned by caller
  201. ret := input.Copy()
  202. jsonMap, _ := output.GetMap()
  203. for k, v := range jsonMap {
  204. if input.Contains(k) && v == jsonutils.JSONNull {
  205. ret.Remove(k)
  206. continue
  207. }
  208. if v != jsonutils.JSONNull && !v.IsZero() {
  209. ret.Set(k, v)
  210. }
  211. }
  212. return ret
  213. }
  214. func ValidateCreateData(funcName string, manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
  215. ret, err := call(manager, funcName, ctx, userCred, ownerId, query, data)
  216. if err != nil {
  217. return nil, httperrors.NewGeneralError(err)
  218. }
  219. if len(ret) != 2 {
  220. return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
  221. }
  222. resVal := ret[0]
  223. if err := ValueToError(ret[1]); err != nil {
  224. return nil, err
  225. }
  226. return mergeInputOutputData(data, resVal), nil
  227. }
  228. func ExpandBatchCreateData(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict, index int) (*jsonutils.JSONDict, error) {
  229. funcName := "ExpandBatchCreateData"
  230. ret, err := call(manager, funcName, ctx, userCred, ownerId, query, data, index)
  231. if err != nil {
  232. return nil, errors.Wrapf(err, "call %s", funcName)
  233. }
  234. if len(ret) != 2 {
  235. return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
  236. }
  237. resVal := ret[0]
  238. if err := ValueToError(ret[1]); err != nil {
  239. return nil, errors.Wrap(err, "ValueToError")
  240. }
  241. return mergeInputOutputData(data, resVal), nil
  242. }
  243. func ListItemFilter(manager IModelManager, ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
  244. return _callListQueryFilter(manager, "ListItemFilter", ctx, q, userCred, query)
  245. }
  246. func ExtendListQuery(manager IModelManager, ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
  247. return _callListQueryFilter(manager, "ExtendListQuery", ctx, q, userCred, query)
  248. }
  249. func _callListQueryFilter(manager IModelManager, funcName string, ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
  250. ret, err := call(manager, funcName, ctx, q, userCred, query)
  251. if err != nil {
  252. return nil, httperrors.NewGeneralError(err)
  253. }
  254. if len(ret) != 2 {
  255. return nil, httperrors.NewInternalServerError("Invald %s return value count %d", funcName, len(ret))
  256. }
  257. if err := ValueToError(ret[1]); err != nil {
  258. return nil, err
  259. }
  260. return ret[0].Interface().(*sqlchemy.SQuery), nil
  261. }
  262. func OrderByExtraFields(
  263. manager IModelManager,
  264. ctx context.Context,
  265. q *sqlchemy.SQuery,
  266. userCred mcclient.TokenCredential,
  267. query jsonutils.JSONObject,
  268. ) (*sqlchemy.SQuery, error) {
  269. ret, err := call(manager, "OrderByExtraFields", ctx, q, userCred, query)
  270. if err != nil {
  271. return nil, httperrors.NewGeneralError(err)
  272. }
  273. if len(ret) != 2 {
  274. return nil, httperrors.NewInternalServerError("Invald OrderByExtraFields return value count %d", len(ret))
  275. }
  276. if err := ValueToError(ret[1]); err != nil {
  277. return nil, err
  278. }
  279. return ret[0].Interface().(*sqlchemy.SQuery), nil
  280. }
  281. func FetchCustomizeColumns(
  282. manager IModelManager,
  283. ctx context.Context,
  284. userCred mcclient.TokenCredential,
  285. query jsonutils.JSONObject,
  286. objs []interface{},
  287. fields stringutils2.SSortedStrings,
  288. isList bool,
  289. ) ([]*jsonutils.JSONDict, error) {
  290. ret, err := call(manager, "FetchCustomizeColumns", ctx, userCred, query, objs, fields, isList)
  291. if err != nil {
  292. return nil, httperrors.NewGeneralError(err)
  293. }
  294. if len(ret) != 1 {
  295. return nil, httperrors.NewInternalServerError("Invalid FetchCustomizeColumns return value count %d", len(ret))
  296. }
  297. if ret[0].IsNil() {
  298. return nil, nil
  299. }
  300. if ret[0].Kind() != reflect.Slice {
  301. return nil, httperrors.NewInternalServerError("Invalid FetchCustomizeColumns return value type, not a slice!")
  302. }
  303. if ret[0].Len() != len(objs) {
  304. return nil, httperrors.NewInternalServerError("Invalid FetchCustomizeColumns return value, inconsistent obj count: input %d != output %d", len(objs), ret[0].Len())
  305. }
  306. showReason := false
  307. if query.Contains("show_fail_reason") {
  308. showReason = true
  309. }
  310. retVal := make([]*jsonutils.JSONDict, ret[0].Len())
  311. for i := 0; i < ret[0].Len(); i += 1 {
  312. jsonDict := ValueToJSONDict(ret[0].Index(i))
  313. // NOTE: don't use obj update jsonDict as retval
  314. jsonDict.Update(jsonutils.Marshal(objs[i]).(*jsonutils.JSONDict))
  315. out := apis.ModelBaseDetails{
  316. CanDelete: true,
  317. CanUpdate: true,
  318. }
  319. err = ValidateDeleteCondition(objs[i].(IModel), ctx, jsonDict)
  320. if err != nil {
  321. out.CanDelete = false
  322. if showReason {
  323. out.DeleteFailReason = httperrors.NewErrorFromGeneralError(ctx, err)
  324. }
  325. }
  326. err = ValidateUpdateCondition(objs[i].(IModel), ctx)
  327. if err != nil {
  328. out.CanUpdate = false
  329. if showReason {
  330. out.UpdateFailReason = httperrors.NewErrorFromGeneralError(ctx, err)
  331. }
  332. }
  333. jsonDict.Update(jsonutils.Marshal(out))
  334. retVal[i] = jsonDict
  335. }
  336. return retVal, nil
  337. }
  338. func ValidateUpdateData(model IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
  339. ret, err := call(model, "ValidateUpdateData", ctx, userCred, query, data)
  340. if err != nil {
  341. return nil, httperrors.NewGeneralError(err)
  342. }
  343. if len(ret) != 2 {
  344. return nil, httperrors.NewInternalServerError("Invald ValidateUpdateData return value")
  345. }
  346. resVal := ret[0]
  347. if err := ValueToError(ret[1]); err != nil {
  348. return nil, err
  349. }
  350. return mergeInputOutputData(data, resVal), nil
  351. }
  352. func CustomizeDelete(model IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  353. ret, err := call(model, "CustomizeDelete", ctx, userCred, query, data)
  354. if err != nil {
  355. return httperrors.NewGeneralError(err)
  356. }
  357. if len(ret) != 1 {
  358. return httperrors.NewInternalServerError("Invald CustomizeDelete return value")
  359. }
  360. return ValueToError(ret[0])
  361. }
  362. func ValidateDeleteCondition(model IModel, ctx context.Context, data jsonutils.JSONObject) error {
  363. ret, err := call(model, "ValidateDeleteCondition", ctx, data)
  364. if err != nil {
  365. return httperrors.NewGeneralError(err)
  366. }
  367. if len(ret) != 1 {
  368. return httperrors.NewInternalServerError("Invald ValidateDeleteCondition return value")
  369. }
  370. return ValueToError(ret[0])
  371. }
  372. func ValidateUpdateCondition(model IModel, ctx context.Context) error {
  373. ret, err := call(model, "ValidateUpdateCondition", ctx)
  374. if err != nil {
  375. return httperrors.NewGeneralError(err)
  376. }
  377. if len(ret) != 1 {
  378. return httperrors.NewInternalServerError("Invald ValidateUpdateCondition return value")
  379. }
  380. return ValueToError(ret[0])
  381. }