Skip to content

Commit

Permalink
Merged in bugfix/SHIBUI-953 (pull request #246)
Browse files Browse the repository at this point in the history
SHIBUI-953 Fixed issue with validation on organizations

Approved-by: Jonathan Johnson <jj@scaldingspoon.com>
Approved-by: Ryan Mathis <rmathis@unicon.net>
  • Loading branch information
rmathis committed Nov 30, 2018
2 parents 757217a + 3248d54 commit 46526ab
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 21 deletions.
5 changes: 5 additions & 0 deletions backend/src/main/resources/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,11 @@ message.uri-valid-format=URI must be valid format.
message.id-unique=ID must be unique.
message.array-items-must-be-unique=Items in list must be unique.

message.org-name-required=Organization Name is required.
message.org-displayName-required=Organization Name is required.
message.org-url-required=Organization Name is required.
message.org-incomplete=These three fields must all be entered if any single field has a value.

message.conflict=Conflict
message.data-version-contention=Data Version Contention
message.contention-new-version=A newer version of this metadata source has been saved. Below are a list of changes. You can use your changes or their changes.
Expand Down
30 changes: 18 additions & 12 deletions backend/src/main/resources/metadata-sources-ui-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,24 @@
}
},
"dependencies": {
"name": [
"displayName",
"url"
],
"displayName": [
"name",
"url"
],
"url": [
"name",
"displayName"
]
"name": {
"required": [
"displayName",
"url"
]
},
"displayName": {
"required": [
"name",
"url"
]
},
"url": {
"required": [
"name",
"displayName"
]
}
}
},
"contacts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ describe('Metadata Source Base class', () => {
it('should return a list of validators for the ngx-schema-form', () => {
expect(Object.keys(getValidators([]))).toEqual([
'/',
'/entityId'
'/entityId',
'/organization/name',
'/organization/displayName',
'/organization/url'
]);
});
});
Expand Down
19 changes: 17 additions & 2 deletions ui/src/app/metadata/domain/model/wizards/metadata-source-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ export class MetadataSourceBase implements Wizard<MetadataResolver> {
}

getValidators(entityIdList: string[]): { [key: string]: any } {
const checkOrg = (value, property, form) => {
const org = property.parent;
const orgValue = org.value || {};
const err = Object.keys(orgValue) && !value ? {
code: 'ORG_INCOMPLETE',
path: `#${property.path}`,
message: `message.org-incomplete`,
params: [value]
} : null;
return err;
};
const validators = {
'/': (value, property, form_current) => {
let errors;
Expand All @@ -68,12 +79,13 @@ export class MetadataSourceBase implements Wizard<MetadataResolver> {
const item = value[key];
const validatorKey = `/${key}`;
const validator = validators.hasOwnProperty(validatorKey) ? validators[validatorKey] : null;
const error = validator ? validator(item, { path: `/${key}` }, form_current) : null;
const error = validator ? validator(item, form_current.getProperty(key), form_current) : null;
if (error) {
errors = errors || [];
errors.push(error);
}
});
console.log(errors, form_current);
return errors;
},
'/entityId': (value, property, form) => {
Expand All @@ -84,7 +96,10 @@ export class MetadataSourceBase implements Wizard<MetadataResolver> {
params: [value]
} : null;
return err;
}
},
'/organization/name': checkOrg,
'/organization/displayName': checkOrg,
'/organization/url': checkOrg
};
return validators;
}
Expand Down
92 changes: 89 additions & 3 deletions ui/src/app/schema-form/service/schema.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TestBed, async, inject } from '@angular/core/testing';
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
import { HttpClientModule, HttpRequest } from '@angular/common/http';
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { HttpClientModule } from '@angular/common/http';
import { SchemaService } from './schema.service';

describe(`Schema Service`, () => {
Expand Down Expand Up @@ -171,5 +171,91 @@ describe(`Schema Service`, () => {
})).toBe(true);
})
);

it(`should return true if dependency is active`,
inject([SchemaService], (service: SchemaService) => {
expect(service.isRequired({
parent: {
schema: {
properties: {
foo: { type: 'string' },
bar: { type: 'string' },
baz: { type: 'string' }
},
dependencies: {
foo: { required: ['bar', 'baz'] },
bar: { required: ['foo', 'baz'] },
baz: { required: ['foo', 'bar'] }
}
},
value: {
foo: 'abcdef'
}
},
path: '/bar'
})).toBe(true);
})
);

it(`should return true if the property has an active dependency`,
inject([SchemaService], (service: SchemaService) => {
expect(service.isRequired({
parent: {
schema: {
properties: {
foo: { type: 'string' },
bar: { type: 'string' },
baz: { type: 'string' }
},
dependencies: {
foo: { required: ['bar', 'baz'] },
bar: { required: ['foo', 'baz'] },
baz: { required: ['foo', 'bar'] }
}
},
value: {
foo: 'abc',
bar: '123'
}
},
path: '/foo'
})).toBe(true);
})
);

it(`should return false if no dependencies are defined`,
inject([SchemaService], (service: SchemaService) => {
expect(service.isRequired({
parent: {
schema: {
properties: {
foo: { type: 'string' },
bar: { type: 'string' },
baz: { type: 'string' }
}
},
value: {
foo: true,
baz: true
}
},
path: '/bar'
})).toBe(false);
})
);
});

describe('getRequiredDependencies method', () => {
it('should return the provided result if an array', inject([SchemaService], (service: SchemaService) => {
expect(service.getRequiredDependencies(['foo', 'bar'])).toEqual(['foo', 'bar']);
}));

it('should return the content of the required attribute if provided', inject([SchemaService], (service: SchemaService) => {
expect(service.getRequiredDependencies({required: ['foo', 'bar'] })).toEqual(['foo', 'bar']);
}));

it('should return an empty array if not provided with required property', inject([SchemaService], (service: SchemaService) => {
expect(service.getRequiredDependencies({ foo: 'bar' })).toEqual([]);
}));
});
});
23 changes: 23 additions & 0 deletions ui/src/app/schema-form/service/schema.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class SchemaService {
if (!formProperty || !formProperty.parent) {
return false;
}

let requiredFields = formProperty.parent.schema.required || [];
let fieldPath = formProperty.path;
let controlName = fieldPath.substr(fieldPath.lastIndexOf('/') + 1);
Expand All @@ -38,6 +39,28 @@ export class SchemaService {
required = !required ? requiredFields.indexOf(controlName) > -1 : required;
});
}

if (!required && formProperty.parent instanceof Object) {
const parent = formProperty.parent;
const dependencies = parent.schema.dependencies;
if (dependencies) {
const isDependencyOf = Object.keys(dependencies).filter(d => {
let dep = dependencies[d];
return this.getRequiredDependencies(dep);
});
const hasActiveDependencies = dependencies.hasOwnProperty(controlName) &&
this.getRequiredDependencies(dependencies[controlName]).filter(
d => parent.value.hasOwnProperty(d)
);
const isRequired = isDependencyOf.some(d => parent.value.hasOwnProperty(d) && !!parent.value[d]);
required = isRequired || !!hasActiveDependencies.length;
}
}

return required;
}

getRequiredDependencies(dep: any): string[] {
return (dep instanceof Array) ? dep : dep.hasOwnProperty('required') ? dep.required : [];
}
}
2 changes: 1 addition & 1 deletion ui/src/app/schema-form/widget/array/array.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class CustomArrayComponent extends ArrayWidget implements AfterViewInit,

ngAfterViewInit(): void {
this.errors$ = this.formProperty.errorsChanges.pipe(
map(errors => errors ? errors.reduce((coll, err) => {
map(errors => errors ? errors.filter(err => err.code !== 'UNRESOLVABLE_REFERENCE').reduce((coll, err) => {
coll[err.code] = err;
return coll;
}, {}) : {}),
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/schema-form/widget/object/object.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import { ObjectWidget } from 'ngx-schema-form';
selector: 'custom-object',
templateUrl: `./object.component.html`
})
export class CustomObjectWidget extends ObjectWidget { }
export class CustomObjectWidget extends ObjectWidget {}
4 changes: 3 additions & 1 deletion ui/src/app/schema-form/widget/string/string.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<div class="widget form-group" [class.d-none]="this.getInputType() === 'hidden'">
<label [attr.for]="id" class="d-flex justify-content-between control-label">
<span>
<translate-i18n [key]="schema.title">{{ schema.title }}</translate-i18n> <i class="fa fa-asterisk text-danger" aria-hidden="true" *ngIf="required"></i></span>
<translate-i18n [key]="schema.title">{{ schema.title }}</translate-i18n>&nbsp;
<i class="fa fa-asterisk text-danger" aria-hidden="true" *ngIf="required"></i>
</span>
<span *ngIf="schema.description">
<info-icon [description]="schema.description | translate"></info-icon>
</span>
Expand Down

0 comments on commit 46526ab

Please sign in to comment.