fix(cloudcommon): add crud note (#16870)

Co-authored-by: 马鸿飞 <mahongfei@yunion.cn>
This commit is contained in:
gouqi11
2023-04-27 14:34:49 +08:00
committed by GitHub
parent 2eb92ddde1
commit 75814646cf
4 changed files with 47 additions and 2 deletions
+1
View File
@@ -513,6 +513,7 @@ func handleDelete(ctx context.Context, w http.ResponseWriter, manager IModelDisp
} else {
data = jsonutils.NewDict()
}
// doDelete
result, err := manager.Delete(ctx, resId, query, data, ctxIds)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
+40 -1
View File
@@ -528,6 +528,7 @@ func fetchContextObject(manager IModelManager, ctx context.Context, userCred mcc
}
func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (*printutils.ListResult, error) {
// 获取常规参数
var err error
var maxLimit int64 = consts.GetMaxPagingLimit()
limit, _ := query.Int("limit")
@@ -824,6 +825,7 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok
return calculateListResult(retList, totalCnt, totalJson, int(limit), int(offset), paginate), nil
}
// 构造list返回详情
func calculateListResult(data []jsonutils.JSONObject, total int, totalJson jsonutils.JSONObject, limit, offset int, paginate bool) *printutils.ListResult {
if paginate {
// do offset first
@@ -866,9 +868,11 @@ func getExportCols(query jsonutils.JSONObject, retList []jsonutils.JSONObject) [
}
func (dispatcher *DBModelDispatcher) List(ctx context.Context, query jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (*printutils.ListResult, error) {
// 获取用户信息
userCred := fetchUserCredential(ctx)
manager := dispatcher.manager.GetImmutableInstance(ctx, userCred, query)
// list详情
items, err := ListItems(manager, ctx, userCred, query, ctxIds)
if err != nil {
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "ListItems"))
@@ -1161,6 +1165,7 @@ func FetchIModelObjects(modelManager IModelManager, query *sqlchemy.SQuery) ([]I
}
func DoCreate(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ownerId mcclient.IIdentityProvider) (IModel, error) {
// 锁住一类实例
lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
@@ -1174,6 +1179,7 @@ func doCreateItem(
return _doCreateItem(manager, ctx, userCred, ownerId, query, data, false, 1)
}
// 批量创建
func batchCreateDoCreateItem(
manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject, data jsonutils.JSONObject, baseIndex int) (IModel, error) {
@@ -1181,6 +1187,7 @@ func batchCreateDoCreateItem(
return _doCreateItem(manager, ctx, userCred, ownerId, query, data, true, baseIndex)
}
// 对于modelManager的实际创建过程
func _doCreateItem(
manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject, data jsonutils.JSONObject, batchCreate bool, baseIndex int) (IModel, error) {
@@ -1193,6 +1200,7 @@ func _doCreateItem(
var err error
var generateName string
// 若manager存在name字段且请求包含generate_name,则根据name从数据库中获取相同名称添加后缀
if manager.HasName() {
if dataDict.Contains("generate_name") {
generateName, _ = dataDict.GetString("generate_name")
@@ -1224,12 +1232,13 @@ func _doCreateItem(
if batchCreate {
funcName = "BatchCreateValidateCreateData"
}
// 校验创建请求入参
dataDict, err = ValidateCreateData(funcName, manager, ctx, userCred, ownerId, query, dataDict)
if err != nil {
return nil, err
}
// 若manager用于name字段,确保name唯一
if manager.HasName() {
// run name validation after validate create data
uniqValues := manager.FetchUniqValues(ctx, dataDict)
@@ -1242,19 +1251,23 @@ func _doCreateItem(
}
}
// 检查models定义中tag指定required
err = jsonutils.CheckRequiredFields(dataDict, createRequireFields(manager, userCred))
if err != nil {
return nil, httperrors.NewInputParameterError("%v", err)
}
// 初始化model
model, err := NewModelObject(manager)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
// 检查models定义中tag指定create
filterData := dataDict.CopyIncludes(createFields(manager, userCred)...)
err = filterData.Unmarshal(model)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
// 实际创建前钩子
err = model.CustomizeCreate(ctx, userCred, ownerId, query, dataDict)
if err != nil {
return nil, httperrors.NewGeneralError(err)
@@ -1268,6 +1281,7 @@ func _doCreateItem(
return model, nil
}
// 插入数据库记录
if manager.CreateByInsertOrUpdate() {
err = manager.TableSpec().InsertOrUpdate(ctx, model)
} else {
@@ -1295,6 +1309,7 @@ func (dispatcher *DBModelDispatcher) FetchCreateHeaderData(ctx context.Context,
}
func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (jsonutils.JSONObject, error) {
// 获取用户信息
userCred := fetchUserCredential(ctx)
manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
@@ -1314,6 +1329,7 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
}
}
// 用户角色校验
var policyResult rbacutils.SPolicyResult
policyResult, err = isClassRbacAllowed(ctx, manager, userCred, ownerId, policy.PolicyActionCreate)
if err != nil {
@@ -1329,6 +1345,7 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
// inject tag filters imposed by policy
data.(*jsonutils.JSONDict).Update(policyResult.Json())
// 资源实际创建函数
model, err := DoCreate(manager, ctx, userCred, query, data, ownerId)
if err != nil {
// validate failed, clean pending usage
@@ -1345,6 +1362,7 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
return nil, httperrors.NewGeneralError(err)
}
// 伪创建
if dryRun {
// dry run, clean pending usage
if CancelPendingUsagesInContext != nil {
@@ -1356,6 +1374,7 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
return getItemDetails(manager, model, ctx, userCred, query)
}
// 资源创建完成后所需执行的任务(创建完成指在数据库中存在数据)
func() {
lockman.LockObject(ctx, model)
defer lockman.ReleaseObject(ctx, model)
@@ -1363,6 +1382,7 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
model.PostCreate(ctx, userCred, ownerId, query, data)
}()
// 添加操作日志与消息通知
{
notes := model.GetShortDesc(ctx)
OpsLog.LogEvent(model, ACT_CREATE, notes, userCred)
@@ -1522,12 +1542,14 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
}
func (dispatcher *DBModelDispatcher) PerformClassAction(ctx context.Context, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
// 伪创建,校验创建参数
if action == "check-create-data" {
dataDict := data.(*jsonutils.JSONDict)
dataDict.Set("dry_run", jsonutils.JSONTrue)
return dispatcher.Create(ctx, query, dataDict, nil)
}
// 获取用户信息
userCred := fetchUserCredential(ctx)
manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
@@ -1536,6 +1558,7 @@ func (dispatcher *DBModelDispatcher) PerformClassAction(ctx context.Context, act
return nil, httperrors.NewGeneralError(err)
}
// 锁住一类
lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
@@ -1559,6 +1582,7 @@ func (dispatcher *DBModelDispatcher) PerformAction(ctx context.Context, idStr st
if err := model.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil {
return nil, err
}
// 通过action与实例执行请求
return objectPerformAction(manager, model, reflect.ValueOf(model), ctx, userCred, action, query, data)
}
@@ -1588,6 +1612,7 @@ func reflectDispatcher(
return result, err
}
}
func reflectDispatcherInternal(
// dispatcher *DBModelDispatcher,
manager IModelManager,
@@ -1603,9 +1628,11 @@ func reflectDispatcherInternal(
data jsonutils.JSONObject,
) (jsonutils.JSONObject, error) {
isGeneral := false
// 优先通过action查找该model下的PerformXXX方法
funcName := fmt.Sprintf("%s%s", funcPrefix, utils.Kebab2Camel(spec, "-"))
funcValue := modelValue.MethodByName(funcName)
// 若不存在该方法则根据generalFuncName查找model下的PerformAction方法
if !funcValue.IsValid() || funcValue.IsNil() {
funcValue = modelValue.MethodByName(generalFuncName)
if !funcValue.IsValid() || funcValue.IsNil() {
@@ -1625,6 +1652,7 @@ func reflectDispatcherInternal(
params = []interface{}{ctx, userCred, query, data}
}
// 若perform指定一类资源,则当前用户对一类资源的权限,否则校验用户对该资源的权限
var result rbacutils.SPolicyResult
if model == nil {
ownerId, err := fetchOwnerId(ctx, manager, userCred, data)
@@ -1643,10 +1671,13 @@ func reflectDispatcherInternal(
}
}
// 调用反射的方法
outs, err := callFunc(funcValue, funcName, params...)
if err != nil {
return nil, err
}
// perform方法返回值为jsonutils.JSONObject,error
// 对于perform方法返回值数量不为2时,默认不合法
if len(outs) != 2 {
return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
}
@@ -1676,6 +1707,7 @@ func reflectDispatcherInternal(
func updateItem(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
var err error
// 校验update入参钩子
err = item.ValidateUpdateCondition(ctx)
if err != nil {
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "ValidateUpdateCondition"))
@@ -1822,16 +1854,19 @@ func RealDeleteModel(ctx context.Context, userCred mcclient.TokenCredential, ite
}
func deleteItem(manager IModelManager, model IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
// 获取实例详情
details, err := getItemDetails(manager, model, ctx, userCred, query)
if err != nil {
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "getItemDetails"))
}
// 删除校验
err = ValidateDeleteCondition(model, ctx, details)
if err != nil {
return nil, err
}
// 删除前钩子
err = CustomizeDelete(model, ctx, userCred, query, data)
if err != nil {
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "CustomizeDelete"))
@@ -1839,11 +1874,13 @@ func deleteItem(manager IModelManager, model IModel, ctx context.Context, userCr
model.PreDelete(ctx, userCred)
// 实际删除
err = model.Delete(ctx, userCred)
if err != nil {
return nil, errors.Wrapf(err, "Delete")
}
// 删除后钩子
model.PostDelete(ctx, userCred)
// 避免设置删除状态没有正常返回
@@ -1855,6 +1892,7 @@ func (dispatcher *DBModelDispatcher) Delete(ctx context.Context, idstr string, q
userCred := fetchUserCredential(ctx)
manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
// 找到实例
model, err := fetchItem(manager, ctx, userCred, idstr, nil)
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idstr)
@@ -1862,6 +1900,7 @@ func (dispatcher *DBModelDispatcher) Delete(ctx context.Context, idstr string, q
return nil, httperrors.NewGeneralError(err)
}
// 校验角色
err = isObjectRbacAllowed(ctx, model, userCred, policy.PolicyActionDelete)
if err != nil {
return nil, err
@@ -227,9 +227,11 @@ func (dispatcher *DBJointModelDispatcher) Attach(ctx context.Context, id1 string
}
func (dispatcher *DBJointModelDispatcher) Update(ctx context.Context, id1 string, id2 string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
// 获取用户信息
userCred := fetchUserCredential(ctx)
manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
// 获取对象与关联表(such as guestdisks_tbl)
master, slave, item, err := fetchJointItem(dispatcher, ctx, userCred, id1, id2, query)
if err == sql.ErrNoRows {
if jsonutils.QueryBoolean(query, "auto_create", false) {
@@ -242,11 +244,13 @@ func (dispatcher *DBJointModelDispatcher) Update(ctx context.Context, id1 string
return nil, httperrors.NewGeneralError(err)
}
// 判断权限
err = isJointObjectRbacAllowed(ctx, item, userCred, policy.PolicyActionUpdate)
if err != nil {
return nil, err
}
// 锁住实例与关联表
lockman.LockJointObject(ctx, master, slave)
defer lockman.ReleaseJointObject(ctx, master, slave)
return updateItem(dispatcher.JointModelManager(), item, ctx, userCred, query, data)
+2 -1
View File
@@ -167,8 +167,9 @@ type IModel interface {
// AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool
GetExtraDetailsHeaders(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) map[string]string
// create hooks
// before create hooks
CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error
// after create hooks
PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject)
// allow perform action