use of cern.modesti.schema.category.Category in project modesti by jlsalmon.
the class SchemaInitialiser method loadSchemas.
/**
* Load all schemas inside plugins.
*
* @return the list of schemas that were found on the classpath
* @throws IOException
* @throws URISyntaxException
*/
private List<SchemaImpl> loadSchemas() throws IOException, URISyntaxException {
List<SchemaImpl> schemas = new ArrayList<>();
Map<String, List<SchemaImpl>> pluginSchemas = loadPluginSchemas();
Map<String, List<CategoryImpl>> pluginCategories = loadPluginCategories();
Map<String, List<DatasourceImpl>> pluginDatasources = loadPluginDatasources();
for (Map.Entry<String, List<SchemaImpl>> entry : pluginSchemas.entrySet()) {
for (SchemaImpl schema : entry.getValue()) {
List<Category> categories = new ArrayList<>();
List<Datasource> datasources = new ArrayList<>();
// Skip over abstract schemas
if (schema.isAbstract()) {
continue;
}
// Merge in the parent schema if it is specified
String parent = schema.getParent();
if (parent != null) {
Schema parentSchema = getSchema(pluginSchemas, parent);
if (parentSchema == null) {
throw new IllegalArgumentException(format("Schema %s extends from unknown parent schema %s", schema.getId(), parent));
}
categories.addAll(parentSchema.getCategories());
datasources.addAll(parentSchema.getDatasources());
schema.setIdProperty(parentSchema.getIdProperty());
schema.setSelectableStates(parentSchema.getSelectableStates());
schema.setRowCommentStates(parentSchema.getRowCommentStates());
}
// Attach categories that are specified in the schema
for (Category category : schema.getCategories()) {
boolean categoryFound = false;
for (Map.Entry<String, List<CategoryImpl>> categoryEntry : pluginCategories.entrySet()) {
if (categoryEntry.getValue().contains(category)) {
categories.add(categoryEntry.getValue().get(categoryEntry.getValue().indexOf(category)));
categoryFound = true;
}
}
if (!categoryFound) {
throw new IllegalArgumentException(format("Category %s was not found for schema %s", category.getId(), schema.getId()));
}
// TODO: support referencing another field by name to avoid duplicate field definitions
}
// Attach datasources that are specified in the schema
for (Datasource datasource : schema.getDatasources()) {
boolean datasourceFound = false;
for (Map.Entry<String, List<DatasourceImpl>> datasourceEntry : pluginDatasources.entrySet()) {
if (datasourceEntry.getValue().contains(datasource)) {
datasources.add(datasourceEntry.getValue().get(datasourceEntry.getValue().indexOf(datasource)));
datasourceFound = true;
}
}
if (!datasourceFound) {
throw new IllegalArgumentException(format("Datasource %s was not found for schema %s", datasource.getId(), schema.getId()));
}
}
// Process any overrides to the core categories
for (Category override : schema.getOverrides()) {
if (categories.contains(override)) {
Category overridden = mergeCategories(categories.get(categories.indexOf(override)), override);
categories.set(categories.indexOf(override), overridden);
}
}
// Process any overrides to the core datasources
for (Datasource override : schema.getDatasourceOverrides()) {
if (datasources.contains(override)) {
Category overridden = mergeCategories(datasources.get(datasources.indexOf(override)), override);
datasources.set(datasources.indexOf(override), new DatasourceImpl((DatasourceImpl) overridden));
}
}
schema.setCategories(categories);
schema.setDatasources(datasources);
// Invoke any post processors
Map<String, SchemaPostProcessor> postProcessors = applicationContext.getBeansOfType(SchemaPostProcessor.class);
for (SchemaPostProcessor postProcessor : postProcessors.values()) {
schema = (SchemaImpl) postProcessor.postProcess(schema);
}
schemas.add(schema);
}
}
String loadedPlugIns = schemas.stream().map(p -> p.getId()).collect(Collectors.joining(", "));
log.trace("loaded {} schemas [{}]", schemas.size(), loadedPlugIns);
return schemas;
}
use of cern.modesti.schema.category.Category in project modesti by jlsalmon.
the class CoreValidationService method validatePoints.
private boolean validatePoints(Request request, List<Category> categories) {
boolean valid = true;
for (Point point : request.getNonEmptyPoints()) {
for (Category category : categories) {
for (Field field : category.getFields()) {
Object value = point.getValueByPropertyName(field.getPropertyName());
// Check for invalid fields
if (!isValidValue(value, point, field)) {
point.setValid(false);
valid = false;
point.addErrorMessage(category.getId(), field.getId(), "Value '" + value + "' is not a legal option for field '" + field.getName() + "'. Please select a value from the list.");
}
// Validate unique fields
if (field.getUnique() != null) {
Constraint constraint = new Constraint("unique", Collections.singletonList(field.getId()), null);
if (!Constraints.validate(constraint, request, category)) {
valid = false;
}
}
// Required fields (can be simple boolean or condition list)
boolean required = false;
if (field.getRequired() instanceof Boolean && (Boolean) field.getRequired()) {
required = true;
} else if (field.getRequired() != null) {
required = Conditionals.evaluate(field.getRequired(), point, request);
}
if (required) {
if (value == null || value.equals("")) {
point.setValid(false);
valid = false;
point.addErrorMessage(category.getId(), field.getId(), "'" + field.getName() + "' is mandatory");
}
}
// Min length
if (field.getMinLength() != null) {
if (value != null && value.toString().length() < field.getMinLength()) {
point.setValid(false);
valid = false;
point.addErrorMessage(category.getId(), field.getId(), "'" + field.getName() + "' must be at least " + field.getMinLength() + " characters in length");
}
}
// Max length
if (field.getMaxLength() != null) {
if (value != null && value.toString().length() > field.getMaxLength()) {
point.setValid(false);
valid = false;
point.addErrorMessage(category.getId(), field.getId(), "'" + field.getName() + "' must not exceed " + field.getMaxLength() + " characters in length");
}
}
// Numeric fields
if (field.getType().equals("numeric")) {
if (value != null && !value.toString().isEmpty() && !NumberUtils.isNumber(value.toString())) {
point.setValid(false);
valid = false;
point.addErrorMessage(category.getId(), field.getId(), "Value for '" + field.getName() + "' must be numeric");
}
}
}
}
}
return valid;
}
use of cern.modesti.schema.category.Category in project modesti by jlsalmon.
the class CoreValidationService method validateRequest.
public boolean validateRequest(Request request) {
try {
if (RequestType.DELETE.equals(request.getType())) {
// Delete requests should not be validated
return true;
}
boolean valid = true;
Schema schema = schemaRepository.findOne(request.getDomain());
// Reset all points and clear any error messages.
for (Point point : request.getPoints()) {
point.setValid(true);
point.setErrors(new ArrayList<>());
}
if (environment.getProperty("modesti.disableValidator", Boolean.class, false) || request.isSkipCoreValidation()) {
log.info("core validations disabled");
} else {
// Concatenate all categories and datasources
List<Category> categories = new ArrayList<>(schema.getCategories());
categories.addAll(schema.getDatasources());
// Validate the mutually exclusive column group specifications.
if (!validateMutualExclusions(request, categories)) {
valid = false;
}
// column groups and mutually inclusive fields.
if (!validateConstraints(request, categories)) {
valid = false;
}
// values, min/max length, valid values etc.
if (!validatePoints(request, categories)) {
valid = false;
}
}
request.setValid(valid);
requestService.save(request);
if (!valid) {
log.info(format("request #%s failed validation, not invoking custom validator", request.getRequestId()));
return false;
}
log.info(format("request #%s is valid, invoking custom validator", request.getRequestId()));
RequestProvider plugin = requestProviderRegistry.getPluginFor(request);
RequestValidator validator = getPluginRequestValidator(plugin.getMetadata().getId());
if (validator == null) {
log.info(format("custom validator not provided for request #%s", request.getRequestId()));
return true;
}
valid = validator.validateRequest(request, schema);
request.setValid(valid);
requestService.save(request);
return valid;
} catch (RuntimeException e) {
request.setValid(false);
requestService.save(request);
log.info(format("Unexpected error during validation phase for request #%s '%s'", request.getRequestId(), e.toString()), e);
return false;
}
}
use of cern.modesti.schema.category.Category in project modesti by jlsalmon.
the class CoreValidationService method validateMutualExclusions.
private boolean validateMutualExclusions(Request request, List<Category> categories) {
boolean valid = true;
for (Category category : categories) {
if (category.getExcludes() == null) {
continue;
}
for (String exclude : category.getExcludes()) {
// Get the excluded category
for (Category cat : categories) {
if (cat.getId().equals(exclude)) {
Category excludedCategory = cat;
// so, say something like "Fields in the "Alarms" group cannot be used if fields in the "Commands" group have been specified.".
for (Point point : request.getNonEmptyPoints()) {
List<Field> emptyFields = point.getEmptyFields(category.getFields());
// If at least one of the fields of this category are filled, then we must check the excluded category.
if (emptyFields.size() != category.getFields().size()) {
emptyFields = point.getEmptyFields(excludedCategory.getFields());
if (emptyFields.size() != excludedCategory.getFields().size()) {
point.setValid(false);
valid = false;
point.addErrorMessage(category.getId(), "", "Fields in the '" + category.getName() + "' group cannot be used if fields in the '" + excludedCategory.getName() + "' group have been specified.");
}
}
}
}
}
}
}
return valid;
}
Aggregations