feat(merge_component): Add third souce for diff

Add third source (only from recipe, WIP commit) and attach debugger for checking functions
This commit is contained in:
pakintada@gmail.com 2024-03-05 11:12:03 +07:00
parent 860dc05cde
commit 1b96e298ee
8 changed files with 439 additions and 175 deletions

View file

@ -24,10 +24,7 @@
></path>
</svg>
</button>
<a
routerLink="/departments"
class="flex ml-14 max-sm:hidden"
>
<a routerLink="/departments" class="flex ml-14 max-sm:hidden">
<img
src="assets/logo.svg"
class="h-10 md:h-20 px-4"
@ -35,7 +32,14 @@
/>
</a>
<!-- Redis Status -->
<div class="p-2 rounded-lg border border-double border-black" [ngStyle]="redisStatus == 'Online'?{'background-color':'greenyellow'}:{'background-color':'tomato'}">
<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>
@ -43,24 +47,21 @@
<!-- File Change Status -->
<div *ngIf="isCommitLoaded | async">
<button onclick="patch_merge_modal.showModal()">
<h1 class="text-center font-extrabold text-2xl text-red-500 animate-pulse">Detect Changes! Click</h1>
<h1
class="text-center font-extrabold text-2xl text-red-500 animate-pulse"
>
Detect Changes! Click
</h1>
</button>
<dialog id="patch_merge_modal" class="modal">
<div class="modal-box max-w-screen-2xl">
<app-merge
[commit]="changesCommit"
></app-merge>
<div class="modal-action sticky bottom-0 right-0">
<div class="modal-action">
<form method="dialog">
<button class="btn btn-warning">Close</button>
<button class="text-3xl">&#x2715;</button>
</form>
</div>
<div class="modal-box max-w-screen-2xl">
<app-merge [commit]="changesCommit"></app-merge>
</div>
</dialog>
</div>
@ -184,4 +185,3 @@
</div>
</div>
</div>

View file

@ -19,16 +19,50 @@
</option>
</select>
<br />
<!-- third source selector / from machine -->
<label for="anotherKeys">Select another source : </label>
<select class="select select-sm select-primary" name="anotherKeys" (change)="selectAnotherSource($event)">
<option>--- Commit ---</option>
<option
*ngFor="let commit of getPatchMapKeys()"
[value]="commit"
id="source3_{{ commit }}"
>
[{{ getCommitAttr(commit, "Created_at") }}] [{{
getCommitAttr(commit, "Editor")
}}] | {{ getCommitAttr(commit, "Msg") }}
</option>
<option>--- Machine ---</option>
<option>--- Recipe ---</option>
</select>
<input type="text" placeholder="version number only..." *ngIf="this.anotherSelectedSource == '--- Recipe ---'" (mouseout)="changeAnotherSource($event)">
<!-- enable from console -->
<button class="hidden" (click)="testLoadCheck()">Test load</button>
<!-- This do send to server that the shown commit id will 1be applied to main recipe -->
<div class="m-2 space-x-2" *ngIf="selectedCommit != '' && selectedCommit != '---'">
<div
class="m-2 space-x-2"
*ngIf="selectedCommit != '' && !isNotSelectable()"
>
<button class="btn btn-primary" (click)="sendApply()">Apply</button>
<button class="btn btn-error">Reject</button>
</div>
<!-- Queue -->
<div
class="hidden tooltip m-2 p-2 bg-lime-200 w-48 shadow"
data-tip="All added commits/changes will be applied to this version"
>
<p class="text-center font-semibold justify-center">Merge List</p>
<div class="space-x-2">
<button mat-raised-button>Add</button>
<button mat-raised-button>Remove</button>
</div>
</div>
</div>
</div>
<!-- 3 columns with master at center -->
@ -41,11 +75,92 @@
selectedCommit != '---'
"
>
<details class="collapse collapse-arrow">
<summary class="collapse-title bg-red-200">{{ selectedCommit }}</summary>
<!-- option if has ignored in list -->
<div
class="collapse-content"
class="m-2 p-2 !rounded-md bg-gray-400"
*ngIf="!isIgnoreListEmpty() && isCommitHasIgnored()"
>
<div class="flex p-2">
<p class="font-semibold text-md">Options</p>
</div>
<div
class="flex flex-row p-2 bg-gray-200 space-x-2"
id="ignore-replace-with-base-{{ selectedCommit }}"
>
<input type="checkbox" id="replace-{{ selectedCommit }}-with-base" />
<p>Replace selected values with master value</p>
</div>
<div
class="flex flex-row p-2 bg-gray-200 space-x-2"
id="ignore-replace-with-machine-{{ selectedCommit }}"
>
<input
type="checkbox"
id="replace-{{ selectedCommit }}-with-machine"
/>
<p>Replace selected values with machine value</p>
</div>
<!-- remove -->
<div
class="flex flex-row p-2 bg-gray-200 space-x-2"
id="remove-{{ selectedCommit }}"
>
<input type="checkbox" id="remove-{{ selectedCommit }}" />
<p>Remove it. Don't include this</p>
</div>
<!-- change state -->
<div
class="flex flex-row p-2 bg-gray-200 space-x-2"
id="toggle-state-{{ selectedCommit }}"
>
<input type="checkbox" id="toggle-state-{{ selectedCommit }}" />
<p>Switch state. On {{ "<-->" }} Off</p>
</div>
<!-- other -->
<div class="hidden p-2 bg-gray-200 space-x-2" id="command">
<div class="flex flex-row p-2 space-x-2">
<input type="checkbox" id="command-enable" />
<p>Command</p>
</div>
<!-- <input class="w-56" type="text"> -->
<div class="flex flex-row gap-2">
<code class="">
<textarea
class="p-2 resize-none bg-black text-white"
id="command-code"
cols="30"
rows="10"
>
//Evaluate</textarea
>
</code>
<code class="">
<textarea
class="p-2 resize bg-black text-green-600"
id="command-output"
cols="30"
rows="10"
></textarea>
</code>
</div>
</div>
<div class="flex justify-end">
<button class="m-2 btn btn-warning right-2" (click)="eval()">
OK
</button>
</div>
</div>
<details class="collapse collapse-arrow">
<summary class="collapse-title bg-red-200">
{{ selectedCommit }}
</summary>
<div class="collapse-content">
<app-recipe-list
[productCode]="
getCommitAttr(selectedCommit, 'contents').productCode
@ -64,7 +179,10 @@
<!-- display zone -->
<div *ngFor="let pd of getProductCodesOfCommits()">
<div class="container" *ngIf="pd == getCommitAttr(selectedCommit, 'contents').productCode">
<div
class="container"
*ngIf="pd == getCommitAttr(selectedCommit, 'contents').productCode"
>
<details class="collapse collapse-arrow">
<summary class="collapse-title">{{ pd }}</summary>
<div>
@ -72,8 +190,7 @@
[productCode]="pd!"
[noFetch]="false"
[displayOnly]="true"
[diffChangeContext]="buildContext()"
(recipeListFormChange)="onRecipeListFormChange($event)"
[diffChangeContext]="buildContext('master', pd)"
></app-recipe-list>
</div>
<!-- collapse -->
@ -81,5 +198,30 @@
</div>
</div>
</div>
<div id="another-source" *ngIf="hasProductCodeOfCommits() && anotherSelectedSource != ''">
<div *ngFor="let pd of getProductCodesOfCommits()">
<div *ngIf="pd == getCommitAttr(selectedCommit, 'contents').productCode && anotherTargetRecipe[pd] != undefined">
<details class="collapse collapse-arrow">
<summary class="collapse-title">{{pd}} - {{anotherSelectedSource}}</summary>
<div>
<app-recipe-list
[productCode]="pd!"
[noFetch]="true"
[recipeList]="anotherTargetRecipe[pd].recipes"
[displayOnly]="true"
[diffChangeContext]="buildContext('right', pd)"
(recipeListFormChange)="onSourceListFormChange($event)"
>
</app-recipe-list>
</div>
</details>
</div>
</div>
</div>
</div>
<!-- another source -->
<!-- prep for machine recipe-->
</div>

View file

@ -33,6 +33,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { FormsModule } from '@angular/forms';
import { RecipeListComponent } from '../recipes/recipe-details/recipe-list/recipe-list.component';
import { ResizeEvent, ResizableModule } from 'angular-resizable-element';
import { Debugger } from 'src/app/shared/helpers/debugger';
@Component({
selector: 'app-merge',
@ -48,11 +50,26 @@ import { ResizeEvent, ResizableModule } from 'angular-resizable-element';
MatFormFieldModule,
MatSelectModule,
FormsModule,
ResizableModule
ResizableModule,
],
})
export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {
onRecipeListFormChange($event: any) {}
// from web client
onRecipeListFormChange($event: any) {
console.log("left side change: ", $event)
// check if event[1] is an array of 30 elements or more
if (!Array.isArray($event[0])) {
// event contains index of selection
this.ignoredMap[this.selectedCommit] = $event;
}
}
// from another source
onSourceListFormChange($event: any) {
console.log("right side change: ", $event)
}
// input from layout::getSavedTmp
@Input() commit: Array<any> | null | undefined = undefined;
@ -67,6 +84,8 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
targetRecipe: any = {};
// change map
changeMap: any = {};
// from another source
anotherTargetRecipe: any = {};
// -------------------- current selection
@ -84,6 +103,9 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
| undefined = undefined;
selectedCommit: string = '';
anotherSelectedSource: string = '';
ignoredMap: any = {};
// --------------------- Portal
// deprecated~!
@ -100,6 +122,9 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
// ---------------------- Recipe from machine
recipeFromMachine: any = {};
// --- debug
mergeDebugger = new Debugger();
constructor(
private _recipeService: RecipeService
)
@ -135,6 +160,7 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
// store only
await this.getMasterRecipeOfProductCode(productCode!);
// console.log("get master-commits recipe", this.mapProductCodeWithCommits);
});
@ -149,8 +175,6 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
async ngOnInit(): Promise<void> {
// fetch related product codes
}
@ -239,17 +263,33 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
};
selectCommit = (commit: any) => {
console.log('select commit', commit.target.value);
// console.log('select commit', commit.target.value);
this.selectedCommit = commit.target.value;
}
selectAnotherSource = (sourceType: string) => {
selectAnotherSource = (source: any) => {
this.anotherSelectedSource = source.target.value;
}
buildContext = () => {
changeAnotherSource = async (source: any) => {
this.anotherSelectedSource = "coffeethai02_" + source.target.value+".json";
console.log("another source: target version -> ", this.anotherSelectedSource);
// activate fetch
for(let pd of this.getProductCodesOfCommits()){
await this.getAnotherRecipeOfProductCode(pd);
}
}
if(this.selectedCommit == "" || this.selectedCommit == undefined || this.selectedCommit == '---'){
// get recipe from machine
// -- check ignore
isIgnoreListEmpty = () => Object.keys(this.ignoredMap).length === 0;
isCommitHasIgnored = () => this.ignoredMap[this.selectedCommit] != undefined && this.ignoredMap[this.selectedCommit].length > 0;
isNotSelectable = () => this.selectedCommit.startsWith('---');
buildContext = (ctxType?:string, pd?: string) => {
if(this.selectedCommit == "" || this.selectedCommit == undefined || this.selectedCommit.startsWith('---')){
return {
changeContext: undefined,
skipZeroes: true,
@ -265,6 +305,23 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
// };
if(ctxType != undefined){
switch(ctxType){
case "master":
return {
changeContext: undefined,
skipZeroes: true,
toppingData: this.targetRecipe[pd!].ToppingSet,
};
case "right":
return {
changeContext: undefined,
skipZeroes: true,
toppingData: this.anotherTargetRecipe[pd!].ToppingSet,
};
}
}
return {
changeContext: undefined,
@ -307,6 +364,38 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
});
};
getAnotherRecipeOfProductCode = async (productCode: string) => {
(
await this._recipeService.getRawRecipeOfProductCode(
await this._recipeService.getCurrentCountry(),
this.anotherSelectedSource,
productCode
)
).subscribe({
next: (data: any) => {
this.anotherTargetRecipe[productCode] = data;
// console.log("get master recipe", this.targetRecipe);
// for each patch, get diff master <---> patch
// test compare
this.getPatchMapKeys().forEach((patchId) => {
// compare with master
let cmp = compare(
this.anotherTargetRecipe[productCode!],
this.fullPatches[patchId].contents,
['LastChange']
);
// save only what changes
this.changeMap[patchId+"_"+this.anotherSelectedSource] = {
changes: cmp,
};
});
console.log('change map', this.changeMap);
},
});
};
generateMasterRecipeList(productCode: string): any {
// this do fetch recipelist
@ -319,70 +408,6 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
return this.targetRecipe[productCode].recipes;
}
// getRecipeFromSingleLayerPath = (
// productCode: string,
// path: string,
// ref_id: string,
// popLast?: boolean
// ) => {
// console.log(
// 'mapProductCodeWithCommits map = ',
// this.mapProductCodeWithCommits
// );
// // split path
// let pathList = path.split('.');
// if (popLast) {
// // pop last pathlist
// pathList.pop();
// }
// // get product code data
// let productCodeData = this.targetRecipe[productCode];
// let data = undefined;
// for (let pathKey of pathList) {
// if (data == undefined) {
// if (!isNaN(parseInt(pathKey))) {
// data = productCodeData[parseInt(pathKey)];
// } else {
// data = productCodeData[pathKey];
// }
// } else {
// if (!isNaN(parseInt(pathKey))) {
// data = data[parseInt(pathKey)];
// } else {
// data = data[pathKey];
// }
// }
// }
// // console.log('data from path', path,"=", data);
// // setter
// this.selectedProductCode = productCode;
// this.currentTargetOfMaster = productCodeData.recipes;
// // configure for changes display
// // this.highlightChanges = data;
// // this.minimizeUnchanged = true;
// // packing into one map; do update if selected
// let changeConfigure = {
// minimizeUnchanged: true,
// highlightChangeWhenMatched: data,
// targetProductCode: productCode,
// targetRecipe: productCodeData,
// path: path,
// changes: this.fullPatches[ref_id].contents,
// };
// this.changePackage = changeConfigure;
// };
// revert search by using commit ref id to find productCode
// getProductCodeByCommitRef = (commitRef: string) => {
// return this.patchMap[commitRef].productCode;
// };
// use with 'apply' button
async sendApply() {
@ -399,6 +424,11 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
to_send["appliedMachineRecipe"] = this.recipeFromMachine;
}
// multi-target changes
// if(){
// }
console.log("sending upgrade", to_send);
this._recipeService.upgradeRecipe(
@ -420,4 +450,10 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy, OnChang
}
});
}
// ---------------
// attach debugger
eval = () => this.mergeDebugger.eval(this);
}

View file

@ -22,10 +22,9 @@
<tr
class="bg-white border-b max-h-4"
[ngClass]="{
'bg-red-400': selectedRecipeList.includes(i)
'!bg-red-400': selectedRecipeList.includes(i)
}"
formGroupName="{{ i }}"
(click)="addToSelection(i)"
(mousedown)="initHoldEvent()"
(mouseup)="openRecipeListEditor(i)"
*ngIf="invokeZeroMaterialChecker(i)"

View file

@ -762,6 +762,11 @@ export class RecipeListComponent implements OnInit, OnChanges {
}
this.selectedRecipeList.push(i);
console.log('selected recipe list', this.selectedRecipeList);
if(this.displayOnly){
// ignore list
this.recipeListFormChange.emit(this.selectedRecipeList);
}
}
get recipeListData(): FormArray {
@ -996,6 +1001,7 @@ export class RecipeListComponent implements OnInit, OnChanges {
async openRecipeListEditor(i: number) {
await Promise.resolve();
if (this.timeoutHandler) {
console.log("timeout", this.timeout, "display only: ", this.displayOnly);
if (this.timeout >= 20 && !this.displayOnly) {
// alert("Opening Recipe List Editor in detail")
if (
@ -1005,6 +1011,13 @@ export class RecipeListComponent implements OnInit, OnChanges {
} else {
this.showDetailRecipeList = false;
}
} else if (this.timeout >= 0 && this.displayOnly){
console.log("Activate add to selection",i)
this.addToSelection(i);
} else if (this.timeout < 10 && !this.displayOnly){
// hayaina! taimu a-uto
console.log("早お腹(very fast)!タイムアウト(timeout)", i);
this.addToSelection(i);
}
clearInterval(this.timeoutHandler);

View file

@ -0,0 +1,130 @@
// <div class="hidden p-2 bg-gray-200 space-x-2" id="command">
// <div class="flex flex-row p-2 space-x-2">
// <input type="checkbox" id="command-enable">
// <p>Command</p>
// </div>
// <!-- <input class="w-56" type="text"> -->
// <div class="flex flex-row gap-2">
// <code class="">
// <textarea class="p-2 resize-none bg-black text-white" id="command-code" cols="30" rows="10">//Evaluate</textarea>
// </code>
// <code class="">
// <textarea class="p-2 resize bg-black text-green-600" id="command-output" cols="30" rows="10"></textarea>
// </code>
// </div>
// </div>
// <div class="flex justify-end">
// <button class="m-2 btn btn-warning right-2" (click)="eval()">OK</button>
// </div>
export class Debugger {
public output: any[] = [];
// evaluate
eval(comp: any) {
let enableCommand = document.getElementById('command-enable') as any;
this.clear();
if(enableCommand.checked){
let command = document.getElementById('command-code') as any;
console.log(">eval=",command.value);
let holdon:any = {};
// split line
let lines = command.value.split('\n');
lines.forEach((line: string, index: number) => {
// this.output.push(line);
// escape line, exec function of class
if(line.startsWith("!tb")){
let cmd = line.substring(3).trim();
switch(cmd){
case "proto":
this.output.push(Object.keys(comp));
break;
case "clear":
this.clear();
break;
case "store":
let varname = (lines[index -1] as string).split("=")[0].trim();
if(varname.includes("let") || varname.includes('var')){
varname = varname.split(" ")[1];
}
holdon[varname] = this.output[this.output.length - 1];
this.output.push("OK");
break;
case "vars":
this.output.push(JSON.stringify(holdon));
break;
default:
if(cmd.startsWith("fn::")){
let execFn = cmd.substring(4);
// get some args
let args = execFn.split(' ');
execFn = args[0];
if(args[1].includes("Var::") || args[1].includes("var::")){
args[1] = holdon[args[1].split("::")[1]]
}
if(args[1].includes(',')){
args = args[1].split(',');
} else {
args = [args[1]];
}
console.log("args>", args);
// try exec class function
try{
let res = (comp as any)[execFn].apply("", args);
console.log("res>",res);
this.output.push(res);
} catch (e) {
this.output.push(e);
}
} else if(cmd.startsWith("get::")){
let getter = cmd.substring(5);
this.output.push(JSON.stringify(comp[getter]));
} else if(cmd.startsWith("var::")){
let varname = cmd.substring(5);
this.output.push(JSON.stringify(holdon[varname]));
}
break;
}
} else if (!line.startsWith("//")){
if(!line.startsWith("window") && !line.startsWith("let") && !line.startsWith("var")){
line = "window."+line;
}
try {
// is non-comment, exec js
let evalResult = eval(line);
// if(evalResult == undefined){
// evalResult = "see the console";
// }
this.output.push(evalResult);
} catch (error) {
this.output.push(error+" or scoped not global. Help: each line's scope is end within its line, you may want to drop `let` or `var` to let it live longer ~. Otherwise, write in the same line.");
}
}
});
this.print();
} else {
this.clear();
}
console.log("output", this.output);
}
clear(){
this.output = [];
}
print(){
let output = document.getElementById('command-output') as any;
output.value = this.output.join('\n');
}
}

View file

@ -1079,78 +1079,17 @@ func (d *Data) MergeRecipe(country, filename, changeKey string) (string, error)
}
// get recipe
// var sourceRecipe models.Recipe
sourceRecipe := d.GetRecipe(country, filename)
// copy(d.GetRecipe(country, filename), &sourceRecipe)
// check address
fmt.Println("[Source] source === 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
return d.finalizedVersion(country, sourceRecipe)
}
func (d *Data) MergeRecipeNoCache(country string, filename string, updatedRecipe models.Recipe01) (string, error) {
@ -1159,6 +1098,11 @@ func (d *Data) MergeRecipeNoCache(country string, filename string, updatedRecipe
// apply value to its source
d.SetValuesToRecipe(sourceRecipe.Recipe01, updatedRecipe)
return d.finalizedVersion(country, sourceRecipe)
}
func (d *Data) finalizedVersion(country string, sourceRecipe *models.Recipe) (string, error) {
// updating version
sourceRecipe.MachineSetting.ConfigNumber += 1
newVersionStr := strconv.Itoa(sourceRecipe.MachineSetting.ConfigNumber)

View file

@ -139,7 +139,7 @@ func (s *Server) createHandler() {
ur := routers.NewUserRouter(s.taoLogger, userService)
ur.Route(r)
// fmt.Println("routers", r.Routes())
// //fmt.Println("routers", r.Routes())
})
@ -169,7 +169,7 @@ func (s *Server) createHandler() {
// display all routes [DEBUG]
chi.Walk(r, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
fmt.Println(method, " ---> ", route)
//fmt.Println(method, " ---> ", route)
return nil
})