feat(merge_component): Add merge from website

Merge contents from patch selected by user into newer version

merge from client feature
This commit is contained in:
pakintada@gmail.com 2024-03-04 11:19:11 +07:00
parent 292c7697a4
commit 09c21301d6
15 changed files with 343 additions and 42 deletions

View file

@ -1,6 +1,7 @@
package data
import (
"encoding/json"
"fmt"
"log"
"os"
@ -61,7 +62,7 @@ func NewData(taoLogger *logger.TaoLogger, redisClient *RedisCli) *Data {
defaultFile := "coffeethai02_600.json"
// TODO: read 'version' file by country
// read 'version' file by country
// versionPath := path.Join("cofffeemachineConfig", defaultCountry, "version")
// taoLogger.Log.Debug("version", zap.Any("version path", versionPath))
@ -194,7 +195,7 @@ func (d *Data) GetRecipe(countryID, filename string) *models.Recipe {
d.taoLogger.Log.Debug("invoke GetRecipe", zap.String("countryID", countryID), zap.String("filename", filename))
// TODO: concat submenu into recipe
// concat submenu into recipe
if countryID == "" {
d.taoLogger.Log.Debug("GetRecipe", zap.Any("EmptyCountryId", "return default country = tha"))
@ -488,7 +489,7 @@ func (d *Data) SetValuesToRecipe(base_recipe []models.Recipe01, recipe models.Re
if v.ProductCode == recipe.ProductCode {
// Log.Debug("SetValuesToRecipe", zap.Any("old", v), zap.Any("new", recipe))
// v = recipe
// TODO: change only changed values
// change only changed values
// transform to map
base_recipe01_Map := v.ToMap()
@ -511,7 +512,7 @@ func (d *Data) SetValuesToRecipe(base_recipe []models.Recipe01, recipe models.Re
if sub.ProductCode == recipe.ProductCode {
// Log.Debug("SetValuesToRecipe.SubMenu", zap.Any("old", sub), zap.Any("new", recipe))
// sub = recipe
// TODO: change only changed values
// change only changed values
// transform to map
base_recipe01_Map := sub.ToMap()
@ -918,6 +919,7 @@ func (d *Data) GetCountryIDByName(countryName string) (string, error) {
}
// ------ sorting ------
// FIXME: sorting not working
func (d *Data) SortRecipe(countryID, filename string, sort_by string) (error, []string) {
// Get recipe
recipe := d.GetRecipe(countryID, filename)
@ -1027,3 +1029,200 @@ func (d *Data) SortRecipe(countryID, filename string, sort_by string) (error, []
return nil, emptiedComparators
}
// merge
func (d *Data) Merge(country string, filename string, attr string, changeKey string, updated interface{}) (string, error) {
// change this to switch case
if attr == "Recipe" {
return d.MergeRecipe(country, filename, changeKey)
} else if attr == "NoCache" {
if updated == nil {
return "UpdatedValueError", fmt.Errorf("updated value is nil")
}
return d.MergeRecipeNoCache(country, filename, updated.(models.Recipe01))
}
return "NotKnownAttr", nil
}
func (d *Data) MergeRecipe(country, filename, changeKey string) (string, error) {
// get source
prefix := "Recipe"
// read keys
keys, err := d.redisClient.KeyList()
if err != nil {
return "RedisKeysError", fmt.Errorf("error when read keys from redis: %v", err)
}
// fullKeyString := ""
isLegit := false
// // find key that contains commitId, patchSource
for _, key := range keys {
// Recipe_<productCode>_<commitId>_<patchSource>
if strings.Contains(key, prefix) && key == changeKey {
isLegit = true
}
}
if !isLegit {
return "NotLegitKey", fmt.Errorf("key not found")
}
// get actual value
patchValue := models.Recipe01{}
err = d.redisClient.GetKeyTo(changeKey, &patchValue)
if err != nil {
return "PatchValueError", fmt.Errorf("error while getting value from source: %v", err)
}
// get recipe
sourceRecipe := d.GetRecipe(country, filename)
// apply value to its source
d.SetValuesToRecipe(sourceRecipe.Recipe01, patchValue)
// updating version
sourceRecipe.MachineSetting.ConfigNumber += 1
newVersionStr := strconv.Itoa(sourceRecipe.MachineSetting.ConfigNumber)
// save to local and redis
// create new file name
updatedFilename := ""
prefixLocalFile := "coffeethai02_"
if country != "tha" {
updatedFilename = prefixLocalFile + newVersionStr + "_" + country + ".json"
} else {
updatedFilename = prefixLocalFile + newVersionStr + ".json"
}
fullUpdatedFilename := path.Join("./cofffeemachineConfig", country, updatedFilename)
// create new file
// handle case if file already exists, add version by 1 then search new filename in loop
// list all files in dir
directory := path.Join("./cofffeemachineConfig", country)
files, err := os.ReadDir(directory)
if err != nil {
d.taoLogger.Log.Error("MergeRecipe: Error when read dir", zap.Error(err))
return "ReadDirError", fmt.Errorf("error when read dir: %v", err)
}
for _, file := range files {
if file.Name() == updatedFilename {
// add version by 1
sourceRecipe.MachineSetting.ConfigNumber += 1
newVersionStr = strconv.Itoa(sourceRecipe.MachineSetting.ConfigNumber)
if country != "tha" {
updatedFilename = prefixLocalFile + newVersionStr + "_" + country + ".json"
} else {
updatedFilename = prefixLocalFile + newVersionStr + ".json"
}
fullUpdatedFilename = path.Join("./cofffeemachineConfig", country, updatedFilename)
}
}
file, err := os.Create(fullUpdatedFilename)
if err != nil {
d.taoLogger.Log.Error("MergeRecipe: Error when create new file", zap.Error(err))
return "CreateFileError", fmt.Errorf("error when create new file: %v", err)
}
// write file
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(sourceRecipe)
if err != nil {
d.taoLogger.Log.Error("MergeRecipe: Error when write file", zap.Error(err))
return "WriteFileError", fmt.Errorf("error when write file: %v", err)
}
// set cache
err = d.redisClient.SetToKey(updatedFilename, sourceRecipe)
if err != nil {
d.taoLogger.Log.Error("MergeRecipe: Error when set cache", zap.Error(err))
return "SetCacheError", fmt.Errorf("error when set cache: %v", err)
}
return updatedFilename, nil
}
func (d *Data) MergeRecipeNoCache(country string, filename string, updatedRecipe models.Recipe01) (string, error) {
// get recipe
sourceRecipe := d.GetRecipe(country, filename)
// apply value to its source
d.SetValuesToRecipe(sourceRecipe.Recipe01, updatedRecipe)
// updating version
sourceRecipe.MachineSetting.ConfigNumber += 1
newVersionStr := strconv.Itoa(sourceRecipe.MachineSetting.ConfigNumber)
// create new file name
updatedFilename := ""
prefixLocalFile := "coffeethai02_"
if country != "tha" {
updatedFilename = prefixLocalFile + newVersionStr + "_" + country + ".json"
} else {
updatedFilename = prefixLocalFile + newVersionStr + ".json"
}
fullUpdatedFilename := path.Join("./cofffeemachineConfig", country, updatedFilename)
// create new file
// handle case if file already exists, add version by 1 then search new filename in loop
// list all files in dir
directory := path.Join("./cofffeemachineConfig", country)
files, err := os.ReadDir(directory)
if err != nil {
d.taoLogger.Log.Error("MergeRecipeNoCache: Error when read dir", zap.Error(err))
return "ReadDirError", fmt.Errorf("error when read dir: %v", err)
}
for _, file := range files {
if file.Name() == updatedFilename {
// add version by 1
sourceRecipe.MachineSetting.ConfigNumber += 1
newVersionStr = strconv.Itoa(sourceRecipe.MachineSetting.ConfigNumber)
if country != "tha" {
updatedFilename = prefixLocalFile + newVersionStr + "_" + country + ".json"
} else {
updatedFilename = prefixLocalFile + newVersionStr + ".json"
}
fullUpdatedFilename = path.Join("./cofffeemachineConfig", country, updatedFilename)
}
}
file, err := os.Create(fullUpdatedFilename)
if err != nil {
d.taoLogger.Log.Error("MergeRecipeNoCache: Error when create new file", zap.Error(err))
return "CreateFileError", fmt.Errorf("error when create new file: %v", err)
}
// write file
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(sourceRecipe)
if err != nil {
d.taoLogger.Log.Error("MergeRecipeNoCache: Error when write file", zap.Error(err))
return "WriteFileError", fmt.Errorf("error when write file: %v", err)
}
// set cache
err = d.redisClient.SetToKey(updatedFilename, sourceRecipe)
if err != nil {
d.taoLogger.Log.Error("MergeRecipeNoCache: Error when set cache", zap.Error(err))
return "SetCacheError", fmt.Errorf("error when set cache: %v", err)
}
return updatedFilename, nil
}

View file

@ -137,7 +137,7 @@ func GetTempFile(filename string, user string, suffix int) string {
// return
// }
// // TODO: must check the changes
// // must check the changes
// for _, file := range files {
// var tmpdata models.Recipe

View file

@ -19,7 +19,7 @@ func main() {
config, err := loadConfig(".")
if err != nil {
// TODO: use default config instead
// use default config instead
log.Fatal(err)
}

View file

@ -154,8 +154,6 @@ func (rr *RecipeRouter) Route(r chi.Router) {
}
})
r.Post("/merge", rr.doMergeJson)
r.Get("/{country}/versions", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
@ -200,6 +198,8 @@ func (rr *RecipeRouter) Route(r chi.Router) {
})
r.Post("/sort/{country}/{filename}", rr.sortRecipe)
r.Post("/upgrade/{country}/{filename}", rr.upgradeVersion)
})
}
@ -419,7 +419,7 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
changes = changes.FromMap(ch_map)
rr.taoLogger.Log.Debug("Changes: ", zap.Any("changes", changes))
// TODO: find the matched pd
// find the matched pd
_, err = rr.data.GetRecipe01ByProductCode(filename, countryID, changes.ProductCode)
if err != nil {
@ -465,7 +465,7 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
// store @ temporary file
temp_file_name := helpers.GetTempFile(saved_filename, editor, 0)
// TODO: push this change, editor, commit_msg into db
// push this change, editor, commit_msg into db
// gen hash
commit_hash, err := data.HashCommit(8)
@ -482,7 +482,7 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
}
// ------------------------ SKIP THIS ------------------------
// TODO: save only changes.
// save only changes.
// get new tempfile name if redis is connected;
@ -541,7 +541,7 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
err = data.Insert(&commit)
err = rr.cache_db.SetToKey(patchName, changes)
// TODO: ^----- change this to patch
// ^----- change this to patch
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipeCache", zap.Error(errors.WithMessage(err, "Error when write file")))
@ -580,7 +580,7 @@ func (rr *RecipeRouter) updateMaterialSetting(w http.ResponseWriter, r *http.Req
return
}
// TODO: create commit and set change
// create commit and set change
}
@ -802,18 +802,6 @@ func (rr *RecipeRouter) getImageOfProductCode(w http.ResponseWriter, r *http.Req
png.Encode(w, thisImage)
}
func (rr *RecipeRouter) doMergeJson(w http.ResponseWriter, r *http.Request) {
// TODO: v2, change to binary instead
if !APIhandler(w, r) {
rr.taoLogger.Log.Warn("RecipeRouter.doMergeJson", zap.Error(errors.New("API is busy")))
return
} else {
rr.taoLogger.Log.Debug("RecipeRouter.doMergeJson", zap.Any("status", "ready"))
}
// TODO: add binary command here
}
func APIhandler(w http.ResponseWriter, r *http.Request) bool {
timeout := 10 * time.Second
@ -839,7 +827,8 @@ func lockThenTimeout(mutex *sync.Mutex, timeout time.Duration) bool {
}
}
// ------------------ Sorting ------------------
// ------------------ Sorting ------------------\
// FIXME: sorting not working, will look into it later.
func (rr *RecipeRouter) sortRecipe(w http.ResponseWriter, r *http.Request) {
country := chi.URLParam(r, "country")
filename := chi.URLParam(r, "filename")
@ -872,3 +861,63 @@ func (rr *RecipeRouter) sortRecipe(w http.ResponseWriter, r *http.Request) {
})
}
// TODO: apply contents from commit given by id
// this do match at server
func (rr *RecipeRouter) upgradeVersion(w http.ResponseWriter, r *http.Request) {
country := chi.URLParam(r, "country")
filename := chi.URLParam(r, "filename")
// get short version country
countryID, err := rr.data.GetCountryIDByName(country)
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.upgradeVersion", zap.Error(err))
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// get commit id and patch source from body
var commitInfo map[string]interface{}
err = json.NewDecoder(r.Body).Decode(&commitInfo)
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.upgradeVersion", zap.Error(err))
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
mode := ""
rr.taoLogger.Log.Debug("RecipeRouter.upgradeVersion", zap.Any("commitInfo", commitInfo))
changeKey, ok := commitInfo["changeKey"].(string)
if !ok {
rr.taoLogger.Log.Error("RecipeRouter.upgradeVersion", zap.Error(err))
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// optional AppliedMachineRecipe: if provided, use this instead
// this does mean recipe from machine has been applied on the client website
//
appliedMachineRecipe := commitInfo["appliedMachineRecipe"]
if appliedMachineRecipe != nil {
mode = "NoCache"
} else {
mode = "Recipe"
}
// upgrade version
// merge from website
result, err := rr.data.Merge(countryID, filename, mode, changeKey, nil)
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.upgradeVersion", zap.Error(err))
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"result": result,
})
}