-
Notifications
You must be signed in to change notification settings - Fork 249
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1. 支持{kev:val}和{kev:{val:xxx,bscp_kv_type:xxx}}导入
2. 支持json和yaml导入导出
- Loading branch information
1 parent
f3e4e5e
commit 5ed0b27
Showing
6 changed files
with
563 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
/* | ||
* Tencent is pleased to support the open source community by making Blueking Container Service available. | ||
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. | ||
* Licensed under the MIT License (the "License"); you may not use this file except | ||
* in compliance with the License. You may obtain a copy of the License at | ||
* http://opensource.org/licenses/MIT | ||
* 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 service | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"encoding/json" | ||
"encoding/xml" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"reflect" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/go-chi/chi/v5" | ||
"github.com/go-chi/render" | ||
"github.com/xuri/excelize/v2" | ||
"gopkg.in/yaml.v3" | ||
|
||
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/dal/table" | ||
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/iam/auth" | ||
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/kit" | ||
pbcs "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/config-server" | ||
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/rest" | ||
) | ||
|
||
type kvService struct { | ||
authorizer auth.Authorizer | ||
cfgClient pbcs.ConfigClient | ||
} | ||
|
||
func newKvService(authorizer auth.Authorizer, | ||
cfgClient pbcs.ConfigClient) *kvService { | ||
s := &kvService{ | ||
authorizer: authorizer, | ||
cfgClient: cfgClient, | ||
} | ||
return s | ||
} | ||
|
||
// Import is used to handle file import requests. | ||
func (m *kvService) Import(w http.ResponseWriter, r *http.Request) { | ||
|
||
kt := kit.MustGetKit(r.Context()) | ||
|
||
appIdStr := chi.URLParam(r, "app_id") | ||
appId, _ := strconv.Atoi(appIdStr) | ||
if appId == 0 { | ||
_ = render.Render(w, r, rest.BadRequest(errors.New("validation parameter fail"))) | ||
return | ||
} | ||
reader := bufio.NewReader(r.Body) | ||
|
||
bytes, err := io.ReadAll(reader) | ||
if err != nil { | ||
_ = render.Render(w, r, rest.BadRequest(err)) | ||
return | ||
} | ||
|
||
var kvMap map[string]interface{} | ||
switch { | ||
case readJSONFile(bytes, &kvMap): | ||
case readYAMLFile(bytes, &kvMap): | ||
default: | ||
_ = render.Render(w, r, rest.BadRequest(errors.New("unsupported file type"))) | ||
return | ||
} | ||
|
||
req := &pbcs.BatchUpsertKvsReq{ | ||
BizId: kt.BizID, | ||
AppId: uint32(appId), | ||
Kvs: handleKv(kvMap), | ||
} | ||
|
||
resp, err := m.cfgClient.BatchUpsertKvs(kt.RpcCtx(), req) | ||
if err != nil { | ||
_ = render.Render(w, r, rest.BadRequest(err)) | ||
return | ||
} | ||
|
||
_ = render.Render(w, r, rest.OKRender(resp)) | ||
} | ||
|
||
// yaml格式时 类型是map[interface{}]interface{} | ||
// json格式时 类型是map[string]interface{} | ||
func handleKv(result map[string]interface{}) []*pbcs.BatchUpsertKvsReq_Kv { | ||
kvMap := []*pbcs.BatchUpsertKvsReq_Kv{} | ||
for key, value := range result { | ||
KVType := "" | ||
switch entry := value.(type) { | ||
case map[interface{}]interface{}: | ||
kvType, okType := entry["bscp_kv_type"].(string) | ||
kvValue, okVal := entry["value"] | ||
if okType && okVal { | ||
var val string | ||
// 如果是json需要序列化以下 | ||
val = fmt.Sprintf("%v", kvValue) | ||
if kvType == string(table.KvJson) { | ||
mv, _ := json.Marshal(kvValue) | ||
val = string(mv) | ||
} | ||
kvMap = append(kvMap, &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: key, | ||
Value: val, | ||
KvType: kvType, | ||
}) | ||
} | ||
case map[string]interface{}: | ||
kvType, okType := entry["bscp_kv_type"].(string) | ||
kvValue, okVal := entry["value"] | ||
if okType && okVal { | ||
var val string | ||
// 如果是json需要序列化以下 | ||
val = fmt.Sprintf("%v", kvValue) | ||
if kvType == string(table.KvJson) { | ||
mv, _ := json.Marshal(kvValue) | ||
val = string(mv) | ||
} | ||
kvMap = append(kvMap, &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: key, | ||
Value: val, | ||
KvType: kvType, | ||
}) | ||
} | ||
default: | ||
// 判断是不是数值类型 | ||
if isNumber(value) { | ||
kvMap = append(kvMap, &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: key, | ||
Value: fmt.Sprintf("%v", value), | ||
KvType: string(table.KvNumber), | ||
}) | ||
} else { | ||
// json格式单独处理下 | ||
var js map[string]interface{} | ||
err := json.Unmarshal([]byte(value.(string)), &js) | ||
if err == nil { | ||
// 长度等于2需单独处理下 | ||
if len(js) == 2 { | ||
kvType, okType := js["bscp_kv_type"].(string) | ||
kvValue, okVal := js["value"] | ||
if okType && okVal { | ||
kvMap = append(kvMap, &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: key, | ||
Value: fmt.Sprintf("%v", kvValue), | ||
KvType: kvType, | ||
}) | ||
} | ||
} else { | ||
kvMap = append(kvMap, &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: key, | ||
Value: fmt.Sprintf("%v", value), | ||
KvType: string(table.KvJson), | ||
}) | ||
} | ||
} else { | ||
KVType = determineType(value.(string)) | ||
kvMap = append(kvMap, &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: key, | ||
Value: fmt.Sprintf("%v", value), | ||
KvType: KVType, | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
return kvMap | ||
} | ||
|
||
// 读取json文件 | ||
func readJSONFile(bytes []byte, result *map[string]interface{}) bool { | ||
return !json.Valid(bytes) && json.Unmarshal(bytes, &result) == nil | ||
} | ||
|
||
// 读取yaml文件 | ||
func readYAMLFile(bytes []byte, result *map[string]interface{}) bool { | ||
return yaml.Unmarshal(bytes, &result) == nil | ||
} | ||
|
||
// 根据值判断类型 | ||
func determineType(value string) string { | ||
var result string | ||
switch { | ||
case isYAML(value): | ||
result = "yaml" | ||
case isXML(value): | ||
result = "xml" | ||
case isTEXT(value): | ||
result = "text" | ||
case isNumber(value): | ||
result = "number" | ||
default: | ||
result = "string" | ||
} | ||
return result | ||
} | ||
|
||
// 判断是否为 JSON | ||
func isJSON(value string) bool { | ||
var js map[string]interface{} | ||
return json.Unmarshal([]byte(value), &js) == nil | ||
} | ||
|
||
// 判断是否为 YAML | ||
func isYAML(value string) bool { | ||
var yml map[string]interface{} | ||
return yaml.Unmarshal([]byte(value), &yml) == nil | ||
} | ||
|
||
// 判断是否为 XML | ||
func isXML(value string) bool { | ||
var xmlData interface{} | ||
return xml.Unmarshal([]byte(value), &xmlData) == nil | ||
} | ||
|
||
// 判断是否为 XML | ||
func isTEXT(value string) bool { | ||
return strings.Contains(value, "\n") | ||
} | ||
|
||
// 判断是不是 Number | ||
func isNumber(value interface{}) bool { | ||
// 获取值的类型 | ||
valType := reflect.TypeOf(value) | ||
|
||
// 检查类型是否为数字 | ||
switch valType.Kind() { | ||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
return true | ||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
return true | ||
case reflect.Float32, reflect.Float64: | ||
return true | ||
default: | ||
return false | ||
} | ||
} | ||
|
||
func parseExcelFile(file io.Reader) ([]*pbcs.BatchUpsertKvsReq_Kv, error) { | ||
|
||
r := bufio.NewReader(file) | ||
f, err := excelize.OpenReader(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rows, err := f.GetRows("Sheet1") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var kvs []*pbcs.BatchUpsertKvsReq_Kv | ||
for i := 1; i < len(rows); i++ { | ||
kv := &pbcs.BatchUpsertKvsReq_Kv{ | ||
Key: rows[i][0], | ||
Value: rows[i][1], | ||
KvType: rows[i][2], | ||
} | ||
kvs = append(kvs, kv) | ||
} | ||
|
||
return kvs, nil | ||
} | ||
|
||
func peek(r *bufio.Reader, size int) ([]byte, error) { | ||
|
||
peekedData, err := r.Peek(size) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return peekedData, nil | ||
} | ||
|
||
func startsWith(r *bufio.Reader, prefix string) bool { | ||
peekedData, err := peek(r, len(prefix)) | ||
if err != nil { | ||
return false | ||
} | ||
return bytes.Equal(peekedData, []byte(prefix)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.