⚠️ WIP migrating tmp to patch

This commit is contained in:
pakintada@gmail.com 2024-02-22 16:04:34 +07:00
parent 89ce1f361c
commit fed315367a
13 changed files with 317 additions and 270 deletions

View file

@ -34,7 +34,19 @@
alt="Tao Bin Logo" alt="Tao Bin Logo"
/> />
</a> </a>
<!-- Redis Status -->
<div class="p-2 rounded-lg border border-double border-black" [ngStyle]="redisStatus == 'Online'?{'background-color':'greenyellow'}:{'background-color':'tomato'}">
<p class="text-center font-bold">{{redisStatus}}</p>
</div>
</div> </div>
<!-- File Change Status -->
<button *ngIf="showDetectChanges">
<h1 class="text-center font-extrabold text-2xl text-red-500 animate-pulse">Detect Changes! Click</h1>
</button>
<div class="flex items-center"> <div class="flex items-center">
<div class="flex items-center ml-3"> <div class="flex items-center ml-3">
<div class="flex flex-row"> <div class="flex flex-row">
@ -155,3 +167,5 @@
</div> </div>
</div> </div>
</div> </div>
<!--Modal-->

View file

@ -1,10 +1,15 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router'; import { ActivatedRoute, RouterModule } from '@angular/router';
import { DatePipe, NgFor, NgIf, NgOptimizedImage } from '@angular/common'; import { CommonModule, DatePipe, NgFor, NgIf, NgOptimizedImage } from '@angular/common';
import { GoogleButtonComponent } from 'src/app/shared/googleButton/googleButton.component'; import { GoogleButtonComponent } from 'src/app/shared/googleButton/googleButton.component';
import { UserService } from '../services/user.service'; import { UserService } from '../services/user.service';
import { User } from '../models/user.model'; import { User } from '../models/user.model';
import { Subject, Subscription, map, share, takeUntil, timer } from 'rxjs'; import { Subject, Subscription, map, share, takeUntil, timer } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { RecipeService } from '../services/recipe.service';
import { AsyncStorage } from 'src/app/shared/helpers/asyncStorage';
import { MergeComponent } from "../../features/merge/merge.component";
interface MenuItem { interface MenuItem {
name: string; name: string;
@ -13,17 +18,19 @@ interface MenuItem {
} }
@Component({ @Component({
selector: 'app-layout', selector: 'app-layout',
templateUrl: './layout.component.html', templateUrl: './layout.component.html',
standalone: true, standalone: true,
imports: [ imports: [
RouterModule, RouterModule,
NgFor, NgFor,
NgIf, NgIf,
GoogleButtonComponent, GoogleButtonComponent,
DatePipe, DatePipe,
NgOptimizedImage, NgOptimizedImage,
], CommonModule,
MergeComponent
]
}) })
export class LayoutComponent implements OnInit, OnDestroy { export class LayoutComponent implements OnInit, OnDestroy {
current_department = this._router.snapshot.paramMap.get('department')!; current_department = this._router.snapshot.paramMap.get('department')!;
@ -51,12 +58,17 @@ export class LayoutComponent implements OnInit, OnDestroy {
user: User | null = null; user: User | null = null;
exit$ = new Subject<void>(); exit$ = new Subject<void>();
redisStatus:string = "Offline";
showDetectChanges: boolean = false;
constructor( constructor(
private _userService: UserService, private _userService: UserService,
private _router: ActivatedRoute private _router: ActivatedRoute,
private _httpClient: HttpClient,
private _recipeService: RecipeService
) {} ) {}
ngOnInit(): void { async ngOnInit(): Promise<void> {
this._userService.currentUser this._userService.currentUser
.pipe(takeUntil(this.exit$)) .pipe(takeUntil(this.exit$))
.subscribe((user) => (this.user = user)); .subscribe((user) => (this.user = user));
@ -69,6 +81,32 @@ export class LayoutComponent implements OnInit, OnDestroy {
.subscribe((time) => { .subscribe((time) => {
this.date = time; this.date = time;
}); });
this._httpClient.get(environment.api + "/health/redis").subscribe((status) => {
this.redisStatus = (status as any)["status"];
});
// check if saves existed
this._recipeService.getSavedTmp(
await this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile()
).subscribe({
next: async (data: any) => {
if(data != undefined && typeof data === 'object'){
// check if attr exists
if(data.files != null){
this.showDetectChanges = true;
await AsyncStorage.setItem("detectChanges", "true");
} else {
this.showDetectChanges = false;
await AsyncStorage.setItem("detectChanges", "false");
}
} else {
this.showDetectChanges = false;
await AsyncStorage.setItem("detectChanges", "false");
}
}
});
} }
ngOnDestroy() { ngOnDestroy() {

View file

@ -47,7 +47,10 @@ export class RecipeService {
return this.tmp_files; return this.tmp_files;
} }
constructor(private _httpClient: HttpClient, private _route: ActivatedRoute) {} constructor(
private _httpClient: HttpClient,
private _route: ActivatedRoute
) {}
getRecipesDashboard( getRecipesDashboard(
params: any = { params: any = {
@ -95,8 +98,9 @@ export class RecipeService {
); );
} }
async getRecipeDetail(productCode: string): Promise<Observable<RecipeDetail>> { async getRecipeDetail(
productCode: string
): Promise<Observable<RecipeDetail>> {
let asyncCountry = await this.getCurrentCountry(this.department!); let asyncCountry = await this.getCurrentCountry(this.department!);
console.log('get detail by asyncCountry', asyncCountry); console.log('get detail by asyncCountry', asyncCountry);
@ -115,8 +119,7 @@ export class RecipeService {
async getRecipeDetailMat( async getRecipeDetailMat(
productCode: string productCode: string
): Promise<Observable<{ result: RecipeDetailMat[]; }>> { ): Promise<Observable<{ result: RecipeDetailMat[] }>> {
let asyncCountry = await this.getCurrentCountry(this.department!); let asyncCountry = await this.getCurrentCountry(this.department!);
return this._httpClient.get<{ result: RecipeDetailMat[] }>( return this._httpClient.get<{ result: RecipeDetailMat[] }>(
@ -133,10 +136,8 @@ export class RecipeService {
} }
getCurrentFile(): string { getCurrentFile(): string {
// TODO: get default from server // TODO: get default from server
const currentRecipeFile = localStorage.getItem('currentRecipeFile'); const currentRecipeFile = localStorage.getItem('currentRecipeFile');
if (currentRecipeFile) { if (currentRecipeFile) {
return currentRecipeFile; return currentRecipeFile;
@ -150,9 +151,7 @@ export class RecipeService {
} }
async getCurrentCountry(department?: string): Promise<string> { async getCurrentCountry(department?: string): Promise<string> {
if (department) {
if(department){
// translate back to full name // translate back to full name
let fullname = getCountryMapSwitcher(department); let fullname = getCountryMapSwitcher(department);
@ -167,7 +166,9 @@ export class RecipeService {
// const currentRecipeCountry = localStorage.getItem('currentRecipeCountry'); // const currentRecipeCountry = localStorage.getItem('currentRecipeCountry');
const currentRecipeCountry = await AsyncStorage.getItem<string>('currentRecipeCountry'); const currentRecipeCountry = await AsyncStorage.getItem<string>(
'currentRecipeCountry'
);
if (currentRecipeCountry) { if (currentRecipeCountry) {
return currentRecipeCountry; return currentRecipeCountry;
} }
@ -275,13 +276,37 @@ export class RecipeService {
); );
} }
async getRawRecipeOfProductCode(country: string, filename: string, productCode: string): Promise<Observable<{}>> { async getRawRecipeOfProductCode(
country: string,
filename: string,
productCode: string
): Promise<Observable<{}>> {
return this._httpClient.get<{}>( return this._httpClient.get<{}>(
environment.api + '/recipes/' + country + '/' + filename + '/' + productCode + '/raw_full', environment.api +
'/recipes/' +
country +
'/' +
filename +
'/' +
productCode +
'/raw_full',
{ {
withCredentials: true, withCredentials: true,
responseType: 'json', responseType: 'json',
} }
); );
} }
async getPatchListOfCurrentFile(
country: string,
filename: string
): Promise<Observable<any>> {
console.log("try get patches", country, filename);
return this._httpClient.get<any>(
environment.api + '/recipes/patch/get/' + country + '/' + filename ,
{ withCredentials: true, responseType: 'json' }
);
}
} }

View file

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MergeComponent } from '../merge/merge.component';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment.development'; import { environment } from 'src/environments/environment.development';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
@ -10,7 +10,7 @@ import { FetchLogService } from 'src/app/shared/services/fetch-log.service';
standalone: true, standalone: true,
templateUrl: './changelog.component.html', templateUrl: './changelog.component.html',
styleUrls: ['./changelog.component.css'], styleUrls: ['./changelog.component.css'],
imports: [CommonModule, MergeComponent], imports: [CommonModule],
}) })
export class ChangelogComponent { export class ChangelogComponent {
public displayableLogs: string[] = []; public displayableLogs: string[] = [];

View file

@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { MergeServiceService } from './merge-service.service';
describe('MergeServiceService', () => {
let service: MergeServiceService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MergeServiceService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View file

@ -1,14 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MergeServiceService {
master_version: number = 0;
dev_version: number = 0;
output_path:string = "";
changelog_path:string = "";
constructor() { }
}

View file

@ -1,55 +1 @@
<p>merge works!</p>
<div class="flex-3 bg-stone-400 h-screen justify-centers items-center">
<h3 class="text-2xl text-center py-4 font-semibold">Merge 2 json</h3>
<div class="bg-stone-600 p-1 m-2 rounded-md">
<div class="bg-stone-500 m-2 rounded-md">
<p class="px-1 py-5 font-semibold">
❓ What does this function do?
</p>
<div class="bg-stone-300 rounded">
<p class="px-2 ">
Apply changes from the `dev` version
<br>into the `master` version
</p>
</div>
</div>
<div class="bg-stone-200 p-1 m-2 rounded-md">
<p class="px-2 py-4 text-md text-center font-bold">
❗Beware❗
<br>`master` = base version
<br>`dev` = your version
</p>
</div>
<form class="space-y-6 p-3 bg-stone-500 rounded" [formGroup]="mergeForm" (ngSubmit)="fetchMerge()">
<div class="flex">
<label class="flex-1 text-red-700 font-bold bg-yellow-100 rounded text-center" for="master_version">Master</label>
<input class="flex-1 mx-1 bg-slate-300 hover:bg-blue-400 text-center border border-collapse rounded-md" id="master_version" formControlName="master_version" type="text" required>
</div>
<div class="flex">
<label class="flex-1 bg-yellow-100 font-bold rounded text-center" for="dev_version">Dev</label>
<input class="flex-1 mx-1 bg-slate-300 hover:bg-blue-400 text-center border border-collapse rounded-md" id="dev_version" formControlName="dev_version" type="text" required>
</div>
<!-- Output path -->
<div class="flex">
<label class="flex-1 bg-yellow-100 font-bold rounded text-center" for="output_path">Output Path (.json)</label>
<input class="flex-1 mx-1 bg-slate-300 hover:bg-blue-400 text-center border border-collapse rounded-md" id="output_path" formControlName="output_path" type="text" required>
</div>
<!-- Changelog path -->
<div class="flex">
<label class="flex-1 bg-yellow-100 font-bold rounded text-center" for="changelog_path">Changelog Path (.json)</label>
<input class="flex-1 mx-1 bg-slate-300 hover:bg-blue-400 text-center border border-collapse rounded-md" id="changelog_path" formControlName="changelog_path" type="text" required>
</div>
<button class="button font-semibold bg-red-300 p-4 border border-collapse rounded-md" type="submit">Begin Merge</button>
</form>
</div>
</div>

View file

@ -1,21 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MergeComponent } from './merge.component';
describe('MergeComponent', () => {
let component: MergeComponent;
let fixture: ComponentFixture<MergeComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [MergeComponent]
});
fixture = TestBed.createComponent(MergeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,90 +1,31 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RecipeService } from 'src/app/core/services/recipe.service';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { MergeServiceService } from './merge-service.service';
import { HttpClient, HttpParams } from '@angular/common/http';
import { environment } from 'src/environments/environment.development';
import { Observable } from 'rxjs';
import { ChangelogComponent } from '../changelog/changelog.component';
import { FetchLogService } from 'src/app/shared/services/fetch-log.service';
@Component({ @Component({
selector: 'app-merge', selector: 'app-merge',
standalone: true, standalone: true,
imports: [CommonModule, ReactiveFormsModule, ChangelogComponent], imports: [CommonModule],
templateUrl: './merge.component.html', templateUrl: './merge.component.html',
styleUrls: ['./merge.component.css']
}) })
export class MergeComponent<T> { export class MergeComponent implements OnInit {
exceptionValues = [0, null, undefined] patchMap: any = {}
mergeForm = this.formBuilder.group({
master_version: 0,
dev_version: 0,
output_path: "",
changelog_path: ""
});
default_output_path = "cofffeemachineConfig/merge/"
default_changelog_path = "cofffeemachineConfig/changelog/"
mergeLogs: Map<string, string> | void | undefined
constructor( constructor(
private targets: MergeServiceService, private _recipeService: RecipeService
private formBuilder: FormBuilder, ) { }
private httpClient: HttpClient,
private chlog: ChangelogComponent
){
// Default fetching logs
// fetch html async ngOnInit(): Promise<void> {
// this.fetchLogsToDisplay("", true, false); (await this._recipeService.getPatchListOfCurrentFile(
// // fetch log file await this._recipeService.getCurrentCountry(),
// this.fetchLogsToDisplay("", false, false); this._recipeService.getCurrentFile()
// // fetch json )).subscribe({
// this.mergeLogs = this.fetchLogsToDisplay("", false, true); next: (data: any) => {
this.patchMap = data;
console.log("patches",this.patchMap);
}
});
} }
private isException(value: any){
return this.exceptionValues.includes(value)
}
fetchMerge(){
if(this.isException(this.mergeForm.value.master_version) || this.isException(this.mergeForm.value.dev_version)){
return
}
this.targets.master_version = this.mergeForm.value.master_version!;
this.targets.dev_version = this.mergeForm.value.dev_version!;
this.targets.output_path = this.default_output_path + this.mergeForm.value.output_path!;
this.targets.changelog_path = this.default_changelog_path + this.mergeForm.value.changelog_path!;
// TODO: Fetch merge. Modify this to websocket
this.httpClient.post<T>(environment.api+"/merge", {
master: this.targets.master_version,
dev: this.targets.dev_version,
output: this.targets.output_path,
changelog: this.targets.changelog_path
}, {
withCredentials: true
}).subscribe({
next: (value: T) => {
console.log(value)
if(typeof value === "object" && value !== null){
if("message" in value){
// fetch html
// this.fetchLogsToDisplay("", true, false);
// fetch log file
// this.fetchLogsToDisplay("", false, false);
// fetch json
this.mergeLogs = new FetchLogService(this.httpClient).fetchLogsToDisplay("", false, true,this.targets.changelog_path);
this.chlog.fetchLoglist();
this.chlog.translateLogDirToString();
}
}
},
})
}
} }

View file

@ -89,7 +89,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
savedTmpfiles: any[] = []; savedTmpfiles: any[] = [];
saveTab: boolean = false; saveTab: boolean = false;
showSaveNoti: boolean = true; showSaveNoti: boolean = false;
department: string = this.route.parent!.snapshot.params['department']; department: string = this.route.parent!.snapshot.params['department'];
copyList: any[] = []; copyList: any[] = [];
@ -197,7 +197,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
}) })
); );
// FIXME: Lag assigned // : Lag assigned
this.recipesDashboard$.subscribe(async (data) => { this.recipesDashboard$.subscribe(async (data) => {
this.currentVersion = data.configNumber; this.currentVersion = data.configNumber;
@ -241,37 +241,37 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
// end of FIXME // end of FIXME
this._recipeService // this._recipeService
.getSavedTmp( // .getSavedTmp(
await this._recipeService.getCurrentCountry(this.department), // await this._recipeService.getCurrentCountry(this.department),
this._recipeService.getCurrentFile() // this._recipeService.getCurrentFile()
) // )
.subscribe({ // .subscribe({
next: (files: any) => { // next: (files: any) => {
console.log('Obtain saves: ', typeof files, files); // console.log('Obtain saves: ', typeof files, files);
this.showSaveNoti = false; // this.showSaveNoti = false;
if (files != undefined && typeof files === 'object') { // if (files != undefined && typeof files === 'object') {
if (files.files != null) { // if (files.files != null) {
console.log( // console.log(
'Obtain saves object: ', // 'Obtain saves object: ',
files.files[0], // files.files[0],
typeof files // typeof files
); // );
this.savedTmpfiles = files.files; // this.savedTmpfiles = files.files;
} else { // } else {
this.showSaveNoti = false; // this.showSaveNoti = false;
this.savedTmpfiles = []; // this.savedTmpfiles = [];
console.log(this.showSaveNoti, this.savedTmpfiles); // console.log(this.showSaveNoti, this.savedTmpfiles);
} // }
// let svf = (document.getElementById('select_savefile_modal') as HTMLInputElement)!.checked; // // let svf = (document.getElementById('select_savefile_modal') as HTMLInputElement)!.checked;
// console.log("isSavedModalOpened",svf) // // console.log("isSavedModalOpened",svf)
} else { // } else {
this.showSaveNoti = false; // this.showSaveNoti = false;
this.savedTmpfiles = []; // this.savedTmpfiles = [];
console.log(this.showSaveNoti, this.savedTmpfiles); // console.log(this.showSaveNoti, this.savedTmpfiles);
} // }
}, // },
}); // });
(await this._materialService.getMaterialCodes()) (await this._materialService.getMaterialCodes())
.pipe( .pipe(

View file

@ -135,3 +135,39 @@ func (r *RedisCli) SetKeyTimeout(key string, value interface{}, timeout int) err
fmt.Println("error on EXPIRE ", err) fmt.Println("error on EXPIRE ", err)
return err return err
} }
func (r *RedisCli) KeyList() ([]string, error) {
// if cannot pass healthcheck, return err
if err := r.HealthCheck(); err != nil {
fmt.Println("HS> KEYS error ", err)
return nil, err
}
keys := r.Client.Keys(context.Background(), "*")
return keys.Result()
}
// list operations
func (r *RedisCli) GetList(key string) ([]string, error) {
// if cannot pass healthcheck, return err
if err := r.HealthCheck(); err != nil {
fmt.Println("HS> List.GET error ", err)
return nil, err
}
return r.Client.LRange(context.Background(), key, 0, -1).Result()
}
func (r *RedisCli) Add(key string, value interface{}) error {
// if cannot pass healthcheck, return err
if err := r.HealthCheck(); err != nil {
fmt.Println("HS> List.ADD error ", err)
return err
}
err := r.Client.RPush(context.Background(), key, value)
return err.Err()
}

View file

@ -101,6 +101,8 @@ func (rr *RecipeRouter) Route(r chi.Router) {
r.Get("/saved/{country}/{filename_version_only}", rr.getSavedRecipes) r.Get("/saved/{country}/{filename_version_only}", rr.getSavedRecipes)
r.Get("/patch/get/{country}/{filename}", rr.getSavedAsPatches)
r.Get("/departments", func(w http.ResponseWriter, r *http.Request) { r.Get("/departments", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
@ -447,6 +449,7 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
tempRecipe := models.Recipe01{} tempRecipe := models.Recipe01{}
tempRecipe = tempRecipe.FromMap(changeMap) tempRecipe = tempRecipe.FromMap(changeMap)
rr.data.SetValuesToRecipe(targetRecipe.Recipe01, tempRecipe) rr.data.SetValuesToRecipe(targetRecipe.Recipe01, tempRecipe)
rr.taoLogger.Log.Debug("ApplyChange", zap.Any("status", "passed")) rr.taoLogger.Log.Debug("ApplyChange", zap.Any("status", "passed"))
// check if changed // check if changed
@ -462,7 +465,7 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
// gen hash // gen hash
commit_hash, err := data.HashCommit(8) commit_hash, err := data.HashCommit(8)
rr.cache_db.SetToKey(commit_hash, targetRecipe) // rr.cache_db.SetToKey(commit_hash, targetRecipe)
commit := data.CommitLog{ commit := data.CommitLog{
@ -474,31 +477,68 @@ func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
Relation: filename, Relation: filename,
} }
// ------------------------ SKIP THIS ------------------------
// TODO: save only changes.
// get new tempfile name if redis is connected;
productCodeNoSpl := strings.ReplaceAll(changes.ProductCode, "-", "")
patchName := "Recipe_" + productCodeNoSpl + "-" + commit_hash + "_" + filename + ".patch"
// if cache service online
if rr.cache_db.HealthCheck() == nil {
// do change mode
commit.Change_file = patchName
commit.Relation = commit.Relation + "/patch"
// add to patch list of that filename
// filename:patchlist
err := rr.cache_db.Add(countryID+"."+filename+":patchList", commit.Id)
// add failed
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when tried to add to patch list")))
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
} else {
// this following codes do need users to pass update to each other
// otherwise, the changes will diverge
file, _ := os.Create(temp_file_name)
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when tried to create file")))
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
// write to local if cannot connect
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
// full
err = encoder.Encode(targetRecipe)
// -------------------------------------------------------------
// partial
// err = encoder.Encode(changes)
// put changes to redis
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when write file")))
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
}
// add to commit
err = data.Insert(&commit) err = data.Insert(&commit)
file, _ := os.Create(temp_file_name) err = rr.cache_db.SetToKey(patchName, changes)
if err != nil { // TODO: ^----- change this to patch
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when tried to create file")))
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
// full
err = encoder.Encode(targetRecipe)
// partial
// err = encoder.Encode(changes)
// put changes to redis
if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when write file")))
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
err = rr.cache_db.SetToKey(commit_hash+"_"+filename, changes)
if err != nil { if err != nil {
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipeCache", zap.Error(errors.WithMessage(err, "Error when write file"))) rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipeCache", zap.Error(errors.WithMessage(err, "Error when write file")))
@ -567,6 +607,64 @@ func (rr *RecipeRouter) getSavedRecipes(w http.ResponseWriter, r *http.Request)
json.NewEncoder(w).Encode(map[string]interface{}{"files": commits}) json.NewEncoder(w).Encode(map[string]interface{}{"files": commits})
} }
func (rr *RecipeRouter) getSavedAsPatches(w http.ResponseWriter, r *http.Request) {
filename := chi.URLParam(r, "filename")
country := chi.URLParam(r, "country")
countryID, err := rr.data.GetCountryIDByName(country)
if err != nil {
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", country), http.StatusNotFound)
return
}
patchList, err := rr.cache_db.GetList(countryID + "." + filename + ":patchList")
if err != nil {
// silent return, no patch
return
}
rr.taoLogger.Log.Debug("RecipeRouter.getSavedAsPatches", zap.Any("targetPatchOf", countryID+"."+filename+":patchList"), zap.Any("patchList", patchList))
// find patch content from patch list
keys, err := rr.cache_db.KeyList()
if err != nil {
// keys found nothing
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
// loop through keys, if contain patch id
patchMap := map[string]models.Recipe01{}
for _, key := range keys {
if strings.Contains(key, filename) && strings.Contains(key, "patch") {
// check if legit saved file from patchList
for _, patchID := range patchList {
if strings.Contains(key, patchID) {
// get patch content
var recipePatch models.Recipe01
err := rr.cache_db.GetKeyTo(key, &recipePatch)
if err != nil {
// silent return, no patch
return
}
// append to patch list
// patchContents = append(patchContents, recipePatch)
patchMap[patchID] = recipePatch
}
}
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(patchMap)
rr.taoLogger.Log.Debug("RecipeRouter.getSavedAsPatches", zap.Any("patchMap", patchMap))
}
func (rr *RecipeRouter) getToppings(w http.ResponseWriter, r *http.Request) { func (rr *RecipeRouter) getToppings(w http.ResponseWriter, r *http.Request) {
countryID := chi.URLParam(r, "country") countryID := chi.URLParam(r, "country")
@ -626,7 +724,7 @@ func (rr *RecipeRouter) getRawRecipeOfProductCode(w http.ResponseWriter, r *http
productCode := chi.URLParam(r, "product_code") productCode := chi.URLParam(r, "product_code")
// debug // debug
rr.taoLogger.Log.Debug("RecipeRouter.getRawRecipeOfProductCode", zap.Any("countryID", countryID), zap.Any("filename", filename), zap.Any("productCode", productCode)) // rr.taoLogger.Log.Debug("RecipeRouter.getRawRecipeOfProductCode", zap.Any("countryID", countryID), zap.Any("filename", filename), zap.Any("productCode", productCode))
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
@ -638,7 +736,7 @@ func (rr *RecipeRouter) getRawRecipeOfProductCode(w http.ResponseWriter, r *http
} }
// return recipe // return recipe
rr.taoLogger.Log.Debug("RecipeRouter.getRawRecipeOfProductCode", zap.Any("recipe", recipe)) // rr.taoLogger.Log.Debug("RecipeRouter.getRawRecipeOfProductCode", zap.Any("recipe", recipe))
json.NewEncoder(w).Encode(recipe) json.NewEncoder(w).Encode(recipe)
} }