MIFOSX-365: Simplify 'Data Tables' functionality
This commit is contained in:
+249
-16
@@ -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"> </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"> </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"> </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"> </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"> </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"> </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>
|
||||
|
||||
+65
-64
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -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;
|
||||
|
||||
+136
-64
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user