Skip to content

Commit

Permalink
Fixed #43 - Now map fields in plugin configuration are treated as JSO…
Browse files Browse the repository at this point in the history
…N objects correctly.
  • Loading branch information
ligreman committed Jun 23, 2023
1 parent a4e05e3 commit caa1603
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 73 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "king",
"version": "3.0.9",
"version": "3.1.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
Expand Down
135 changes: 79 additions & 56 deletions src/app/components/dialog-new-plugin/dialog-new-plugin.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import {COMMA, ENTER} from '@angular/cdk/keycodes';
import {Component, Inject, OnDestroy, OnInit} from '@angular/core';
import {FormBuilder, Validators} from '@angular/forms';
import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete';
import {MatChipInputEvent} from '@angular/material/chips';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {TranslateService} from '@ngx-translate/core';
import {
get as _get,
isArray as _isArray,
Expand All @@ -14,13 +14,13 @@ import {
sortedUniq as _sortedUniq,
toInteger as _toInteger
} from 'lodash';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe';
import { forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiService } from '../../services/api.service';
import { ToastService } from '../../services/toast.service';
import { CustomValidators } from '../../shared/custom-validators';
import { Utils } from '../../shared/utils';
import {AutoUnsubscribe} from 'ngx-auto-unsubscribe';
import {forkJoin} from 'rxjs';
import {map} from 'rxjs/operators';
import {ApiService} from '../../services/api.service';
import {ToastService} from '../../services/toast.service';
import {CustomValidators} from '../../shared/custom-validators';
import {Utils} from '../../shared/utils';

@AutoUnsubscribe()
@Component({
Expand All @@ -43,6 +43,7 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
pluginForm = [];
arrayOfStrings = [];
arrayOfRecords = [];
mapFields = [];
fieldTypes = {};
readonly separatorKeysCodes: number[] = [ENTER, COMMA];

Expand All @@ -65,22 +66,35 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
});

constructor(@Inject(MAT_DIALOG_DATA) public pluginData: any, private fb: FormBuilder, private api: ApiService, private toast: ToastService,
public dialogRef: MatDialogRef<DialogNewPluginComponent>, private translate: TranslateService) { }
public dialogRef: MatDialogRef<DialogNewPluginComponent>, private translate: TranslateService) {
}

/*
Getters de campos del formulario
*/
get nameField() { return this.form.get('name'); }
get nameField() {
return this.form.get('name');
}

get instanceNameField() { return this.form.get('instance_name'); }
get instanceNameField() {
return this.form.get('instance_name');
}

get serviceField() { return this.form.get('service.id'); }
get serviceField() {
return this.form.get('service.id');
}

get routeField() { return this.form.get('route.id'); }
get routeField() {
return this.form.get('route.id');
}

get consumerField() { return this.form.get('consumer.id'); }
get consumerField() {
return this.form.get('consumer.id');
}

get protocolsField() { return this.form.get('protocols'); }
get protocolsField() {
return this.form.get('protocols');
}

ngOnInit(): void {
// Recojo del api los datos
Expand All @@ -91,7 +105,12 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
this.api.getPluginsEnabled()
]).pipe(map(([services, routes, consumers, plugins]) => {
// forkJoin returns an array of values, here we map those values to an object
return {services: services['data'], routes: routes['data'], consumers: consumers['data'], plugins: plugins['enabled_plugins']};
return {
services: services['data'],
routes: routes['data'],
consumers: consumers['data'],
plugins: plugins['enabled_plugins']
};
})).subscribe((value) => {
value.services = _orderBy(value.services, ['name'], ['asc']);
value.routes = _orderBy(value.routes, ['name'], ['asc']);
Expand Down Expand Up @@ -157,6 +176,11 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
*/
onSubmit() {
const result = this.prepareDataForKong(this.form.value);

if (!result) {
return;
}

if (!this.editMode) {
// llamo al API
this.api.postNewPlugin(result)
Expand Down Expand Up @@ -237,18 +261,11 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
// Cojo el array de valores
let fValue = _get(plugin, field, null);

// Los maps
const confField = field.replace('config.', '');
if (fValue !== null && this.fieldTypes[confField] === 'map') {
fValue = this.flattenObj(fValue);
}

if (fValue !== null && _isArray(fValue)) {
_set(plugin, field, fValue.join('\n'));
} else if (fValue !== null && _isObject(fValue)) {
const keys = Object.getOwnPropertyNames(fValue);
let converted = [];

keys.forEach(key => {
converted.push(key + ':' + fValue[key]);
});
Expand All @@ -259,6 +276,24 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
}
});

// Campos map
this.mapFields.forEach(field => {
// Cojo el array de valores
let fValue = _get(plugin, field, null);

const confField = field.replace('config.', '');
if (fValue !== null && this.fieldTypes[confField] === 'map') {
try {
fValue = JSON.stringify(fValue);
_set(plugin, field, fValue);
} catch (e) {
this.toast.error_general(this.translate.instant('plugin.error.readMap', {field: field}), {disableTimeOut: true});
}
} else {
_set(plugin, field, null);
}
});

return plugin;
}

Expand Down Expand Up @@ -309,32 +344,6 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
output = values.map((el) => {
return _toInteger(el);
});
}
// Si es map, transformo a objeto
else if (this.fieldTypes[confField] === 'map') {
output = {};
values.forEach(val => {
let [a, b] = val.split(':');
if (a !== undefined && b !== undefined) {
// Intento pasar a número si lo es
let newB = parseInt(b);
if (!isNaN(newB)) {
b = newB;
}

// Sub-objects
const sub = a.split('.');
if (sub.length > 1) {
const first = sub.shift();
if (!output[first]) {
output[first] = {};
}
output[first][sub.join('.')] = b;
} else {
output[a] = b;
}
}
});
} else {
output = values;
}
Expand Down Expand Up @@ -369,6 +378,19 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
_set(body, field, output);
});

// campos map
this.mapFields.forEach(field => {
const fValue = _get(body, field);
let output = "";
try {
output = JSON.parse(fValue);
_set(body, field, output);
} catch (e) {
this.toast.error_general(this.translate.instant('plugin.error.map', {field: field}), {disableTimeOut: true});
return null;
}
});

return body;
}

Expand All @@ -382,6 +404,7 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
const pluginSchemaFields = this.parseSchema(value);
this.arrayOfStrings = [];
this.arrayOfRecords = [];
this.mapFields = [];
this.fieldTypes = {};
this.form.get('config').reset();

Expand Down Expand Up @@ -523,7 +546,7 @@ export class DialogNewPluginComponent implements OnInit, OnDestroy {
validators.push(Validators.required);
}

this.arrayOfStrings.push(currentGroup + '.' + field);
this.mapFields.push(currentGroup + '.' + field);
this.fieldTypes[field] = value.type;

dConfig.addControl(field, this.fb.control(value.default, validators));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@

<mat-form-field *ngIf="field.type === 'string'" appearance="outline" color="accent" fxFlex="100">
<mat-label>{{formatText(field.field) | titlecase}}</mat-label>
<input [formControl]="getField(field.field)" [required]="field.required" matInput type="{{ isPassword(field.field) ? 'password' : 'text' }}"/>
<input [formControl]="getField(field.field)" [required]="field.required" matInput
type="{{ isPassword(field.field) ? 'password' : 'text' }}"/>
</mat-form-field>

<mat-form-field *ngIf="field.type === 'number'" appearance="outline" color="accent" fxFlex="100">
<mat-label>{{formatText(field.field) | titlecase}}</mat-label>
<input [formControl]="getField(field.field)" [required]="field.required" matInput type="number"/>
<mat-hint *ngIf="field.hint">
<span *ngIf="field.hint[0]==null && field.hint[1]!==null">{{ 'plugin.dialog.max' | translate:{max: field.hint[1]} }}. </span>
<span *ngIf="field.hint[0]!==null && field.hint[1]==null">{{ 'plugin.dialog.min' | translate:{min: field.hint[0]} }}. </span>
<span
*ngIf="field.hint[0]==null && field.hint[1]!==null">{{ 'plugin.dialog.max' | translate:{max: field.hint[1]} }}
. </span>
<span
*ngIf="field.hint[0]!==null && field.hint[1]==null">{{ 'plugin.dialog.min' | translate:{min: field.hint[0]} }}
. </span>
<span *ngIf="field.hint[0]!==null && field.hint[1]!==null">
{{ 'plugin.dialog.min_max' | translate:{min: field.hint[0], max: field.hint[1]} }}</span>
</mat-hint>
Expand All @@ -34,14 +39,16 @@
</mat-select>
</mat-form-field>

<div *ngIf="field.type === 'boolean'" class="margin-bottom-20-i" fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox [formControl]="getField(field.field)">{{formatText(field.field) | titlecase}}</mat-checkbox>
<div *ngIf="field.type === 'boolean'" class="margin-bottom-20-i" fxFlex="100" fxLayout="row"
fxLayoutAlign="start center">
<mat-checkbox
[formControl]="getField(field.field)">{{formatText(field.field) | titlecase}}</mat-checkbox>
</div>

<ng-container *ngIf="field.type === 'map'">
<mat-form-field appearance="outline" color="accent" fxFlex="100">
<mat-label>{{formatText(field.field) | titlecase}}</mat-label>
<textarea [formControl]="getField(field.field)" [required]="field.required" matInput></textarea>
<input [formControl]="getField(field.field)" [required]="field.required" matInput/>
<mat-hint>{{ 'plugin.dialog.map' | translate }}</mat-hint>
</mat-form-field>
</ng-container>
Expand All @@ -55,7 +62,8 @@

<ng-container *ngIf="field.type === 'record'">
<!-- Recursivo, concatenando el grupo -->
<app-plugin-form-fields [currentGroup]="field.field" [fields]="field.child_fields" [group]="group + '.' + field.field"
<app-plugin-form-fields [currentGroup]="field.field" [fields]="field.child_fields"
[group]="group + '.' + field.field"
[parentForm]="parentForm"></app-plugin-form-fields>
</ng-container>

Expand All @@ -68,7 +76,8 @@
<div *ngFor="let childF of field.child_fields">
<mat-form-field *ngIf="childF.type === 'string'" appearance="outline" color="accent">
<mat-label>{{formatText(childF.name) | titlecase}}</mat-label>
<mat-select [(ngModel)]="arrayRecordsFields[field.field][childF.name]" [ngModelOptions]="{standalone: true}"
<mat-select [(ngModel)]="arrayRecordsFields[field.field][childF.name]"
[ngModelOptions]="{standalone: true}"
[required]="childF.required">
<mat-option *ngIf="!childF.required" value=""></mat-option>
<mat-option *ngFor="let opt of childF.one_of" value="{{opt}}">{{opt}}</mat-option>
Expand All @@ -77,39 +86,45 @@

<mat-form-field *ngIf="childF.type === 'number'" appearance="outline" color="accent">
<mat-label>{{formatText(childF.name) | titlecase}}</mat-label>
<input [(ngModel)]="arrayRecordsFields[field.field][childF.name]" [ngModelOptions]="{standalone: true}"
<input [(ngModel)]="arrayRecordsFields[field.field][childF.name]"
[ngModelOptions]="{standalone: true}"
[required]="childF.required" matInput type="number"/>
</mat-form-field>

<mat-form-field *ngIf="childF.type === 'boolean'" appearance="outline" color="accent">
<mat-checkbox [(ngModel)]="arrayRecordsFields[field.field][childF.name]" [ngModelOptions]="{standalone: true}"
<mat-checkbox [(ngModel)]="arrayRecordsFields[field.field][childF.name]"
[ngModelOptions]="{standalone: true}"
[required]="childF.required">
{{formatText(childF.name) | titlecase}}</mat-checkbox>
</mat-form-field>

<mat-form-field *ngIf="childF.type === 'array'" appearance="outline" color="accent">
<mat-label>{{formatText(childF.name) | titlecase}}</mat-label>
<input [(ngModel)]="arrayRecordsFields[field.field][childF.name]" [ngModelOptions]="{standalone: true}"
<input [(ngModel)]="arrayRecordsFields[field.field][childF.name]"
[ngModelOptions]="{standalone: true}"
[required]="childF.required" matInput type="text"/>
<mat-hint>{{'plugin.dialog.comma_separated_hint' | translate}}</mat-hint>
</mat-form-field>
</div>
</div>
<div fxLayout="column">
<div fxLayout="row">
<button (click)="addToArrayRecord(field.field)" color="accent" fxFlex="75" mat-button matSuffix>
<button (click)="addToArrayRecord(field.field)" color="accent" fxFlex="75" mat-button
matSuffix>
{{ 'plugin.dialog.add_to_list' | translate }}
<mat-icon>add</mat-icon>
</button>
<button (click)="cleanArrayRecords(field.field)" color="warn" fxFlex="25" mat-button matSuffix>
<button (click)="cleanArrayRecords(field.field)" color="warn" fxFlex="25" mat-button
matSuffix>
{{ 'plugin.dialog.clean_list' | translate }}
<mat-icon>clear</mat-icon>
</button>
</div>
<!-- textarea de resultado (un json string por línea)-->
<mat-form-field appearance="outline" color="accent" fxFlex="100">
<mat-label>{{formatText(field.field) | titlecase}}</mat-label>
<textarea [(ngModel)]="arrayRecordsValuesString[field.field]" [ngModelOptions]="{standalone: true}"
<textarea [(ngModel)]="arrayRecordsValuesString[field.field]"
[ngModelOptions]="{standalone: true}"
id="array-records-{{getIdArray(field.field)}}" matInput rows="5"></textarea>
</mat-form-field>
</div>
Expand Down
6 changes: 5 additions & 1 deletion src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,14 @@
"min": "Number greater than {{min}}",
"max": "Number lower than {{max}}",
"min_max": "Number between {{min}} and {{max}}",
"map": "Format to use -> field.subfield:valor",
"map": "JSON format. Example -> {\"request.headers\":\"return nil\"}",
"add_to_list": "Add to list",
"clean_list": "Empty list",
"comma_separated_hint": "Comma separated values"
},
"error": {
"map": "Field {{field}} must be a valid JSON",
"readMap": "Error reading the field {{field}}: it is not a valid JSON"
}
},
"target": {
Expand Down
6 changes: 5 additions & 1 deletion src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,14 @@
"min": "Número mayor de {{min}}",
"max": "Número menor de {{max}}",
"min_max": "Número entre {{min}} y {{max}}",
"map": "Formato a usar -> campo.subcampo:valor",
"map": "Formato JSON. Ejemplo -> {\"request.headers\":\"return nil\"}",
"add_to_list": "Añadir a la lista",
"clean_list": "Vaciar la lista",
"comma_separated_hint": "Valores separados por comas"
},
"error": {
"map": "El campo {{field}} debe ser un JSON válido",
"readMap": "Error leyendo el campo {{field}}: no es un JSON válido"
}
},
"target": {
Expand Down

0 comments on commit caa1603

Please sign in to comment.