diff --git a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json
index 1969c9ab4..8e1989de0 100644
--- a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json
+++ b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json
@@ -598,8 +598,7 @@
"certificateFile": {
"title": "label.certificate-file",
"description": "tooltip.certificate-file",
- "type": "string",
- "default": ""
+ "type": "string"
}
},
"anyOf": [
diff --git a/ui/src/app/metadata/metadata.module.ts b/ui/src/app/metadata/metadata.module.ts
index 946c89423..fd00a2274 100644
--- a/ui/src/app/metadata/metadata.module.ts
+++ b/ui/src/app/metadata/metadata.module.ts
@@ -9,7 +9,8 @@ import { MetadataRoutingModule } from './metadata.routing';
import { ProviderModule } from './provider/provider.module';
import { I18nModule } from '../i18n/i18n.module';
import { CustomWidgetRegistry } from '../schema-form/registry';
-import { WidgetRegistry } from 'ngx-schema-form';
+import { WidgetRegistry, SchemaValidatorFactory } from 'ngx-schema-form';
+import { CustomSchemaValidatorFactory } from '../schema-form/service/schema-validator';
@NgModule({
@@ -23,7 +24,11 @@ import { WidgetRegistry } from 'ngx-schema-form';
I18nModule
],
providers: [
- { provide: WidgetRegistry, useClass: CustomWidgetRegistry }
+ { provide: WidgetRegistry, useClass: CustomWidgetRegistry },
+ {
+ provide: SchemaValidatorFactory,
+ useClass: CustomSchemaValidatorFactory
+ }
],
declarations: [
MetadataPageComponent
diff --git a/ui/src/app/schema-form/model/messages.ts b/ui/src/app/schema-form/model/messages.ts
new file mode 100644
index 000000000..6d6fa61fd
--- /dev/null
+++ b/ui/src/app/schema-form/model/messages.ts
@@ -0,0 +1,3 @@
+export const HARD_CODED_REQUIRED_MSG = RegExp('Missing required property');
+
+export const REQUIRED_MSG_OVERRIDE = 'message.required';
\ No newline at end of file
diff --git a/ui/src/app/schema-form/schema-form.module.ts b/ui/src/app/schema-form/schema-form.module.ts
index 842e903dd..13aba1369 100644
--- a/ui/src/app/schema-form/schema-form.module.ts
+++ b/ui/src/app/schema-form/schema-form.module.ts
@@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
-import { SchemaFormModule } from 'ngx-schema-form';
+import { SchemaFormModule, SchemaValidatorFactory } from 'ngx-schema-form';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
@@ -23,6 +23,7 @@ import { CustomObjectWidget } from './widget/object/object.component';
import { CustomRadioComponent } from './widget/radio/radio.component';
import { InlineObjectListComponent } from './widget/array/inline-obj-list.component';
import { InlineObjectComponent } from './widget/object/inline-obj.component';
+import { CustomSchemaValidatorFactory } from './service/schema-validator';
export const COMPONENTS = [
BooleanRadioComponent,
diff --git a/ui/src/app/schema-form/service/schema-validator.ts b/ui/src/app/schema-form/service/schema-validator.ts
new file mode 100644
index 000000000..075a78ec9
--- /dev/null
+++ b/ui/src/app/schema-form/service/schema-validator.ts
@@ -0,0 +1,19 @@
+import * as ZSchema from 'z-schema';
+import { ZSchemaValidatorFactory } from 'ngx-schema-form';
+
+export class CustomSchemaValidatorFactory extends ZSchemaValidatorFactory {
+
+ protected zschema;
+
+ constructor() {
+ super();
+ this.createSchemaValidator();
+ }
+
+ private createSchemaValidator() {
+ this.zschema = new ZSchema({
+ breakOnFirstError: false
+ });
+ }
+}
+
diff --git a/ui/src/app/schema-form/service/schema.service.ts b/ui/src/app/schema-form/service/schema.service.ts
index 9cd7346de..617769de6 100644
--- a/ui/src/app/schema-form/service/schema.service.ts
+++ b/ui/src/app/schema-form/service/schema.service.ts
@@ -31,7 +31,8 @@ export class SchemaService {
Object
.keys(condition.properties)
.some(
- key => values.hasOwnProperty(key) ? condition.properties[key].enum[0] === values[key] : false
+ key => values.hasOwnProperty(key) && condition.properties[key].enum ?
+ condition.properties[key].enum[0] === values[key] : false
)
);
currentConditions.forEach(el => {
diff --git a/ui/src/app/schema-form/widget/select/select.component.html b/ui/src/app/schema-form/widget/select/select.component.html
index e66a83350..a7d1dd2ff 100644
--- a/ui/src/app/schema-form/widget/select/select.component.html
+++ b/ui/src/app/schema-form/widget/select/select.component.html
@@ -42,7 +42,7 @@
,
- error
+ error
diff --git a/ui/src/app/schema-form/widget/select/select.component.ts b/ui/src/app/schema-form/widget/select/select.component.ts
index d504f0384..ecd50a75b 100644
--- a/ui/src/app/schema-form/widget/select/select.component.ts
+++ b/ui/src/app/schema-form/widget/select/select.component.ts
@@ -4,6 +4,7 @@ import { SelectWidget } from 'ngx-schema-form';
import { SchemaService } from '../../service/schema.service';
import { map, shareReplay, startWith } from 'rxjs/operators';
import { Subscription } from 'rxjs';
+import { HARD_CODED_REQUIRED_MSG } from '../../model/messages';
@Component({
selector: 'select-component',
@@ -43,7 +44,7 @@ export class CustomSelectComponent extends SelectWidget implements AfterViewInit
}
this.errorSub = this.control.valueChanges.pipe(startWith(this.control.value)).subscribe(v => {
- if (!v && this.required && !this.errorMessages.some(msg => !!msg.toLowerCase().match('required').length)) {
+ if (!v && this.required && !this.errorMessages.some(msg => HARD_CODED_REQUIRED_MSG.test(msg))) {
this.errorMessages.push('message.required');
}
});
@@ -56,4 +57,8 @@ export class CustomSelectComponent extends SelectWidget implements AfterViewInit
get required(): boolean {
return this.widgetService.isRequired(this.formProperty);
}
+
+ getError(error: string): string {
+ return HARD_CODED_REQUIRED_MSG.test(error) ? 'message.required' : error;
+ }
}
diff --git a/ui/src/app/schema-form/widget/string/string.component.html b/ui/src/app/schema-form/widget/string/string.component.html
index 26964a59f..4986846ff 100644
--- a/ui/src/app/schema-form/widget/string/string.component.html
+++ b/ui/src/app/schema-form/widget/string/string.component.html
@@ -13,6 +13,7 @@
validate="true"
[attr.readonly]="schema.readOnly?true:null"
class="text-widget.id textline-widget form-control"
+ [class.is-invalid]="control.touched && !control.value && errorMessages.length"
[attr.type]="this.getInputType()"
[attr.id]="id"
[formControl]="control"
@@ -25,7 +26,7 @@
,
- error
+ error
{
- if (!v && this.required && !this.errorMessages.some(msg => !!msg.toLowerCase().match('required').length)) {
- this.errorMessages.push('message.required');
+ let listener = this.formProperty.parent ? this.formProperty.parent : this.control;
+ this.errorSub = listener.valueChanges.pipe(startWith(listener.value)).subscribe(v => {
+ if (!this.control.value
+ && this.required
+ && !this.errorMessages.some(msg => HARD_CODED_REQUIRED_MSG.test(msg))
+ && this.errorMessages.indexOf(REQUIRED_MSG_OVERRIDE) < 0) {
+ this.errorMessages.push(REQUIRED_MSG_OVERRIDE);
+ }
+ if (!this.required) {
+ this.errorMessages = this.errorMessages.filter(e => e !== REQUIRED_MSG_OVERRIDE);
}
});
}
@@ -38,4 +47,8 @@ export class CustomStringComponent extends StringWidget implements AfterViewInit
return req;
}
+
+ getError(error: string): string {
+ return HARD_CODED_REQUIRED_MSG.test(error) ? REQUIRED_MSG_OVERRIDE : error;
+ }
}
diff --git a/ui/src/app/schema-form/widget/textarea/textarea.component.html b/ui/src/app/schema-form/widget/textarea/textarea.component.html
index 3705c4009..d47d6007d 100644
--- a/ui/src/app/schema-form/widget/textarea/textarea.component.html
+++ b/ui/src/app/schema-form/widget/textarea/textarea.component.html
@@ -21,7 +21,7 @@
,
- error
+ error
diff --git a/ui/src/app/schema-form/widget/textarea/textarea.component.ts b/ui/src/app/schema-form/widget/textarea/textarea.component.ts
index 81d940bde..fdbce4b35 100644
--- a/ui/src/app/schema-form/widget/textarea/textarea.component.ts
+++ b/ui/src/app/schema-form/widget/textarea/textarea.component.ts
@@ -4,6 +4,7 @@ import { TextAreaWidget } from 'ngx-schema-form';
import { SchemaService } from '../../service/schema.service';
import { Subscription } from 'rxjs';
import { startWith } from 'rxjs/operators';
+import { HARD_CODED_REQUIRED_MSG } from '../../model/messages';
@Component({
selector: 'textarea-component',
@@ -22,7 +23,7 @@ export class CustomTextAreaComponent extends TextAreaWidget implements AfterView
ngAfterViewInit(): void {
super.ngAfterViewInit();
this.errorSub = this.control.valueChanges.pipe(startWith(this.control.value)).subscribe(v => {
- if (!v && this.required && !this.errorMessages.some(msg => !!msg.toLowerCase().match('required').length)) {
+ if (!v && this.required && !this.errorMessages.some(msg => HARD_CODED_REQUIRED_MSG.test(msg))) {
this.errorMessages.push('message.required');
}
});
@@ -35,4 +36,8 @@ export class CustomTextAreaComponent extends TextAreaWidget implements AfterView
get required(): boolean {
return this.widgetService.isRequired(this.formProperty);
}
+
+ getError(error: string): string {
+ return error.match('required').length ? 'message.required' : error;
+ }
}
diff --git a/ui/src/assets/schema/provider/filebacked-http-filters.schema.json b/ui/src/assets/schema/provider/filebacked-http-filters.schema.json
index 4bb2f399c..820063bfa 100644
--- a/ui/src/assets/schema/provider/filebacked-http-filters.schema.json
+++ b/ui/src/assets/schema/provider/filebacked-http-filters.schema.json
@@ -50,8 +50,7 @@
"title": "label.certificate-file",
"description": "tooltip.certificate-file",
"type": "string",
- "widget": "textline",
- "default": ""
+ "widget": "textline"
}
},
"anyOf": [
@@ -59,6 +58,10 @@
"properties": {
"requireSignedRoot": {
"enum": [ true ]
+ },
+ "certificateFile": {
+ "minLength": 1,
+ "type": "string"
}
},
"required": [