MIFOSX-365: Simplify 'Data Tables' functionality

This commit is contained in:
Lech Rozanski
2013-06-04 14:48:41 +02:00
parent e99834bd83
commit 16996b5e8c
4 changed files with 454 additions and 145 deletions
+249 -16
View File
@@ -1063,11 +1063,19 @@
<tr>
<td><a href="#datatables">Data Table</a></td>
<td>datatables</td>
<td></td>
<td><a href="#datatables_createTable">Create Data Table</a></td>
<td><a href="#datatables_list">List Data Tables</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>datatables/{datatable}</td>
<td></td>
<td><a href="#datatables_getTable">Retrieve Data Table Details</a></td>
<td><a href="#datatables_updateTable">Update Data Table</a></td>
<td><a href="#datatables_deleteTable">Delete Data Table</a></td>
</tr>
<tr class="alt">
<td></td>
<td>datatables/register/{datatable}/{apptable}</td>
@@ -11276,6 +11284,57 @@ No Request Body:
</div>
</div>
<a id="datatables_createTable" name="datatables_createTable"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Create Data Table</h4>
<p>Create a new data table and registers it with the Mifos X Core application
table.</p>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/datatables
</code>
<code class="method-request">
POST https://DomainName/api/v1/datatables
Content-Type: application/json
Request Body:
{
"datatableName": "extra_client_details",
"apptableName": "m_client",
"columns": [
{
"name": "Gender",
"type": "Dropdown",
"code": "Gender"
},
{
"name": "Some Decimal",
"type": "Decimal",
"mandatory": true
},
{
"name": "Birth Date",
"type": "Date"
},
{
"name": "Question 2",
"type": "String",
"length": 100,
"mandatory": false
}
]
}
</code>
<code class="method-response">
{
"resourceIdentifier": "extra_client_details"
}
</code>
</div>
</div>
<a id="datatables_list" name="datatables_list"
class="old-syle-anchor">&nbsp;</a>
@@ -11303,25 +11362,191 @@ No Request Body:
<code class="method-declaration">
GET https://DomainName/api/v1/datatables
</code>
<code class="method-response">
<code class="method-response">
[
{
"applicationTableName": "m_client",
"registeredTableName": "Additional Client Fields Data"
},
{
"applicationTableName": "m_client",
"registeredTableName": "Extra Family Details Data"
},
{
"applicationTableName": "m_loan",
"registeredTableName": "Additional Loan Fields Data"
}
]
{
"applicationTableName": "m_client",
"registeredTableName": "extra_client_details",
"columnHeaderData": [
{
"columnName": "client_id",
"columnType": "bigint",
"columnLength": 0,
"columnDisplayType": "INTEGER",
"isColumnNullable": false,
"isColumnPrimaryKey": true,
"columnValues": []
},
{
"columnName": "Gender_cd_Question",
"columnType": "int",
"columnLength": 0,
"columnDisplayType": "INTEGER",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Some Decimal",
"columnType": "decimal",
"columnLength": 0,
"columnDisplayType": "DECIMAL",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Birth Date",
"columnType": "date",
"columnLength": 0,
"columnDisplayType": "DATE",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
}
]
}
]
</code>
</div>
</div>
<a id="datatables_getTable" name="datatables_getTable"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve Data Table Details</h4>
<p>Lists a registered data table details and the Mifos X Core
application table they are registered to.</p>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/datatables/{datatable}
</code>
<code class="method-response">
{
"applicationTableName": "m_client",
"registeredTableName": "extra_client_details",
"columnHeaderData": [
{
"columnName": "client_id",
"columnType": "bigint",
"columnLength": 0,
"columnDisplayType": "INTEGER",
"isColumnNullable": false,
"isColumnPrimaryKey": true,
"columnValues": []
},
{
"columnName": "Gender_cd_Question",
"columnType": "int",
"columnLength": 0,
"columnDisplayType": "INTEGER",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Some Decimal",
"columnType": "decimal",
"columnLength": 0,
"columnDisplayType": "DECIMAL",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Birth Date",
"columnType": "date",
"columnLength": 0,
"columnDisplayType": "DATE",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
}
]
}
</code>
</div>
</div>
<a id="datatables_updateTable" name="datatables_updateTable"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Update Data Table</h4>
<p>Modifies fields of a data table. If the apptableName parameter is passed,
data table is deregistered and registered with the new application table.</p>
</div>
<div class="method-example">
<code class="method-declaration">
PUT https://DomainName/api/v1/datatables/{datatables}
</code>
<code class="method-request">
PUT https://DomainName/api/v1/datatables/extra_client_details
Content-Type: application/json
Request Body:
{
"apptableName": "m_client",
"columns": [
{
"name": "Gender",
"type": "Dropdown",
"code": "Gender"
},
{
"name": "Some Decimal",
"type": "Decimal",
"mandatory": true
},
{
"name": "Birth Date",
"type": "Date"
},
{
"name": "Question 2",
"type": "String",
"length": 100,
"mandatory": false
}
]
}
</code>
<code class="method-response">
{
"resourceIdentifier": "extra_client_details"
}
</code>
</div>
</div>
<a id="datatables_deleteTable" name="datatables_deleteTable"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Delete Data Table</h4>
<p>Deletes a data table and unregisters it from the Mifos X Core
application table.</p>
</div>
<div class="method-example">
<code class="method-declaration">
DELETE https://DomainName/api/v1/datatables/{datatables}
</code>
<code class="method-request">
DELETE https://DomainName/api/v1/datatables/extra_client_details
Content-Type: application/json
No Request Body
</code>
<code class="method-response">
{
"resourceIdentifier": "extra_client_details"
}
</code>
</div>
</div>
<a id="datatables_register" name="datatables_register"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
@@ -15357,11 +15582,19 @@ GET https://DomainName/api/v1/search?query=000000111
<tr class="alt">
<td><a href="#datatables">Data Table</a></td>
<td>datatables</td>
<td></td>
<td><a href="#datatables_createTable">Create Data Table</a></td>
<td><a href="#datatables_list">List Data Tables</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>datatables/{datatable}</td>
<td></td>
<td><a href="#datatables_getTable">Retrieve Data Table Details</a></td>
<td><a href="#datatables_updateTable">Update Data Table</a></td>
<td><a href="#datatables_deleteTable">Delete Data Table</a></td>
</tr>
<tr>
<td></td>
<td>datatables/register/{datatable}/{apptable}</td>
@@ -18,6 +18,7 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -29,22 +30,23 @@ import com.google.gson.reflect.TypeToken;
public class DatatableCommandFromApiJsonDeserializer {
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_COLUMN_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,}[a-zA-Z0-9]$";
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParametersForCreate = new HashSet<String>(Arrays.asList(
"datatableName", "apptableName", "multiRow", "columns"));
private final Set<String> supportedParametersForCreateColumns = new HashSet<String>(Arrays.asList(
"name", "type", "length", "mandatory"));
"name", "type", "length", "mandatory", "code"));
private final Set<String> supportedParametersForUpdate = new HashSet<String>(Arrays.asList(
"apptableName", "changeColumns", "addColumns", "dropColumns"));
private final Set<String> supportedParametersForAddColumns = new HashSet<String>(Arrays.asList(
"name", "type", "length", "mandatory", "after"));
"name", "type", "length", "mandatory", "after", "code"));
private final Set<String> supportedParametersForChangeColumns = new HashSet<String>(Arrays.asList(
"name", "newName", "type", "length", "mandatory", "after"));
"name", "newName", "mandatory", "after", "code", "newCode"));
private final Set<String> supportedParametersForDropColumns = new HashSet<String>(Arrays.asList(
"name"));
private final Object[] supportedColumnTypes = { "String", "Number", "Decimal", "Date", "Text" };
private final Object[] supportedColumnTypes = { "String", "Number", "Decimal", "Date", "Text", "Dropdown" };
private final Object[] supportedApptableNames = { "m_loan", "m_savings_account", "m_client", "m_group", "m_office" };
private final FromJsonHelper fromApiJsonHelper;
@@ -54,6 +56,35 @@ public class DatatableCommandFromApiJsonDeserializer {
this.fromApiJsonHelper = fromApiJsonHelper;
}
private void validateType(final DataValidatorBuilder baseDataValidator, final JsonElement column) {
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")) {
if (length != null) {
baseDataValidator.reset().parameter("length").value(length).notBlank()
.zeroOrPositiveAmount();
} else {
baseDataValidator.reset().parameter("length").value(length)
.cantBeBlankWhenParameterProvidedIs("type", type);
}
} else {
baseDataValidator.reset().parameter("length").value(length)
.mustBeBlankWhenParameterProvidedIs("type", type);
}
final String code = fromApiJsonHelper.extractStringNamed("code", column);
if (type != null && type.equals("Dropdown")) {
baseDataValidator.reset().parameter("code").value(code).notBlank()
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
} else {
baseDataValidator.reset().parameter("code").value(code).mustBeBlankWhenParameterProvided("type", type);
}
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -83,28 +114,16 @@ public class DatatableCommandFromApiJsonDeserializer {
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);
baseDataValidator.reset().parameter("name").value(name).notBlank()
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_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);
}
validateType(baseDataValidator, column);
final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column);
baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank()
.isOneOfTheseValues(true, false);
@@ -118,7 +137,10 @@ public class DatatableCommandFromApiJsonDeserializer {
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(); }
if (!json.matches("(?s)\\A\\{.*?(\\\".*?\\\"\\s*?:\\s*?)+.*?\\}\\z")) {
throw new PlatformDataIntegrityException("error.msg.invalid.request.body.no.parameters",
"Provided JSON request body does not have any parameters.");
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForUpdate);
@@ -129,7 +151,7 @@ public class DatatableCommandFromApiJsonDeserializer {
final JsonElement element = fromApiJsonHelper.parse(json);
final String apptableName = fromApiJsonHelper.extractStringNamed("apptableName", element);
baseDataValidator.reset().parameter("apptableName").value(apptableName).ignoreIfNull().notBlank()
.notExceedingLengthOf(50).isOneOfTheseValues(supportedApptableNames);
.isOneOfTheseValues(supportedApptableNames);
final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : "";
final JsonArray changeColumns = fromApiJsonHelper.extractJsonArrayNamed("changeColumns", element);
@@ -140,36 +162,31 @@ public class DatatableCommandFromApiJsonDeserializer {
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForChangeColumns);
final String name = fromApiJsonHelper.extractStringNamed("name", column);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50)
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
baseDataValidator.reset().parameter("name").value(name).notBlank()
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
final String newName = fromApiJsonHelper.extractStringNamed("newName", column);
baseDataValidator.reset().parameter("newName").value(newName).ignoreIfNull().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 String code = fromApiJsonHelper.extractStringNamed("code", column);
baseDataValidator.reset().parameter("code").value(code).ignoreIfNull().notBlank()
.notExceedingLengthOf(100).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
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 String newCode = fromApiJsonHelper.extractStringNamed("newCode", column);
baseDataValidator.reset().parameter("newCode").value(newCode).ignoreIfNull().notBlank()
.notExceedingLengthOf(100).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
if (StringUtils.isBlank(code) && StringUtils.isNotBlank(newCode)) {
baseDataValidator.reset().parameter("code").value(code)
.cantBeBlankWhenParameterProvidedIs("newCode", newCode);
}
final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column);
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()
.isOneOfTheseValues(true, false);
@@ -184,26 +201,10 @@ public class DatatableCommandFromApiJsonDeserializer {
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForAddColumns);
final String name = fromApiJsonHelper.extractStringNamed("name", column);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50)
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
baseDataValidator.reset().parameter("name").value(name).notBlank()
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_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);
}
validateType(baseDataValidator, column);
final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column);
baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank()
@@ -223,8 +224,8 @@ public class DatatableCommandFromApiJsonDeserializer {
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForDropColumns);
final String name = fromApiJsonHelper.extractStringNamed("name", column);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(50)
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
baseDataValidator.reset().parameter("name").value(name).notBlank()
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
}
}
@@ -17,7 +17,6 @@ public final class ResultsetColumnHeaderData {
private final String columnName;
private final String columnType;
@SuppressWarnings("unused")
private final Long columnLength;
private final String columnDisplayType;
private final boolean isColumnNullable;
@@ -161,6 +160,10 @@ public final class ResultsetColumnHeaderData {
public String getColumnType() {
return this.columnType;
}
public Long getColumnLength() {
return this.columnLength;
}
public String getColumnDisplayType() {
return this.columnDisplayType;
@@ -62,7 +62,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class);
private final static HashMap<String, String> apiTypeToMySQL = new HashMap<String, String>() {{
put("String", "VARCHAR"); put("Number", "INT"); put("Decimal", "DECIMAL"); put("Date", "DATE");
put("Text", "TEXT");
put("Text", "TEXT"); put ("Dropdown", "INT");
}};
private final JdbcTemplate jdbcTemplate;
@@ -292,22 +292,34 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
}
}
private String datatableColumnNameToCodeValueName(final String columnName, final String code) {
return (code + "_cd_" + columnName);
}
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 code = (column.has("code")) ? column.get("code").getAsString() : null;
if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code);
}
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)");
} else if (type.equals("Dropdown")) {
sqlBuilder = sqlBuilder.append("(11)");
}
}
if (mandatory != null) {
@@ -317,7 +329,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
sqlBuilder = sqlBuilder.append(" DEFAULT NULL");
}
}
sqlBuilder = sqlBuilder.append(", ");
}
@@ -326,29 +338,29 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
public CommandProcessingResult createDatatable(final JsonCommand command) {
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, ")
@@ -357,14 +369,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
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`)")
@@ -372,7 +384,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
.append(", CONSTRAINT `fk_" + fkName + "` ")
.append("FOREIGN KEY (`" + fkColumnName + "`) ")
.append("REFERENCES `" + apptableName + "` (`id`)");
} else {
sqlBuilder = sqlBuilder
.append(", PRIMARY KEY (`" + fkColumnName + "`)")
@@ -380,10 +391,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
.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();
@@ -400,25 +411,43 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
.build();
}
private void parseDatatableColumnForUpdate(final JsonObject column, StringBuilder sqlBuilder) {
private void parseDatatableColumnForUpdate(final JsonObject column,
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, 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)");
String code = (column.has("code")) ? column.get("code").getAsString() : null;
String newCode = (column.has("newCode")) ? column.get("newCode").getAsString() : null;
if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code);
if (StringUtils.isNotBlank(newCode)) {
newName = datatableColumnNameToCodeValueName(newName, newCode);
} else {
newName = datatableColumnNameToCodeValueName(newName, code);
}
}
if (!mapColumnNameDefinition.containsKey(name)) {
throw new PlatformDataIntegrityException("error.msg.datatable.column.missing.update.parse",
"Column " + name + " does not exist.", name);
}
String type = mapColumnNameDefinition.get(name).getColumnType();
Long length = mapColumnNameDefinition.get(name).getColumnLength();
sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + type);
if (length != null && length > 0) {
if (type.equals("DECIMAL")) {
sqlBuilder.append("(19,6)");
} else if (type.equals("VARCHAR")) {
sqlBuilder.append("(" + length + ")");
}
}
if (mandatory != null) {
if (mandatory) {
sqlBuilder = sqlBuilder.append(" NOT NULL");
@@ -432,21 +461,28 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
}
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 code = (column.has("code")) ? column.get("code").getAsString() : null;
if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code);
}
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)");
} else if (type.equals("Dropdown")) {
sqlBuilder = sqlBuilder.append("(11)");
}
}
if (mandatory != null) {
@@ -462,9 +498,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
}
private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) {
String name = (column.has("name")) ? column.get("name").getAsString() : null;
sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`");
}
@@ -475,59 +511,95 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
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);
List<ResultsetColumnHeaderData> columnHeaderData = this.genericDataService.fillResultsetColumnHeaders(datatableName);
Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition = new HashMap<String, ResultsetColumnHeaderData>();
for (ResultsetColumnHeaderData columnHeader : columnHeaderData) {
mapColumnNameDefinition.put(columnHeader.getColumnName(), columnHeader);
}
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) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
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());
}
// Remove the first comma, right after ALTER TABLE `datatable`
int indexOfFirstComma = sqlBuilder.indexOf(",");
if (indexOfFirstComma != -1) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
if (addColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
for (JsonElement column : addColumns) {
parseDatatableColumnForAdd(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());
}
if (changeColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
for (JsonElement column : changeColumns) {
parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, 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());
}
this.jdbcTemplate.execute(sqlBuilder.toString());
} catch (SQLGrammarException e) {
Throwable realCause = e.getCause();
if (realCause.getMessage().contains("Unknown column")) {
if (realCause.getMessage().toLowerCase().contains("unknown column")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.column.missing", realCause.getMessage());
"error.msg.datatable.column.missing.update", realCause.getMessage());
} else if (realCause.getMessage().toLowerCase().contains("can't drop")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.column.missing.drop", realCause.getMessage());
} else if (realCause.getMessage().toLowerCase().contains("duplicate column")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.duplicate.column", realCause.getMessage());
}
}
}
@@ -535,18 +607,18 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
@Transactional
@Override
public void deleteDatatable(final String datatableName) {
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());