|
- package service
-
- import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "os"
- "strings"
-
- "github.com/QuantumNous/new-api/common"
- "github.com/QuantumNous/new-api/model"
- cmerrs "gitlab.ecloud.com/ecloud/ecloudsdkcore/errs"
- cmmodel "gitlab.ecloud.com/ecloud/ecloudsdkmaas/model"
- )
-
- const defaultChinaMobileAssetBaseURL = "https://ecloud.10086.cn"
- const defaultChinaMobileAssetPoolID = "CIDC-CORE-00"
- const chinaMobileAssetAKEnv = "CHINAMOBILE_ASSET_AK"
- const chinaMobileAssetSKEnv = "CHINAMOBILE_ASSET_SK"
- const chinaMobileAssetPoolIDEnv = "CHINAMOBILE_ASSET_POOL_ID"
-
- type ChinaMobileAssetAdapter struct {
- newClient func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient
- }
-
- func NewChinaMobileAssetAdapter() AssetAdapter {
- return &ChinaMobileAssetAdapter{newClient: newChinaMobileAssetSDKClient}
- }
-
- func (a *ChinaMobileAssetAdapter) Name() string {
- return "chinamobile_asset"
- }
-
- func (a *ChinaMobileAssetAdapter) Supports(operation AssetOperation) bool {
- switch operation {
- case AssetOperationAssetCreate,
- AssetOperationAssetList,
- AssetOperationAssetGet,
- AssetOperationAssetUpdate,
- AssetOperationAssetDelete,
- AssetOperationAssetGroupCreate,
- AssetOperationAssetGroupList,
- AssetOperationAssetGroupGet,
- AssetOperationAssetGroupUpdate,
- AssetOperationAssetGroupDelete:
- return true
- default:
- return false
- }
- }
-
- func (a *ChinaMobileAssetAdapter) DoAssetRequest(ctx context.Context, channel *model.Channel, req AssetRequest) (*AssetUpstreamResponse, *AssetError) {
- if !a.Supports(req.Action.Operation) {
- return nil, newAssetError(AssetErrorOperationNotSupported, fmt.Sprintf("asset operation %s is not supported", req.Action.Operation), http.StatusBadRequest)
- }
- if err := validateChinaMobileCompatibility(req.Body); err != nil {
- return nil, err
- }
- if err := validateChinaMobileAssetRequest(req); err != nil {
- return nil, err
- }
-
- credential, err := chinaMobileAssetCredentialFromEnv()
- if err != nil {
- return nil, newAssetError(AssetErrorInvalidRequest, err.Error(), http.StatusBadRequest)
- }
- newClient := a.newClient
- if newClient == nil {
- newClient = newChinaMobileAssetSDKClient
- }
- result, err := callChinaMobileAssetSDK(ctx, newClient(credential), req)
- if err != nil {
- if assetErr := classifyChinaMobileAssetSDKError(err); assetErr != nil {
- return nil, assetErr
- }
- return nil, newAssetError(AssetErrorUpstream, err.Error(), http.StatusBadGateway)
- }
- normalized, assetErr := normalizeChinaMobileAssetSDKResponse(req.Action, req.Version, result)
- if assetErr != nil {
- return nil, assetErr
- }
- return &AssetUpstreamResponse{StatusCode: http.StatusOK, Header: http.Header{}, Body: normalized}, nil
- }
-
- func joinChinaMobileAssetURL(baseURL string, path string) (string, error) {
- u, err := url.Parse(baseURL)
- if err != nil {
- return "", err
- }
- u.Path = strings.TrimRight(u.Path, "/") + path
- u.RawQuery = ""
- u.Fragment = ""
- return u.String(), nil
- }
-
- type chinaMobileAssetCredential struct {
- AK string `json:"ak"`
- SK string `json:"sk"`
- PoolID string `json:"pool_id"`
- }
-
- func chinaMobileAssetCredentialFromEnv() (chinaMobileAssetCredential, error) {
- return normalizeChinaMobileAssetCredential(chinaMobileAssetCredential{
- AK: os.Getenv(chinaMobileAssetAKEnv),
- SK: os.Getenv(chinaMobileAssetSKEnv),
- PoolID: os.Getenv(chinaMobileAssetPoolIDEnv),
- })
- }
-
- func normalizeChinaMobileAssetCredential(credential chinaMobileAssetCredential) (chinaMobileAssetCredential, error) {
- credential.AK = strings.TrimSpace(credential.AK)
- credential.SK = strings.TrimSpace(credential.SK)
- credential.PoolID = strings.TrimSpace(credential.PoolID)
- if credential.AK == "" || credential.SK == "" {
- return chinaMobileAssetCredential{}, fmt.Errorf("%s and %s are required for China Mobile asset library", chinaMobileAssetAKEnv, chinaMobileAssetSKEnv)
- }
- if credential.PoolID == "" {
- credential.PoolID = defaultChinaMobileAssetPoolID
- }
- return credential, nil
- }
-
- func callChinaMobileAssetSDK(ctx context.Context, client chinaMobileAssetSDKClient, req AssetRequest) (any, error) {
- type sdkResult struct {
- value any
- err error
- }
- resultCh := make(chan sdkResult, 1)
- go func() {
- value, err := executeChinaMobileAssetSDK(client, req)
- resultCh <- sdkResult{value: value, err: err}
- }()
- select {
- case <-ctx.Done():
- return nil, ctx.Err()
- case result := <-resultCh:
- return result.value, result.err
- }
- }
-
- func executeChinaMobileAssetSDK(client chinaMobileAssetSDKClient, req AssetRequest) (any, error) {
- switch req.Action.Operation {
- case AssetOperationAssetCreate:
- return client.CreateAsset(&cmmodel.CreateAssetRequest{CreateAssetBody: newChinaMobileCreateAssetBody(req.Body)})
- case AssetOperationAssetList:
- return client.ListAssets(&cmmodel.ListAssetsRequest{ListAssetsBody: newChinaMobileListAssetsBody(req.Body)})
- case AssetOperationAssetGet:
- id, err := requiredAssetString(req.Body, "Id")
- if err != nil {
- return nil, err
- }
- return client.GetAsset(&cmmodel.GetAssetRequest{GetAssetPath: (&cmmodel.GetAssetPath{}).SetAssetId(id)})
- case AssetOperationAssetUpdate:
- id, err := requiredAssetString(req.Body, "Id")
- if err != nil {
- return nil, err
- }
- return client.UpdateAsset(&cmmodel.UpdateAssetRequest{
- UpdateAssetPath: (&cmmodel.UpdateAssetPath{}).SetAssetId(id),
- UpdateAssetBody: newChinaMobileUpdateAssetBody(req.Body),
- })
- case AssetOperationAssetDelete:
- id, err := requiredAssetString(req.Body, "Id")
- if err != nil {
- return nil, err
- }
- return client.DeleteAsset(&cmmodel.DeleteAssetRequest{DeleteAssetPath: (&cmmodel.DeleteAssetPath{}).SetAssetId(id)})
- case AssetOperationAssetGroupCreate:
- return client.CreateAssetGroup(&cmmodel.CreateAssetGroupRequest{CreateAssetGroupBody: newChinaMobileCreateAssetGroupBody(req.Body)})
- case AssetOperationAssetGroupList:
- return client.ListAssetGroups(&cmmodel.ListAssetGroupsRequest{ListAssetGroupsBody: newChinaMobileListAssetGroupsBody(req.Body)})
- case AssetOperationAssetGroupGet:
- id, err := requiredAssetString(req.Body, "Id")
- if err != nil {
- return nil, err
- }
- return client.GetAssetGroup(&cmmodel.GetAssetGroupRequest{GetAssetGroupPath: (&cmmodel.GetAssetGroupPath{}).SetGroupId(id)})
- case AssetOperationAssetGroupUpdate:
- id, err := requiredAssetString(req.Body, "Id")
- if err != nil {
- return nil, err
- }
- return client.UpdateAssetGroup(&cmmodel.UpdateAssetGroupRequest{
- UpdateAssetGroupPath: (&cmmodel.UpdateAssetGroupPath{}).SetGroupId(id),
- UpdateAssetGroupBody: newChinaMobileUpdateAssetGroupBody(req.Body),
- })
- case AssetOperationAssetGroupDelete:
- id, err := requiredAssetString(req.Body, "Id")
- if err != nil {
- return nil, err
- }
- return client.DeleteAssetGroup(&cmmodel.DeleteAssetGroupRequest{DeleteAssetGroupPath: (&cmmodel.DeleteAssetGroupPath{}).SetGroupId(id)})
- default:
- return nil, fmt.Errorf("unsupported asset operation %s", req.Action.Operation)
- }
- }
-
- func isChinaMobileAssetLocalValidationError(err error) bool {
- return err != nil && strings.Contains(err.Error(), " is required")
- }
-
- func classifyChinaMobileAssetSDKError(err error) *AssetError {
- if err == nil {
- return nil
- }
- if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
- return newAssetError(AssetErrorUpstream, err.Error(), http.StatusGatewayTimeout)
- }
- if isChinaMobileAssetLocalValidationError(err) {
- return newAssetError(AssetErrorInvalidRequest, err.Error(), http.StatusBadRequest)
- }
- var responseErr *cmerrs.ServerResponseError
- if errors.As(err, &responseErr) && responseErr.Code == http.StatusBadRequest {
- message := strings.TrimSpace(responseErr.Body)
- if message == "" {
- message = responseErr.Error()
- }
- return newAssetError(AssetErrorInvalidRequest, message, http.StatusBadRequest)
- }
- return nil
- }
-
- func validateChinaMobileAssetRequest(req AssetRequest) *AssetError {
- invalid := func(message string) *AssetError {
- return newAssetError(AssetErrorInvalidRequest, message, http.StatusBadRequest)
- }
- requireString := func(key string) *AssetError {
- if strings.TrimSpace(stringValue(req.Body, key)) == "" {
- return invalid(key + " is required")
- }
- return nil
- }
- validatePage := func() *AssetError {
- pageNumber := intValue(req.Body, "PageNumber", 1)
- pageSize := intValue(req.Body, "PageSize", 10)
- if pageNumber < 1 {
- return invalid("PageNumber must be greater than or equal to 1")
- }
- if pageSize < 1 || pageSize > 999999 {
- return invalid("PageSize must be between 1 and 999999")
- }
- return nil
- }
-
- switch req.Action.Operation {
- case AssetOperationAssetCreate:
- for _, key := range []string{"GroupId", "Name", "URL", "AssetType"} {
- if err := requireString(key); err != nil {
- return err
- }
- }
- if len([]rune(stringValue(req.Body, "Name"))) > 64 {
- return invalid("Name must not exceed 64 characters")
- }
- assetURL, err := url.ParseRequestURI(strings.TrimSpace(stringValue(req.Body, "URL")))
- if err != nil || (assetURL.Scheme != "http" && assetURL.Scheme != "https") || assetURL.Host == "" {
- return invalid("URL must be a valid public HTTP or HTTPS URL")
- }
- switch stringValue(req.Body, "AssetType") {
- case "Image", "Video", "Audio":
- default:
- return invalid("AssetType must be one of Image, Video, Audio")
- }
- case AssetOperationAssetList:
- if err := validatePage(); err != nil {
- return err
- }
- groupType := strings.TrimSpace(stringValue(mapValue(req.Body, "Filter"), "GroupType"))
- if groupType == "" {
- return invalid("Filter.GroupType is required")
- }
- if groupType != "AIGC" && groupType != "LivenessFace" {
- return invalid("Filter.GroupType must be AIGC or LivenessFace")
- }
- case AssetOperationAssetGroupCreate:
- if stringValue(req.Body, "GroupType") != "AIGC" {
- return invalid("GroupType must be AIGC")
- }
- if len([]rune(stringValue(req.Body, "Name"))) > 64 {
- return invalid("Name must not exceed 64 characters")
- }
- if len([]rune(stringValue(req.Body, "Description"))) > 300 {
- return invalid("Description must not exceed 300 characters")
- }
- case AssetOperationAssetGroupList:
- return validatePage()
- case AssetOperationAssetUpdate:
- if err := requireString("Id"); err != nil {
- return err
- }
- if len([]rune(stringValue(req.Body, "Name"))) > 64 {
- return invalid("Name must not exceed 64 characters")
- }
- case AssetOperationAssetGroupUpdate:
- if err := requireString("Id"); err != nil {
- return err
- }
- if len([]rune(stringValue(req.Body, "Name"))) > 64 {
- return invalid("Name must not exceed 64 characters")
- }
- if len([]rune(stringValue(req.Body, "Description"))) > 300 {
- return invalid("Description must not exceed 300 characters")
- }
- case AssetOperationAssetGet, AssetOperationAssetDelete, AssetOperationAssetGroupGet, AssetOperationAssetGroupDelete:
- return requireString("Id")
- }
- return nil
- }
-
- func newChinaMobileCreateAssetBody(body map[string]any) *cmmodel.CreateAssetBody {
- assetType := cmmodel.CreateAssetBodyAssetTypeEnum(stringValue(body, "AssetType"))
- result := &cmmodel.CreateAssetBody{}
- result.SetGroupId(stringValue(body, "GroupId"))
- result.SetAssetName(stringValue(body, "Name"))
- result.SetAssetUrl(stringValue(body, "URL"))
- if strings.TrimSpace(string(assetType)) != "" {
- result.SetAssetType(assetType)
- }
- return result
- }
-
- func newChinaMobileListAssetsBody(body map[string]any) *cmmodel.ListAssetsBody {
- mapped := mapChinaMobileListAssets(body)
- result := &cmmodel.ListAssetsBody{}
- result.SetPageNo(int32(intValue(mapped, "pageNo", 1)))
- result.SetPageSize(int32(intValue(mapped, "pageSize", 10)))
- if value := stringValue(mapped, "groupType"); value != "" {
- result.SetGroupType(value)
- }
- if value := stringValue(mapped, "assetName"); value != "" {
- result.SetAssetName(value)
- }
- if values := stringArrayValue(mapped, "groupIds"); len(values) > 0 {
- result.SetGroupIds(values)
- }
- if values := stringArrayValue(mapped, "statuses"); len(values) > 0 {
- result.SetStatuses(values)
- }
- return result
- }
-
- func newChinaMobileUpdateAssetBody(body map[string]any) *cmmodel.UpdateAssetBody {
- result := &cmmodel.UpdateAssetBody{}
- if value := stringValue(body, "Name"); value != "" {
- result.SetAssetName(value)
- }
- return result
- }
-
- func newChinaMobileCreateAssetGroupBody(body map[string]any) *cmmodel.CreateAssetGroupBody {
- result := &cmmodel.CreateAssetGroupBody{}
- if value := stringValue(body, "GroupType"); value != "" {
- result.SetGroupType(value)
- }
- if value := stringValue(body, "Name"); value != "" {
- result.SetGroupName(value)
- }
- if value := stringValue(body, "Description"); value != "" {
- result.SetDescription(value)
- }
- return result
- }
-
- func newChinaMobileListAssetGroupsBody(body map[string]any) *cmmodel.ListAssetGroupsBody {
- mapped := mapChinaMobileListAssetGroups(body)
- result := &cmmodel.ListAssetGroupsBody{}
- result.SetPageNo(int32(intValue(mapped, "pageNo", 1)))
- result.SetPageSize(int32(intValue(mapped, "pageSize", 10)))
- if value := stringValue(mapped, "groupType"); value != "" {
- result.SetGroupType(value)
- }
- if value := stringValue(mapped, "groupName"); value != "" {
- result.SetGroupName(value)
- }
- if values := stringArrayValue(mapped, "groupIds"); len(values) > 0 {
- result.SetGroupIds(values)
- }
- return result
- }
-
- func newChinaMobileUpdateAssetGroupBody(body map[string]any) *cmmodel.UpdateAssetGroupBody {
- result := &cmmodel.UpdateAssetGroupBody{}
- if value := stringValue(body, "Name"); value != "" {
- result.SetGroupName(value)
- }
- if value := stringValue(body, "Description"); value != "" {
- result.SetDescription(value)
- }
- return result
- }
-
- func buildChinaMobileAssetRequest(req AssetRequest) (string, string, map[string]any, error) {
- switch req.Action.Operation {
- case AssetOperationAssetCreate:
- return http.MethodPost, "/api/openapi-maas/exp/aicc/v2/asset", mapChinaMobileCreateAsset(req.Body), nil
- case AssetOperationAssetList:
- return http.MethodPost, "/api/openapi-maas/exp/aicc/v2/asset/query", mapChinaMobileListAssets(req.Body), nil
- case AssetOperationAssetGet:
- id, err := requiredAssetString(req.Body, "Id")
- return http.MethodGet, "/api/openapi-maas/exp/aicc/v2/asset/" + url.PathEscape(id), nil, err
- case AssetOperationAssetUpdate:
- id, err := requiredAssetString(req.Body, "Id")
- return http.MethodPut, "/api/openapi-maas/exp/aicc/v2/asset/" + url.PathEscape(id), mapChinaMobileUpdateAsset(req.Body), err
- case AssetOperationAssetDelete:
- id, err := requiredAssetString(req.Body, "Id")
- return http.MethodDelete, "/api/openapi-maas/exp/aicc/v2/asset/" + url.PathEscape(id), nil, err
- case AssetOperationAssetGroupCreate:
- return http.MethodPost, "/api/openapi-maas/exp/aicc/v2/asset-group", mapChinaMobileCreateAssetGroup(req.Body), nil
- case AssetOperationAssetGroupList:
- return http.MethodPost, "/api/openapi-maas/exp/aicc/v2/asset-group/query", mapChinaMobileListAssetGroups(req.Body), nil
- case AssetOperationAssetGroupGet:
- id, err := requiredAssetString(req.Body, "Id")
- return http.MethodGet, "/api/openapi-maas/exp/aicc/v2/asset-group/" + url.PathEscape(id), nil, err
- case AssetOperationAssetGroupUpdate:
- id, err := requiredAssetString(req.Body, "Id")
- return http.MethodPut, "/api/openapi-maas/exp/aicc/v2/asset-group/" + url.PathEscape(id), mapChinaMobileUpdateAssetGroup(req.Body), err
- case AssetOperationAssetGroupDelete:
- id, err := requiredAssetString(req.Body, "Id")
- return http.MethodDelete, "/api/openapi-maas/exp/aicc/v2/asset-group/" + url.PathEscape(id), nil, err
- default:
- return "", "", nil, fmt.Errorf("unsupported asset operation %s", req.Action.Operation)
- }
- }
-
- func mapChinaMobileCreateAsset(body map[string]any) map[string]any {
- return compactAssetMap(map[string]any{
- "groupId": stringValue(body, "GroupId"),
- "assetName": stringValue(body, "Name"),
- "assetUrl": stringValue(body, "URL"),
- "assetType": stringValue(body, "AssetType"),
- })
- }
-
- func mapChinaMobileListAssets(body map[string]any) map[string]any {
- filter := mapValue(body, "Filter")
- mapped := map[string]any{
- "pageNo": intValue(body, "PageNumber", 1),
- "pageSize": intValue(body, "PageSize", 10),
- }
- copyStringArray(mapped, "groupIds", filter, "GroupIds")
- copyString(mapped, "groupType", filter, "GroupType")
- copyString(mapped, "assetName", filter, "Name")
- copyStatusArray(mapped, "statuses", filter, "Statuses")
- return compactAssetMap(mapped)
- }
-
- func mapChinaMobileUpdateAsset(body map[string]any) map[string]any {
- return compactAssetMap(map[string]any{"assetName": stringValue(body, "Name")})
- }
-
- func mapChinaMobileCreateAssetGroup(body map[string]any) map[string]any {
- return compactAssetMap(map[string]any{
- "groupType": stringValue(body, "GroupType"),
- "groupName": stringValue(body, "Name"),
- "description": stringValue(body, "Description"),
- })
- }
-
- func mapChinaMobileListAssetGroups(body map[string]any) map[string]any {
- filter := mapValue(body, "Filter")
- mapped := map[string]any{
- "pageNo": intValue(body, "PageNumber", 1),
- "pageSize": intValue(body, "PageSize", 10),
- }
- copyString(mapped, "groupType", filter, "GroupType")
- copyString(mapped, "groupName", filter, "Name")
- copyStringArray(mapped, "groupIds", filter, "GroupIds")
- return compactAssetMap(mapped)
- }
-
- func mapChinaMobileUpdateAssetGroup(body map[string]any) map[string]any {
- return compactAssetMap(map[string]any{
- "groupName": stringValue(body, "Name"),
- "description": stringValue(body, "Description"),
- })
- }
-
- func normalizeChinaMobileAssetResponse(spec AssetActionSpec, version string, data []byte) ([]byte, *AssetError) {
- var payload struct {
- RequestID string `json:"requestId"`
- State string `json:"state"`
- ErrorCode string `json:"errorCode"`
- ErrorMessage string `json:"errorMessage"`
- Body any `json:"body"`
- }
- if err := common.Unmarshal(data, &payload); err != nil {
- return nil, newAssetError(AssetErrorUpstream, err.Error(), http.StatusBadGateway)
- }
- if strings.EqualFold(payload.State, "ERROR") {
- message := strings.TrimSpace(payload.ErrorMessage)
- if message == "" {
- message = payload.ErrorCode
- }
- return nil, newAssetError(AssetErrorUpstream, message, http.StatusBadGateway)
- }
- if !strings.EqualFold(payload.State, "OK") {
- return nil, newAssetError(AssetErrorUpstream, "China Mobile asset response has invalid state", http.StatusBadGateway)
- }
- if spec.Delete {
- deleted, ok := payload.Body.(bool)
- if !ok || !deleted {
- return nil, newAssetError(AssetErrorUpstream, "China Mobile asset delete was not confirmed", http.StatusBadGateway)
- }
- }
- var result any
- if !spec.Delete {
- result = normalizeChinaMobileResult(spec.Operation, payload.Body)
- }
- body, err := buildAssetSuccessResponseWithRequestID(spec.Action, version, payload.RequestID, result)
- if err != nil {
- return nil, newAssetError(AssetErrorServer, err.Error(), http.StatusInternalServerError)
- }
- return body, nil
- }
-
- func normalizeChinaMobileAssetSDKResponse(spec AssetActionSpec, version string, response any) ([]byte, *AssetError) {
- data, err := common.Marshal(response)
- if err != nil {
- return nil, newAssetError(AssetErrorServer, err.Error(), http.StatusInternalServerError)
- }
- return normalizeChinaMobileAssetResponse(spec, version, data)
- }
-
- func normalizeChinaMobileResult(operation AssetOperation, body any) any {
- raw, ok := body.(map[string]any)
- if !ok {
- return body
- }
- switch operation {
- case AssetOperationAssetList:
- return map[string]any{
- "Items": normalizeChinaMobileItems(raw["data"], false),
- "TotalCount": raw["total"],
- }
- case AssetOperationAssetGroupList:
- return map[string]any{
- "Items": normalizeChinaMobileItems(raw["data"], true),
- "TotalCount": raw["total"],
- }
- case AssetOperationAssetGroupCreate, AssetOperationAssetGroupGet, AssetOperationAssetGroupUpdate:
- return normalizeChinaMobileAssetGroup(raw)
- default:
- return normalizeChinaMobileAsset(raw)
- }
- }
-
- func normalizeChinaMobileItems(value any, group bool) []any {
- items, ok := value.([]any)
- if !ok {
- return nil
- }
- normalized := make([]any, 0, len(items))
- for _, item := range items {
- raw, ok := item.(map[string]any)
- if !ok {
- continue
- }
- if group {
- normalized = append(normalized, normalizeChinaMobileAssetGroup(raw))
- } else {
- normalized = append(normalized, normalizeChinaMobileAsset(raw))
- }
- }
- return normalized
- }
-
- func normalizeChinaMobileAsset(raw map[string]any) map[string]any {
- return compactAssetMap(map[string]any{
- "Id": raw["assetId"],
- "GroupId": raw["groupId"],
- "Name": raw["assetName"],
- "AssetType": raw["assetType"],
- "URL": raw["assetUrl"],
- "Status": chinaMobileStatusToOfficial(fmt.Sprint(raw["status"])),
- "ErrorMessage": raw["errorMessage"],
- "CreatedAt": raw["createdTime"],
- "UpdatedAt": raw["updatedTime"],
- })
- }
-
- func normalizeChinaMobileAssetGroup(raw map[string]any) map[string]any {
- return compactAssetMap(map[string]any{
- "Id": raw["groupId"],
- "GroupId": raw["groupId"],
- "GroupType": raw["groupType"],
- "Name": raw["groupName"],
- "Description": raw["description"],
- "CreatedAt": raw["createdTime"],
- "UpdatedAt": raw["updatedTime"],
- })
- }
-
- func validateChinaMobileCompatibility(body map[string]any) *AssetError {
- if projectName := strings.TrimSpace(stringValue(body, "ProjectName")); projectName != "" && projectName != "default" {
- return newAssetError(AssetErrorOperationNotSupported, "China Mobile asset API does not support non-default ProjectName", http.StatusBadRequest)
- }
- if sortBy := strings.TrimSpace(stringValue(body, "SortBy")); sortBy != "" && sortBy != "CreateTime" {
- return newAssetError(AssetErrorOperationNotSupported, "China Mobile asset API does not support custom SortBy", http.StatusBadRequest)
- }
- if sortOrder := strings.TrimSpace(stringValue(body, "SortOrder")); sortOrder != "" && !strings.EqualFold(sortOrder, "Desc") {
- return newAssetError(AssetErrorOperationNotSupported, "China Mobile asset API does not support custom SortOrder", http.StatusBadRequest)
- }
- return nil
- }
-
- func chinaMobileStatusToOfficial(status string) string {
- switch strings.ToUpper(strings.TrimSpace(status)) {
- case "PROCESSING":
- return "Processing"
- case "ACTIVE":
- return "Active"
- case "FAILED":
- return "Failed"
- default:
- return status
- }
- }
-
- func officialStatusToChinaMobile(status string) string {
- switch strings.ToLower(strings.TrimSpace(status)) {
- case "processing":
- return "PROCESSING"
- case "active":
- return "ACTIVE"
- case "failed":
- return "FAILED"
- default:
- return status
- }
- }
-
- func requiredAssetString(body map[string]any, key string) (string, error) {
- value := strings.TrimSpace(stringValue(body, key))
- if value == "" {
- return "", fmt.Errorf("%s is required", key)
- }
- return value, nil
- }
-
- func stringValue(body map[string]any, key string) string {
- if body == nil {
- return ""
- }
- value, _ := body[key].(string)
- return value
- }
-
- func mapValue(body map[string]any, key string) map[string]any {
- if body == nil {
- return nil
- }
- value, _ := body[key].(map[string]any)
- return value
- }
-
- func intValue(body map[string]any, key string, fallback int) int {
- if body == nil {
- return fallback
- }
- switch value := body[key].(type) {
- case int:
- return value
- case int32:
- return int(value)
- case int64:
- return int(value)
- case float64:
- return int(value)
- case float32:
- return int(value)
- default:
- return fallback
- }
- }
-
- func copyString(dst map[string]any, dstKey string, src map[string]any, srcKey string) {
- value := stringValue(src, srcKey)
- if strings.TrimSpace(value) != "" {
- dst[dstKey] = value
- }
- }
-
- func copyStringArray(dst map[string]any, dstKey string, src map[string]any, srcKey string) {
- values := stringArrayValue(src, srcKey)
- if len(values) > 0 {
- dst[dstKey] = values
- }
- }
-
- func copyStatusArray(dst map[string]any, dstKey string, src map[string]any, srcKey string) {
- values := stringArrayValue(src, srcKey)
- if len(values) == 0 {
- return
- }
- mapped := make([]string, 0, len(values))
- for _, value := range values {
- mapped = append(mapped, officialStatusToChinaMobile(value))
- }
- dst[dstKey] = mapped
- }
-
- func stringArrayValue(body map[string]any, key string) []string {
- if body == nil {
- return nil
- }
- switch value := body[key].(type) {
- case []string:
- return value
- case []any:
- values := make([]string, 0, len(value))
- for _, item := range value {
- if s, ok := item.(string); ok && strings.TrimSpace(s) != "" {
- values = append(values, s)
- }
- }
- return values
- default:
- return nil
- }
- }
-
- func compactAssetMap(input map[string]any) map[string]any {
- output := make(map[string]any, len(input))
- for key, value := range input {
- switch v := value.(type) {
- case string:
- if strings.TrimSpace(v) != "" {
- output[key] = v
- }
- case nil:
- continue
- default:
- output[key] = value
- }
- }
- return output
- }
|