From 14e55045885c66215a5f5609fe1d7fcaa8ab77ae Mon Sep 17 00:00:00 2001 From: Lech Rozanski Date: Wed, 29 May 2013 16:09:01 +0200 Subject: [PATCH] MIFOSX-365: Simplify 'Data Tables' functionality --- .../core/data/DataValidatorBuilder.java | 36 +- ...tatableCommandFromApiJsonDeserializer.java | 153 +++--- .../UpdateDatatableCommandHandler.java | 14 +- .../ReadWriteNonCoreDataServiceImpl.java | 451 ++++++++++-------- 4 files changed, 373 insertions(+), 281 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index 35e0db5fd..313377e10 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -16,6 +16,8 @@ import net.fortuna.ical4j.model.property.RRule; import org.apache.commons.lang.StringUtils; import org.springframework.util.ObjectUtils; +import com.google.gson.JsonArray; + public class DataValidatorBuilder { private final List dataValidationErrors; @@ -206,12 +208,13 @@ public class DataValidatorBuilder { if (value != null) { List valuesList = Arrays.asList(values); + String valuesListStr = StringUtils.join(valuesList, ", "); if (!valuesList.contains(this.value)) { StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) .append(".is.not.one.of.expected.enumerations"); StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be one of [ ") - .append(values).append(" ] ").append("."); + .append(valuesListStr).append(" ] ").append("."); ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), parameter, this.value, values); @@ -227,12 +230,13 @@ public class DataValidatorBuilder { if (value != null) { List valuesList = Arrays.asList(values); + String valuesListStr = StringUtils.join(valuesList, ", "); if (valuesList.contains(this.value)) { StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) - .append(".is.one.of.expected.enumerations"); - StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must not be one of [ ") - .append(values).append(" ] ").append("."); + .append(".is.one.of.unwanted.enumerations"); + StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must not be any of [ ") + .append(valuesListStr).append(" ] ").append("."); ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), parameter, this.value, values); @@ -330,6 +334,22 @@ public class DataValidatorBuilder { } return this; } + + public DataValidatorBuilder jsonArrayNotEmpty() { + if (value == null && ignoreNullValue) { return this; } + + JsonArray array = (JsonArray)value; + if (value != null && !array.iterator().hasNext()) { + StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) + .append(".cannot.be.empty"); + StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append( + " cannot be empty. You must select at least one."); + ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), + parameter); + dataValidationErrors.add(error); + } + return this; + } public void expectedArrayButIsNot() { StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) @@ -406,7 +426,7 @@ public class DataValidatorBuilder { } public DataValidatorBuilder cantBeBlankWhenParameterProvidedIs(final String parameterName, final Object parameterValue) { - if (StringUtils.isNotBlank(value.toString())) { return this; } + if (value != null && StringUtils.isNotBlank(value.toString())) { return this; } StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) .append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue); @@ -557,12 +577,12 @@ public class DataValidatorBuilder { public DataValidatorBuilder matchesRegularExpression(final String expression) { if (value == null && ignoreNullValue) { return this; } - - if (!value.toString().matches(expression)) { + + if (value != null && !value.toString().matches(expression)) { StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) .append(".does.not.match.regexp"); StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter) - .append(" must match the provided regular expression ") + .append(" must match the provided regular expression [ ") .append(expression).append(" ] ").append("."); ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java index f1201cb30..db1f74a08 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java @@ -44,7 +44,8 @@ public class DatatableCommandFromApiJsonDeserializer { "name", "newName", "type", "length", "mandatory", "after")); private final Set supportedParametersForDropColumns = new HashSet(Arrays.asList( "name")); - private final Object[] supportedColumnTypes = { "String", "Number", "Decimal", "Date" }; + private final Object[] supportedColumnTypes = { "String", "Number", "Decimal", "Date", "Text" }; + private final Object[] supportedApptableNames = { "m_loan", "m_savings_account", "m_client", "m_group", "m_office" }; private final FromJsonHelper fromApiJsonHelper; @@ -64,40 +65,50 @@ public class DatatableCommandFromApiJsonDeserializer { final JsonElement element = fromApiJsonHelper.parse(json); final String apptableName = fromApiJsonHelper.extractStringNamed("apptableName", element); - final String fkColumnName = apptableName.substring(2) + "_id"; - baseDataValidator.reset().parameter("apptableName").value(apptableName).notNull().notBlank().notExceedingLengthOf(50); + baseDataValidator.reset().parameter("apptableName").value(apptableName).notBlank().notExceedingLengthOf(50) + .isOneOfTheseValues(supportedApptableNames); + final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : ""; final String datatableName = fromApiJsonHelper.extractStringNamed("datatableName", element); - baseDataValidator.reset().parameter("datatableName").value(datatableName).notNull().notBlank().notExceedingLengthOf(50) + baseDataValidator.reset().parameter("datatableName").value(datatableName).notBlank().notExceedingLengthOf(50) .matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); final Boolean multiRow = fromApiJsonHelper.extractBooleanNamed("multiRow", element); - baseDataValidator.reset().parameter("multiRow").value(multiRow).ignoreIfNull().trueOrFalseRequired(multiRow); + baseDataValidator.reset().parameter("multiRow").value(multiRow).ignoreIfNull().notBlank() + .isOneOfTheseValues(true, false); final JsonArray columns = fromApiJsonHelper.extractJsonArrayNamed("columns", element); - baseDataValidator.reset().parameter("columns").value(columns).notNull(); + baseDataValidator.reset().parameter("columns").value(columns).notNull().jsonArrayNotEmpty(); - for (JsonElement column : columns) { - fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForCreateColumns); - - final String name = fromApiJsonHelper.extractStringNamed("name", column); - baseDataValidator.reset().parameter("name").value(name).notNull().notBlank().notExceedingLengthOf(50) - .isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); - - final String type = fromApiJsonHelper.extractStringNamed("type", column); - baseDataValidator.reset().parameter("type").value(type).notNull().notBlank().isOneOfTheseValues(supportedColumnTypes); - - final Integer length = fromApiJsonHelper.extractIntegerSansLocaleNamed("length", column); - if (type.equals("String")) { - baseDataValidator.reset().parameter("length").value(length).notNull().notBlank() - .zeroOrPositiveAmount(); - } else { - baseDataValidator.reset().parameter("length").value(length).ignoreIfNull().notBlank() - .zeroOrPositiveAmount(); - } - - final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column); - baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().trueOrFalseRequired(mandatory); + if (columns != null) { + for (JsonElement column : columns) { + fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForCreateColumns); + + final String name = fromApiJsonHelper.extractStringNamed("name", column); + baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50) + .isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); + + final String type = fromApiJsonHelper.extractStringNamed("type", column); + baseDataValidator.reset().parameter("type").value(type).notBlank().isOneOfTheseValues(supportedColumnTypes); + + final Integer length = fromApiJsonHelper.extractIntegerSansLocaleNamed("length", column); + if (type != null && (type.equals("String") || type.equals("Text"))) { + if (length != null) { + baseDataValidator.reset().parameter("length").value(length).notBlank() + .zeroOrPositiveAmount(); + } else if (!type.equals("Text")) { + baseDataValidator.reset().parameter("length").value(length) + .cantBeBlankWhenParameterProvidedIs("type", type); + } + } else if (type != null && length != null) { + baseDataValidator.reset().parameter("length").value(length) + .mustBeBlankWhenParameterProvidedIs("type", type); + } + + final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column); + baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank() + .isOneOfTheseValues(true, false); + } } throwExceptionIfValidationWarningsExist(dataValidationErrors); @@ -105,6 +116,9 @@ public class DatatableCommandFromApiJsonDeserializer { public void validateForUpdate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + // Because all parameters are optional, a check to see if at least one parameter + // has been specified is necessary in order to avoid JSON requests with no parameters + if (!json.matches("(?s)\\A\\{.*?(\\\".*?\\\"\\s*?:\\s*?)+.*?\\}\\z")) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForUpdate); @@ -114,18 +128,19 @@ public class DatatableCommandFromApiJsonDeserializer { final JsonElement element = fromApiJsonHelper.parse(json); final String apptableName = fromApiJsonHelper.extractStringNamed("apptableName", element); - final String fkColumnName = apptableName.substring(2) + "_id"; - baseDataValidator.reset().parameter("apptableName").value(apptableName).notBlank().notExceedingLengthOf(50); + baseDataValidator.reset().parameter("apptableName").value(apptableName).ignoreIfNull().notBlank() + .notExceedingLengthOf(50).isOneOfTheseValues(supportedApptableNames); + final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : ""; - if (fromApiJsonHelper.parameterExists("changeColumns", element)) { - final JsonArray changeColumns = fromApiJsonHelper.extractJsonArrayNamed("changeColumns", element); - baseDataValidator.reset().parameter("changeColumns").value(changeColumns).ignoreIfNull(); + final JsonArray changeColumns = fromApiJsonHelper.extractJsonArrayNamed("changeColumns", element); + baseDataValidator.reset().parameter("changeColumns").value(changeColumns).ignoreIfNull().jsonArrayNotEmpty(); + if (changeColumns != null) { for (JsonElement column : changeColumns) { fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForChangeColumns); final String name = fromApiJsonHelper.extractStringNamed("name", column); - baseDataValidator.reset().parameter("name").value(name).notNull().notBlank().notExceedingLengthOf(50) + baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50) .isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); final String newName = fromApiJsonHelper.extractStringNamed("newName", column); @@ -134,65 +149,81 @@ public class DatatableCommandFromApiJsonDeserializer { .matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); final String type = fromApiJsonHelper.extractStringNamed("type", column); - baseDataValidator.reset().parameter("type").value(type).notNull().notBlank() + baseDataValidator.reset().parameter("type").value(type).notBlank() .isOneOfTheseValues(supportedColumnTypes); final Integer length = fromApiJsonHelper.extractIntegerSansLocaleNamed("length", column); - if (type.equals("String")) { - baseDataValidator.reset().parameter("length").value(length).notNull().notBlank() - .zeroOrPositiveAmount(); - } else { - baseDataValidator.reset().parameter("length").value(length).ignoreIfNull().notBlank() - .zeroOrPositiveAmount(); + if (type != null && (type.equals("String") || type.equals("Text"))) { + if (length != null) { + baseDataValidator.reset().parameter("length").value(length).notBlank() + .zeroOrPositiveAmount(); + } else if (!type.equals("Text")) { + baseDataValidator.reset().parameter("length").value(length) + .cantBeBlankWhenParameterProvidedIs("type", type); + } + } else if (type != null && length != null) { + baseDataValidator.reset().parameter("length").value(length) + .mustBeBlankWhenParameterProvidedIs("type", type); } final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column); - baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().trueOrFalseRequired(mandatory); - + baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank() + .isOneOfTheseValues(true, false); + final Boolean after = fromApiJsonHelper.extractBooleanNamed("after", column); - baseDataValidator.reset().parameter("after").value(after).ignoreIfNull().notBlank(); + baseDataValidator.reset().parameter("after").value(after).ignoreIfNull().notBlank() + .isOneOfTheseValues(true, false); } } - if (fromApiJsonHelper.parameterExists("addColumns", element)) { - final JsonArray addColumns = fromApiJsonHelper.extractJsonArrayNamed("addColumns", element); - baseDataValidator.reset().parameter("addColumns").value(addColumns).ignoreIfNull(); - + + final JsonArray addColumns = fromApiJsonHelper.extractJsonArrayNamed("addColumns", element); + baseDataValidator.reset().parameter("addColumns").value(addColumns).ignoreIfNull().jsonArrayNotEmpty(); + + if (addColumns != null) { for (JsonElement column : addColumns) { fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForAddColumns); final String name = fromApiJsonHelper.extractStringNamed("name", column); - baseDataValidator.reset().parameter("name").value(name).notNull().notBlank().notExceedingLengthOf(50) + baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50) .isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); final String type = fromApiJsonHelper.extractStringNamed("type", column); - baseDataValidator.reset().parameter("type").value(type).notNull().notBlank() + baseDataValidator.reset().parameter("type").value(type).notBlank() .isOneOfTheseValues(supportedColumnTypes); final Integer length = fromApiJsonHelper.extractIntegerSansLocaleNamed("length", column); - if (type.equals("String")) { - baseDataValidator.reset().parameter("length").value(length).notNull().notBlank() - .zeroOrPositiveAmount(); - } else { - baseDataValidator.reset().parameter("length").value(length).ignoreIfNull().notBlank() - .zeroOrPositiveAmount(); + if (type != null && (type.equals("String") || type.equals("Text"))) { + if (length != null) { + baseDataValidator.reset().parameter("length").value(length).notBlank() + .zeroOrPositiveAmount(); + } else if (!type.equals("Text")) { + baseDataValidator.reset().parameter("length").value(length) + .cantBeBlankWhenParameterProvidedIs("type", type); + } + } else if (type != null && length != null) { + baseDataValidator.reset().parameter("length").value(length) + .mustBeBlankWhenParameterProvidedIs("type", type); } final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column); - baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().trueOrFalseRequired(mandatory); + baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank() + .isOneOfTheseValues(true, false); final Boolean after = fromApiJsonHelper.extractBooleanNamed("after", column); - baseDataValidator.reset().parameter("after").value(after).ignoreIfNull().notBlank(); + baseDataValidator.reset().parameter("after").value(after).ignoreIfNull().notBlank() + .isOneOfTheseValues(true, false); } } - if (fromApiJsonHelper.parameterExists("dropColumns", element)) { - final JsonArray dropColumns = fromApiJsonHelper.extractJsonArrayNamed("dropColumns", element); - baseDataValidator.reset().parameter("dropColumns").value(dropColumns).ignoreIfNull(); + final JsonArray dropColumns = fromApiJsonHelper.extractJsonArrayNamed("dropColumns", element); + baseDataValidator.reset().parameter("dropColumns").value(dropColumns).ignoreIfNull().jsonArrayNotEmpty(); + + if (dropColumns != null) { for (JsonElement column : dropColumns) { fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForDropColumns); final String name = fromApiJsonHelper.extractStringNamed("name", column); - baseDataValidator.reset().parameter("name").value(name).notNull().notBlank().notExceedingLengthOf(50) + baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50) .isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateDatatableCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateDatatableCommandHandler.java index 625e62330..b96a8bfc5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateDatatableCommandHandler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateDatatableCommandHandler.java @@ -27,14 +27,14 @@ public class UpdateDatatableCommandHandler implements NewCommandSourceHandler { @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { - - final String datatableName = command.getUrl().replaceAll("/datatables/", ""); - + + final String datatableName = command.getUrl().replaceAll("/datatables/", ""); + this.writePlatformService.updateDatatable(datatableName, command); - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withResourceIdAsString(datatableName) // - .build(); + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withResourceIdAsString(datatableName) + .build(); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index fdc2c3e7a..4b6ebd737 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -18,6 +18,7 @@ import javax.sql.DataSource; import org.apache.commons.lang.StringUtils; import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.exception.SQLGrammarException; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.ApiParameterError; @@ -56,18 +57,13 @@ import com.google.gson.reflect.TypeToken; @Service public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataService { - private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$"; + private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$"; private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class); - private final static HashMap apiTypeToMySQL = new HashMap() { - - { - put("String", "VARCHAR"); - put("Number", "INT"); - put("Decimal", "DECIMAL"); - put("Date", "DATE"); - } - }; + private final static HashMap apiTypeToMySQL = new HashMap() {{ + put("String", "VARCHAR"); put("Number", "INT"); put("Decimal", "DECIMAL"); put("Date", "DATE"); + put("Text", "TEXT"); + }}; private final JdbcTemplate jdbcTemplate; private final DataSource dataSource; @@ -285,232 +281,277 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } } - private Boolean datatableExists(final String datatable) { - - String sql = "SELECT COUNT(*) FROM `x_registered_table` WHERE `registered_table_name`='" + datatable + "'"; - Integer rowCount = this.jdbcTemplate.queryForInt(sql); - - return (rowCount > 0); - } - - private void validateDatatableExists(final String name) { - - if (!datatableExists(name)) { throw new PlatformDataIntegrityException("error.msg.datatables.missing.table.name", "Table '" + name - + "' does not exist.", name); } - } - - private void validateDatatableNotExists(final String name) { - - if (datatableExists(name)) { throw new PlatformDataIntegrityException("error.msg.datatables.duplicate.table.name", "Table '" + name - + "' already exists.", name); } - } - private void validateDatatableName(final String name) { - - if (name == null || name.isEmpty()) { - throw new PlatformDataIntegrityException("error.msg.datatables.datatable.null.name", "Data table name must not be blank."); - } else if (!name.matches(DATATABLE_NAME_REGEX_PATTERN)) { throw new PlatformDataIntegrityException( - "error.msg.datatables.datatable.invalid.name.regex", "Invalid data table name.", name); } + + if (name == null || name.isEmpty()) { + throw new PlatformDataIntegrityException("error.msg.datatables.datatable.null.name", + "Data table name must not be blank."); + } else if (!name.matches(DATATABLE_NAME_REGEX_PATTERN)) { + throw new PlatformDataIntegrityException("error.msg.datatables.datatable.invalid.name.regex", + "Invalid data table name.", name); + } } private void parseDatatableColumnObjectForCreate(final JsonObject column, StringBuilder sqlBuilder) { - - String name = (column.has("name")) ? column.get("name").getAsString() : null; - String type = (column.has("type")) ? column.get("type").getAsString() : null; - Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; - Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; - - String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append("`" + name + "` " + mysqlType); - - if (type != null) { - if (type.equals("String")) { - sqlBuilder = sqlBuilder.append("(" + length + ")"); - } else if (type.equals("Decimal")) { - sqlBuilder = sqlBuilder.append("(19,6)"); - } - } - if (mandatory != null) { - if (mandatory) { - sqlBuilder = sqlBuilder.append(" NOT NULL"); - } else { - sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); - } - } - - sqlBuilder = sqlBuilder.append(", "); + + String name = (column.has("name")) ? column.get("name").getAsString() : null; + String type = (column.has("type")) ? column.get("type").getAsString() : null; + Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; + Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; + + String mysqlType = apiTypeToMySQL.get(type); + sqlBuilder = sqlBuilder.append("`" + name + "` " + mysqlType); + + if (type != null) + { + if (type.equals("String")) { + sqlBuilder = sqlBuilder.append("(" + length + ")"); + } else if (type.equals("Decimal")) { + sqlBuilder = sqlBuilder.append("(19,6)"); + } + } + if (mandatory != null) { + if (mandatory) { + sqlBuilder = sqlBuilder.append(" NOT NULL"); + } else { + sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); + } + } + + sqlBuilder = sqlBuilder.append(", "); } @Transactional @Override public CommandProcessingResult createDatatable(final JsonCommand command) { - context.authenticatedUser(); - this.fromApiJsonDeserializer.validateForCreate(command.json()); + String datatableName = null; + + try { + context.authenticatedUser(); + this.fromApiJsonDeserializer.validateForCreate(command.json()); + + JsonElement element = this.fromJsonHelper.parse(command.json()); + JsonArray columns = this.fromJsonHelper.extractJsonArrayNamed("columns", element); + datatableName = this.fromJsonHelper.extractStringNamed("datatableName", element); + String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element); + Boolean multiRow = this.fromJsonHelper.extractBooleanNamed("multiRow", element); + + if (multiRow == null) { + multiRow = false; + } + + validateDatatableName(datatableName); + validateAppTable(apptableName); + + String fkColumnName = apptableName.substring(2) + "_id"; + String fkName = datatableName.toLowerCase().replaceAll("\\s", "_"); + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` ("); + + if (multiRow) { + sqlBuilder = sqlBuilder + .append("`id` BIGINT(20) NOT NULL AUTO_INCREMENT, ") + .append("`" + fkColumnName + "` BIGINT(20) NOT NULL, "); + } else { + sqlBuilder = sqlBuilder + .append("`" + fkColumnName + "` BIGINT(20) NOT NULL, "); + } + + for (JsonElement column : columns) { + parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder); + } + + // Remove trailing comma and space + sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length()); + + if (multiRow) { + sqlBuilder = sqlBuilder + .append(", PRIMARY KEY (`id`)") + .append(", KEY `fk_" + apptableName + "` (`" + fkColumnName + "`)") + .append(", CONSTRAINT `fk_" + fkName + "` ") + .append("FOREIGN KEY (`" + fkColumnName + "`) ") + .append("REFERENCES `" + apptableName + "` (`id`)"); + + } else { + sqlBuilder = sqlBuilder + .append(", PRIMARY KEY (`" + fkColumnName + "`)") + .append(", CONSTRAINT `fk_" + fkName + "` ") + .append("FOREIGN KEY (`" + fkColumnName + "`) ") + .append("REFERENCES `" + apptableName + "` (`id`)"); + } + + sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + this.jdbcTemplate.execute(sqlBuilder.toString()); + + registerDatatable(datatableName, apptableName); + } catch (SQLGrammarException e) { + Throwable realCause = e.getCause(); - JsonElement element = this.fromJsonHelper.parse(command.json()); - JsonArray columns = this.fromJsonHelper.extractJsonArrayNamed("columns", element); - String datatableName = this.fromJsonHelper.extractStringNamed("datatableName", element); - String apptable = this.fromJsonHelper.extractStringNamed("apptableName", element); - Boolean multiRow = this.fromJsonHelper.extractBooleanNamed("multiRow", element); + if (realCause.getMessage().contains("Table") && realCause.getMessage().contains("already exists")) { + throw new PlatformDataIntegrityException( + "error.msg.datatable.already.exists", realCause.getMessage()); + } + } - if (multiRow == null) { - multiRow = false; - } - - validateDatatableName(datatableName); - validateDatatableName(apptable); - validateDatatableNotExists(datatableName); - - String fkColumnName = apptable.substring(2) + "_id"; - String fkName = datatableName.toLowerCase().replaceAll("\\s", "_"); - StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` ("); - - if (multiRow) { - sqlBuilder = sqlBuilder.append("`id` BIGINT(20) NOT NULL AUTO_INCREMENT, ").append( - "`" + fkColumnName + "` BIGINT(20) NOT NULL, "); - } else { - sqlBuilder = sqlBuilder.append("`" + fkColumnName + "` BIGINT(20) NOT NULL, "); - } - - for (JsonElement column : columns) { - parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder); - } - - // Remove trailing comma and space - sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length()); - - if (multiRow) { - sqlBuilder = sqlBuilder.append(", PRIMARY KEY (`id`)").append(", KEY `fk_" + apptable + "` (`" + fkColumnName + "`)") - .append(", CONSTRAINT `fk_" + fkName + "` ").append("FOREIGN KEY (`" + fkColumnName + "`) ") - .append("REFERENCES `" + apptable + "` (`id`)"); - - } else { - sqlBuilder = sqlBuilder.append(", PRIMARY KEY (`" + fkColumnName + "`)").append(", CONSTRAINT `fk_" + fkName + "` ") - .append("FOREIGN KEY (`" + fkColumnName + "`) ").append("REFERENCES `" + apptable + "` (`id`)"); - } - - sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - this.jdbcTemplate.execute(sqlBuilder.toString()); - - registerDatatable(datatableName, apptable); - - return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withResourceIdAsString(datatableName).build(); + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withResourceIdAsString(datatableName) + .build(); } private void parseDatatableColumnForUpdate(final JsonObject column, StringBuilder sqlBuilder) { - - String name = (column.has("name")) ? column.get("name").getAsString() : null; - String newName = (column.has("newName")) ? column.get("newName").getAsString() : name; - String type = (column.has("type")) ? column.get("type").getAsString() : null; - Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; - Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; - String after = (column.has("after")) ? column.get("after").getAsString() : null; - - String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + mysqlType); - - if (type != null) { - if (type.equals("String") && length != null) { - sqlBuilder = sqlBuilder.append("(" + length + ")"); - } else if (type.equals("Decimal")) { - sqlBuilder = sqlBuilder.append("(19,6)"); - } - } - if (mandatory != null) { - if (mandatory) { - sqlBuilder = sqlBuilder.append(" NOT NULL"); - } else { - sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); - } - } - if (after != null) { - sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); - } + + String name = (column.has("name")) ? column.get("name").getAsString() : null; + String newName = (column.has("newName")) ? column.get("newName").getAsString() : name; + String type = (column.has("type")) ? column.get("type").getAsString() : null; + Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; + Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; + String after = (column.has("after")) ? column.get("after").getAsString() : null; + + String mysqlType = apiTypeToMySQL.get(type); + sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + mysqlType); + + if (type != null) { + if (type.equals("String") && length != null) { + sqlBuilder = sqlBuilder.append("(" + length + ")"); + } else if (type.equals("Decimal")) { + sqlBuilder = sqlBuilder.append("(19,6)"); + } + } + if (mandatory != null) { + if (mandatory) { + sqlBuilder = sqlBuilder.append(" NOT NULL"); + } else { + sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); + } + } + if (after != null) { + sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); + } } private void parseDatatableColumnForAdd(final JsonObject column, StringBuilder sqlBuilder) { - - String name = (column.has("name")) ? column.get("name").getAsString() : null; - String type = (column.has("type")) ? column.get("type").getAsString() : null; - Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; - Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; - String after = (column.has("after")) ? column.get("after").getAsString() : null; - - String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append(", ADD `" + name + "` " + mysqlType); - - if (type != null) { - if (type.equals("String") && length != null) { - sqlBuilder = sqlBuilder.append("(" + length + ")"); - } else if (type.equals("Decimal")) { - sqlBuilder = sqlBuilder.append("(19,6)"); - } - } - if (mandatory != null) { - if (mandatory) { - sqlBuilder = sqlBuilder.append(" NOT NULL"); - } else { - sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); - } - } - if (after != null) { - sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); - } + + String name = (column.has("name")) ? column.get("name").getAsString() : null; + String type = (column.has("type")) ? column.get("type").getAsString() : null; + Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; + Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; + String after = (column.has("after")) ? column.get("after").getAsString() : null; + + String mysqlType = apiTypeToMySQL.get(type); + sqlBuilder = sqlBuilder.append(", ADD `" + name + "` " + mysqlType); + + if (type != null) { + if (type.equals("String") && length != null) { + sqlBuilder = sqlBuilder.append("(" + length + ")"); + } else if (type.equals("Decimal")) { + sqlBuilder = sqlBuilder.append("(19,6)"); + } + } + if (mandatory != null) { + if (mandatory) { + sqlBuilder = sqlBuilder.append(" NOT NULL"); + } else { + sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); + } + } + if (after != null) { + sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); + } } - private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) { - - String name = (column.has("name")) ? column.get("name").getAsString() : null; - - sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`"); - } + private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) { + + String name = (column.has("name")) ? column.get("name").getAsString() : null; + + sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`"); + } @Transactional @Override public void updateDatatable(final String datatableName, final JsonCommand command) { - context.authenticatedUser(); - this.fromApiJsonDeserializer.validateForUpdate(command.json()); + try { + context.authenticatedUser(); + this.fromApiJsonDeserializer.validateForUpdate(command.json()); + + JsonElement element = this.fromJsonHelper.parse(command.json()); + JsonArray changeColumns = this.fromJsonHelper.extractJsonArrayNamed("changeColumns", element); + JsonArray addColumns = this.fromJsonHelper.extractJsonArrayNamed("addColumns", element); + JsonArray dropColumns = this.fromJsonHelper.extractJsonArrayNamed("dropColumns", element); + String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element); + + validateDatatableName(datatableName); + + if (!StringUtils.isBlank(apptableName)) { + validateAppTable(apptableName); + + String oldApptableName = this.queryForApplicationTableName(datatableName); + if (!StringUtils.equals(oldApptableName, apptableName)) { + deregisterDatatable(datatableName); + registerDatatable(datatableName, apptableName); + } + } + + if (changeColumns == null && addColumns == null && dropColumns == null) { + return; + } + + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + + if (changeColumns != null) { + for (JsonElement column : changeColumns) { + parseDatatableColumnForUpdate(column.getAsJsonObject(), sqlBuilder); + } + } + if (addColumns != null) { + for (JsonElement column : addColumns) { + parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder); + } + } + if (dropColumns != null) { + for (JsonElement column : dropColumns) { + parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder); + } + } + + // Remove the first comma, right after ALTER TABLE `datatable` + int indexOfFirstComma = sqlBuilder.indexOf(","); + if (indexOfFirstComma != -1) { + sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); + } + this.jdbcTemplate.execute(sqlBuilder.toString()); + } catch (SQLGrammarException e) { + Throwable realCause = e.getCause(); - JsonElement element = this.fromJsonHelper.parse(command.json()); - JsonArray changeColumns = this.fromJsonHelper.extractJsonArrayNamed("changeColumns", element); - JsonArray addColumns = this.fromJsonHelper.extractJsonArrayNamed("addColumns", element); - JsonArray dropColumns = this.fromJsonHelper.extractJsonArrayNamed("dropColumns", element); - - validateDatatableName(datatableName); - validateDatatableExists(datatableName); - - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); - - for (JsonElement column : changeColumns) { - parseDatatableColumnForUpdate(column.getAsJsonObject(), sqlBuilder); - } - for (JsonElement column : addColumns) { - parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder); - } - for (JsonElement column : dropColumns) { - parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder); - } - - // Remove the first comma, right after ALTER TABLE `datatable` - sqlBuilder = sqlBuilder.deleteCharAt(sqlBuilder.indexOf(",")); - this.jdbcTemplate.execute(sqlBuilder.toString()); + if (realCause.getMessage().contains("Unknown column")) { + throw new PlatformDataIntegrityException( + "error.msg.datatable.column.missing", realCause.getMessage()); + } + } } @Transactional @Override public void deleteDatatable(final String datatableName) { - - context.authenticatedUser(); - - validateDatatableName(datatableName); - validateDatatableExists(datatableName); - deregisterDatatable(datatableName); - - String sql = "DROP TABLE `" + datatableName + "`"; - this.jdbcTemplate.execute(sql); + + try { + context.authenticatedUser(); + + validateDatatableName(datatableName); + deregisterDatatable(datatableName); + + String sql = "DROP TABLE `" + datatableName + "`"; + this.jdbcTemplate.execute(sql); + } catch (SQLGrammarException e) { + Throwable realCause = e.getCause(); + + if (realCause.getMessage().contains("Unknown table")) { + throw new PlatformDataIntegrityException( + "error.msg.datatable.missing", realCause.getMessage()); + } + } } @Transactional