MIFOSX-237: api doc changes and ensure error handling supported is added back in for datatables api.

This commit is contained in:
Keith Woodlock
2013-03-11 19:42:44 +00:00
parent b3c77fe045
commit 84747cb109
11 changed files with 319 additions and 160 deletions
+142 -49
View File
@@ -8126,21 +8126,20 @@ GET https://DomainName/api/v1/datatables
POST https://DomainName/api/v1/datatables/register/{datatable}/{apptable} POST https://DomainName/api/v1/datatables/register/{datatable}/{apptable}
</code> </code>
<code class="method-request"> <code class="method-request">
POST datatables/register/userDefinedTableName/m_client POST datatables/register/extra_client_details/m_client
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{} {}
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"resourceId": null "resourceIdentifier": "extra_client_details"
} }
</code> </code>
</div> </div>
</div> </div>
<a id="datatables_deregister" name="datatables_deregister" <a id="datatables_deregister" name="datatables_deregister"
class="old-syle-anchor">&nbsp;</a> class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
@@ -8154,7 +8153,7 @@ Request Body:
POST https://DomainName/api/v1/datatables/deregister/{datatable} POST https://DomainName/api/v1/datatables/deregister/{datatable}
</code> </code>
<code class="method-request"> <code class="method-request">
POST datatables/deregister/userDefinedTableName POST datatables/deregister/extra_client_details
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
@@ -8162,8 +8161,8 @@ Request Body:
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"resourceId": null "resourceIdentifier": "extra_client_details"
} }
</code> </code>
</div> </div>
</div> </div>
@@ -8186,20 +8185,20 @@ Request Body:
POST https://DomainName/api/v1/datatables/{datatable}/{apptableId} POST https://DomainName/api/v1/datatables/{datatable}/{apptableId}
</code> </code>
<code class="method-request"> <code class="method-request">
POST datatables/Additional Client Fields Data/1 POST datatables/extra_client_details/1
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"Business Description": "Livestock sales", "Business Description": "Livestock sales",
Comment: "First comment\nmade", "Comment": "First comment made",
Education_cv: "Trade", "Education_cv": "Primary",
Gender_cd: "21", "Gender_cd": "6",
"Highest Rate Paid": "8.5", "Highest Rate Paid": "8.5",
"Next Visit": "01 October 2012", "Next Visit": "01 October 2012",
"Years in Business": "5", "Years in Business": "5",
dateFormat: "dd MMMM yyyy", "dateFormat": "dd MMMM yyyy",
locale: "en" "locale": "en"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -8235,46 +8234,142 @@ Request Body:
<dd> <dd>
<span>optional</span> <span>optional</span>
</dd> </dd>
<dd>If 'true' a Generic Result Set JSON format is returned. <dd>If 'true' an optimised JSON format is returned suitable for tabular display of data.
This format is used by the default data tables UI functionality.</dd> This format is used by the default data tables UI functionality.</dd>
</dl> </dl>
<p>Example Requests:</p> <p>Example Requests:</p>
<div class=apiClick>datatables/Additional Client Fields <div class=apiClick>datatables/extra_client_details/1</div>
Data/1</div>
<br> <br>
<br> <br>
<div class=apiClick>datatables/Extra Family Details <div class=apiClick>datatables/extra_family_details/1?order=`Date of Birth` desc</div>
Data/1?order=`Date of Birth` desc</div>
<br> <br>
<br> <br>
<div class=apiClick>datatables/Additional Client Fields <div class=apiClick>datatables/extra_client_details/1?genericResultSet=true</div>
Data/1?genericResultSet=true</div>
</div> </div>
<div class="method-example"> <div class="method-example">
<code class="method-declaration"> <code class="method-declaration">
GET https://DomainName/api/v1/datatables/{datatable}/{apptableId} GET https://DomainName/api/v1/datatables/{datatable}/{apptableId}?genericResultSet=true
</code> </code>
<code class="method-response"> <code class="method-response">
[
{ {
"client_id": 1, "columnHeaders": [
"Business Description": "Livestock sales", {
"Years in Business": 5, "columnName": "client_id",
"Gender_cd": "21", "columnType": "bigint",
"Education_cv": "Trade", "columnLength": 0,
"Next Visit": [2012, 10, 1], "columnDisplayType": "INTEGER",
"Highest Rate Paid": 8.500000, "isColumnNullable": false,
"Comment": "First comment made" "isColumnPrimaryKey": true,
} "columnValues": []
] },
{
"columnName": "Business Description",
"columnType": "varchar",
"columnLength": 100,
"columnDisplayType": "STRING",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Years in Business",
"columnType": "int",
"columnLength": 0,
"columnDisplayType": "INTEGER",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Gender_cd",
"columnType": "int",
"columnLength": 0,
"columnDisplayType": "CODELOOKUP",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": [
{
"id": 5,
"value": "option.Male"
},
{
"id": 6,
"value": "option.Female"
}
]
},
{
"columnName": "Education_cv",
"columnType": "varchar",
"columnLength": 60,
"columnDisplayType": "CODEVALUE",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": [
{
"id": 9,
"value": "Primary"
},
{
"id": 10,
"value": "Secondary"
},
{
"id": 11,
"value": "University"
}
]
},
{
"columnName": "Next Visit",
"columnType": "date",
"columnLength": 0,
"columnDisplayType": "DATE",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Highest Rate Paid",
"columnType": "decimal",
"columnLength": 0,
"columnDisplayType": "DECIMAL",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
},
{
"columnName": "Comment",
"columnType": "text",
"columnLength": 65535,
"columnDisplayType": "TEXT",
"isColumnNullable": true,
"isColumnPrimaryKey": false,
"columnValues": []
}
],
"data": [
{
"row": [
"1",
"Livestock sales",
"5",
"6",
"Primary",
"2012-10-01",
"8.500000",
"First\tcomment made"
]
}
]
}
</code> </code>
</div> </div>
</div> </div>
<a id="datatables_update" name="datatables_update" <a id="datatables_update" name="datatables_update" class="old-syle-anchor">&nbsp;</a>
class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Update Entry in Data Table (One to One)</h4> <h4>Update Entry in Data Table (One to One)</h4>
@@ -8285,23 +8380,22 @@ GET https://DomainName/api/v1/datatables/{datatable}/{apptableId}
PUT https://DomainName/api/v1/datatables/{datatable}/{apptableId} PUT https://DomainName/api/v1/datatables/{datatable}/{apptableId}
</code> </code>
<code class="method-request"> <code class="method-request">
PUT datatables/Additional Client Fields Data/1 PUT datatables/extra_client_details/1
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"Business Description": "Livestock sales updated", "Business Description": "Livestock sales updated",
Comment: "First comment\nmade", }
Education_cv: "Trade", </code>
Gender_cd: "21", <code class="method-response">
"Highest Rate Paid": "8.5", {
"Next Visit": "01 October 2012", "resourceId": 1,
"Years in Business": "5", "changes": {
dateFormat: "dd MMMM yyyy", "Business Description": "Livestock sales updated"
locale: "en" }
} }
</code> </code>
<code class="method-response">{ "resourceId": 1 } </code>
</div> </div>
</div> </div>
@@ -8385,8 +8479,7 @@ Request Body:
<code class="method-response">{ "resourceId": 1 } </code> <code class="method-response">{ "resourceId": 1 } </code>
</div> </div>
</div> </div>
<!-- end of datatables api docs -->
<!-- start of datatables api docs -->
<!-- start of Notes api docs --> <!-- start of Notes api docs -->
<a id="notes" name="notes" class="old-syle-anchor">&nbsp;</a> <a id="notes" name="notes" class="old-syle-anchor">&nbsp;</a>
@@ -19,13 +19,17 @@ public class CommandProcessingResult {
private final Long groupId; private final Long groupId;
private final Long clientId; private final Long clientId;
private final Long loanId; private final Long loanId;
private Long resourceId; private final Long resourceId;
private final String transactionId; private final String transactionId;
private Map<String, Object> changes; private final Map<String, Object> changes;
@SuppressWarnings("unused")
private final String resourceIdentifier;
public static CommandProcessingResult fromDetails(final Long commandId, final Long officeId, final Long groupId, final Long clientId, public static CommandProcessingResult fromDetails(final Long commandId, final Long officeId, final Long groupId, final Long clientId,
final Long loanId, final Long entityId, final String transactionId, final Map<String, Object> changes) { final Long loanId, final String resourceIdentifier, final Long entityId, final String transactionId,
return new CommandProcessingResult(commandId, officeId, groupId, clientId, loanId, entityId, transactionId, changes); final Map<String, Object> changes) {
return new CommandProcessingResult(commandId, officeId, groupId, clientId, loanId, resourceIdentifier, entityId, transactionId,
changes);
} }
public static CommandProcessingResult commandOnlyResult(final Long commandId) { public static CommandProcessingResult commandOnlyResult(final Long commandId) {
@@ -54,10 +58,18 @@ public class CommandProcessingResult {
} }
public static CommandProcessingResult empty() { public static CommandProcessingResult empty() {
return new CommandProcessingResult(Long.valueOf(-1), Long.valueOf(-1), Long.valueOf(-1), null); return new CommandProcessingResult(null, null, null, null);
} }
/*
* Deprecated
*/
public CommandProcessingResult(final Long entityId) { public CommandProcessingResult(final Long entityId) {
if (entityId != null) {
this.resourceIdentifier = entityId.toString();
} else {
this.resourceIdentifier = null;
}
this.resourceId = entityId; this.resourceId = entityId;
this.officeId = null; this.officeId = null;
this.groupId = null; this.groupId = null;
@@ -68,18 +80,24 @@ public class CommandProcessingResult {
} }
private CommandProcessingResult(final Long commandId, final Long officeId, final Long groupId, final Long clientId, final Long loanId, private CommandProcessingResult(final Long commandId, final Long officeId, final Long groupId, final Long clientId, final Long loanId,
final Long resourceId, final String transactionId, final Map<String, Object> changesOnly) { final String resourceIdentifier, final Long resourceId, final String transactionId, final Map<String, Object> changesOnly) {
this.commandId = commandId; this.commandId = commandId;
this.officeId = officeId; this.officeId = officeId;
this.groupId = groupId; this.groupId = groupId;
this.clientId = clientId; this.clientId = clientId;
this.loanId = loanId; this.loanId = loanId;
this.resourceIdentifier = resourceIdentifier;
this.resourceId = resourceId; this.resourceId = resourceId;
this.changes = changesOnly; this.changes = changesOnly;
this.transactionId = transactionId; this.transactionId = transactionId;
} }
private CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map<String, Object> changesOnly) { private CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map<String, Object> changesOnly) {
if (resourceId != null) {
this.resourceIdentifier = resourceId.toString();
} else {
this.resourceIdentifier = null;
}
this.resourceId = resourceId; this.resourceId = resourceId;
this.officeId = officeId; this.officeId = officeId;
this.groupId = null; this.groupId = null;
@@ -102,7 +120,7 @@ public class CommandProcessingResult {
return this.officeId; return this.officeId;
} }
public void setOfficeId(Long officeId) { public void setOfficeId(final Long officeId) {
this.officeId = officeId; this.officeId = officeId;
} }
@@ -18,12 +18,14 @@ public class CommandProcessingResultBuilder {
private Long groupId; private Long groupId;
private Long clientId; private Long clientId;
private Long loanId; private Long loanId;
private String resourceIdentifier;
private Long entityId; private Long entityId;
private String transactionId; private String transactionId;
private Map<String, Object> changes; private Map<String, Object> changes;
public CommandProcessingResult build() { public CommandProcessingResult build() {
return CommandProcessingResult.fromDetails(commandId, officeId, groupId, clientId, loanId, entityId, transactionId, changes); return CommandProcessingResult.fromDetails(commandId, officeId, groupId, clientId, loanId, resourceIdentifier, entityId,
transactionId, changes);
} }
public CommandProcessingResultBuilder withCommandId(final Long withCommandId) { public CommandProcessingResultBuilder withCommandId(final Long withCommandId) {
@@ -36,6 +38,11 @@ public class CommandProcessingResultBuilder {
return this; return this;
} }
public CommandProcessingResultBuilder withResourceIdAsString(final String withResourceIdentifier) {
this.resourceIdentifier = withResourceIdentifier;
return this;
}
public CommandProcessingResultBuilder withEntityId(final Long withEntityId) { public CommandProcessingResultBuilder withEntityId(final Long withEntityId) {
this.entityId = withEntityId; this.entityId = withEntityId;
return this; return this;
@@ -25,6 +25,7 @@ import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.dataqueries.data.DatatableData; import org.mifosplatform.infrastructure.dataqueries.data.DatatableData;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData; import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
@@ -38,7 +39,7 @@ import org.springframework.stereotype.Component;
@Path("/datatables") @Path("/datatables")
@Component @Component
@Scope("singleton") @Scope("singleton")
public class DataTableApiResource { public class DatatablesApiResource {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final GenericDataService genericDataService; private final GenericDataService genericDataService;
@@ -47,7 +48,7 @@ public class DataTableApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired @Autowired
public DataTableApiResource(final PlatformSecurityContext context, final GenericDataService genericDataService, public DatatablesApiResource(final PlatformSecurityContext context, final GenericDataService genericDataService,
final ReadWriteNonCoreDataService readWriteNonCoreDataService, final ReadWriteNonCoreDataService readWriteNonCoreDataService,
final ToApiJsonSerializer<GenericResultsetData> toApiJsonSerializer, final ToApiJsonSerializer<GenericResultsetData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
@@ -77,7 +78,9 @@ public class DataTableApiResource {
this.readWriteNonCoreDataService.registerDatatable(datatable, apptable); this.readWriteNonCoreDataService.registerDatatable(datatable, apptable);
return this.toApiJsonSerializer.serialize(CommandProcessingResult.empty()); final CommandProcessingResult result = new CommandProcessingResultBuilder().withResourceIdAsString(datatable).build();
return this.toApiJsonSerializer.serialize(result);
} }
@POST @POST
@@ -88,7 +91,9 @@ public class DataTableApiResource {
this.readWriteNonCoreDataService.deregisterDatatable(datatable); this.readWriteNonCoreDataService.deregisterDatatable(datatable);
return this.toApiJsonSerializer.serialize(CommandProcessingResult.empty()); final CommandProcessingResult result = new CommandProcessingResultBuilder().withResourceIdAsString(datatable).build();
return this.toApiJsonSerializer.serialize(result);
} }
@GET @GET
@@ -143,7 +148,7 @@ public class DataTableApiResource {
@Path("{datatable}/{apptableId}") @Path("{datatable}/{apptableId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String newDatatableEntry(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId, public String createDatatableEntry(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId,
final String apiRequestBodyAsJson) { final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createDatatable(datatable, apptableId, null) final CommandWrapper commandRequest = new CommandWrapperBuilder().createDatatable(datatable, apptableId, null)
@@ -10,15 +10,13 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
/** /**
* A {@link RuntimeException} thrown when client resources are not found. * A {@link RuntimeException} thrown when client resources are not found.
*/ */
public class DataTableNotFoundException extends AbstractPlatformResourceNotFoundException { public class DatatableNotFoundException extends AbstractPlatformResourceNotFoundException {
public DataTableNotFoundException(final String datatable, Long id) { public DatatableNotFoundException(final String datatable, final Long id) {
super("error.msg.datatable.data.not.found", "Data Not Found for Data Table: ", super("error.msg.datatable.data.not.found", "Data not found for datatable: ", datatable + " Id:" + id);
datatable + " Id:" + id); }
}
public DatatableNotFoundException(final String datatable) {
public DataTableNotFoundException(final String datatable) { super("error.msg.datatable.not.found", "Datatable not found.", datatable);
super("error.msg.datatable.not.found", "Data Table Not Found", }
datatable);
}
} }
@@ -13,6 +13,6 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
public class ReportNotFoundException extends AbstractPlatformResourceNotFoundException { public class ReportNotFoundException extends AbstractPlatformResourceNotFoundException {
public ReportNotFoundException(final String reportSql) { public ReportNotFoundException(final String reportSql) {
super("error.msg.report.name.not.found", "Reporting Meta Data Entry Not Found", "Input Sql: " + reportSql); super("error.msg.report.name.not.found", "Reporting meta-data entry not found.", "Input sql: " + reportSql);
} }
} }
@@ -12,7 +12,6 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder
import org.mifosplatform.infrastructure.dataqueries.service.ReadWriteNonCoreDataService; import org.mifosplatform.infrastructure.dataqueries.service.ReadWriteNonCoreDataService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class CreateDatatableEntryCommandHandler implements NewCommandSourceHandler { public class CreateDatatableEntryCommandHandler implements NewCommandSourceHandler {
@@ -24,11 +23,10 @@ public class CreateDatatableEntryCommandHandler implements NewCommandSourceHandl
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { public CommandProcessingResult processCommand(final JsonCommand command) {
this.writePlatformService.newDatatableEntry(command.entityName(), command.getApptableId(), command); this.writePlatformService.createNewDatatableEntry(command.entityName(), command.getApptableId(), command);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
@@ -20,7 +20,5 @@ public interface GenericDataService {
String wrapSQL(String sql); String wrapSQL(String sql);
String getDatabaseName();
List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(String datatable); List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(String datatable);
} }
@@ -5,20 +5,18 @@
*/ */
package org.mifosplatform.infrastructure.dataqueries.service; package org.mifosplatform.infrastructure.dataqueries.service;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData; import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData; import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnValueData; import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnValueData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData; import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
import org.mifosplatform.infrastructure.dataqueries.exception.DataTableNotFoundException; import org.mifosplatform.infrastructure.dataqueries.exception.DatatableNotFoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.jdbc.support.rowset.SqlRowSet;
@@ -168,15 +166,6 @@ public class GenericDataServiceImpl implements GenericDataService {
} }
@Override
public String getDatabaseName() {
try {
return dataSource.getConnection().getCatalog();
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), "Error Accessing Database Name");
}
}
@Override @Override
public List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(final String datatable) { public List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(final String datatable) {
@@ -249,6 +238,6 @@ public class GenericDataServiceImpl implements GenericDataService {
final SqlRowSet columnDefinitions = this.jdbcTemplate.queryForRowSet(sql); final SqlRowSet columnDefinitions = this.jdbcTemplate.queryForRowSet(sql);
if (columnDefinitions.next()) { return columnDefinitions; } if (columnDefinitions.next()) { return columnDefinitions; }
throw new DataTableNotFoundException(datatable); throw new DatatableNotFoundException(datatable);
} }
} }
@@ -25,7 +25,7 @@ public interface ReadWriteNonCoreDataService {
GenericResultsetData retrieveDataTableGenericResultSet(String datatable, Long appTableId, String order, Long id); GenericResultsetData retrieveDataTableGenericResultSet(String datatable, Long appTableId, String order, Long id);
void newDatatableEntry(String datatable, Long appTableId, JsonCommand command); void createNewDatatableEntry(String datatable, Long appTableId, JsonCommand command);
Map<String, Object> updateDatatableEntryOneToOne(String datatable, Long appTableId, JsonCommand command); Map<String, Object> updateDatatableEntryOneToOne(String datatable, Long appTableId, JsonCommand command);
@@ -17,6 +17,7 @@ import java.util.Set;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
@@ -29,12 +30,14 @@ import org.mifosplatform.infrastructure.dataqueries.data.DatatableData;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData; import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData; import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData; import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
import org.mifosplatform.infrastructure.dataqueries.exception.DataTableNotFoundException; import org.mifosplatform.infrastructure.dataqueries.exception.DatatableNotFoundException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData; import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
@@ -96,63 +99,89 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
return datatables; return datatables;
} }
private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
logger.error(dve.getMessage(), dve);
}
@Override @Override
public void registerDatatable(final String datatable, final String appTable) { public void registerDatatable(final String dataTableName, final String applicationTableName) {
validateAppTable(appTable); // FIXME - KW - hardcoded supported app tables are m_loan or m_client?
validateAppTable(applicationTableName);
final String createPermission = "'CREATE_" + datatable + "'"; try {
final String createPermissionChecker = "'CREATE_" + datatable + "_CHECKER'"; // TODO - JW - put in batch command later
final String readPermission = "'READ_" + datatable + "'"; final String registerDatatableSql = "insert into x_registered_table (registered_table_name, application_table_name) values ('"
final String updatePermission = "'UPDATE_" + datatable + "'"; + dataTableName + "', '" + applicationTableName + "')";
final String updatePermissionChecker = "'UPDATE_" + datatable + "_CHECKER'";
final String deletePermission = "'DELETE_" + datatable + "'";
final String deletePermissionChecker = "'DELETE_" + datatable + "_CHECKER'";
// TODO - JW - put in batch command later this.jdbcTemplate.update(registerDatatableSql);
final String registerDatatableSql = "insert into x_registered_table (registered_table_name, application_table_name) values ('" } catch (DataIntegrityViolationException dve) {
+ datatable + "', '" + appTable + "')"; Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException(
"error.msg.datatable.registered", "Datatable `" + dataTableName
+ "` is already registered against an application table.", "dataTableName", dataTableName); }
this.jdbcTemplate.update(registerDatatableSql); logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
final String permissionsSql = "insert into m_permission (grouping, code, action_name, entity_name, can_maker_checker) values " try {
+ "('datatable', " final String createPermission = "'CREATE_" + dataTableName + "'";
+ createPermission final String createPermissionChecker = "'CREATE_" + dataTableName + "_CHECKER'";
+ ", 'CREATE', '" final String readPermission = "'READ_" + dataTableName + "'";
+ datatable final String updatePermission = "'UPDATE_" + dataTableName + "'";
+ "', true)," final String updatePermissionChecker = "'UPDATE_" + dataTableName + "_CHECKER'";
+ "('datatable', " final String deletePermission = "'DELETE_" + dataTableName + "'";
+ createPermissionChecker final String deletePermissionChecker = "'DELETE_" + dataTableName + "_CHECKER'";
+ ", 'CREATE', '"
+ datatable
+ "', false),"
+ "('datatable', "
+ readPermission
+ ", 'READ', '"
+ datatable
+ "', false),"
+ "('datatable', "
+ updatePermission
+ ", 'UPDATE', '"
+ datatable
+ "', true),"
+ "('datatable', "
+ updatePermissionChecker
+ ", 'UPDATE', '"
+ datatable
+ "', false),"
+ "('datatable', "
+ deletePermission
+ ", 'DELETE', '"
+ datatable
+ "', true),"
+ "('datatable', "
+ deletePermissionChecker
+ ", 'DELETE', '"
+ datatable
+ "', false)";
this.jdbcTemplate.update(permissionsSql); final String permissionsSql = "insert into m_permission (grouping, code, action_name, entity_name, can_maker_checker) values "
+ "('datatable', "
+ createPermission
+ ", 'CREATE', '"
+ dataTableName
+ "', true),"
+ "('datatable', "
+ createPermissionChecker
+ ", 'CREATE', '"
+ dataTableName
+ "', false),"
+ "('datatable', "
+ readPermission
+ ", 'READ', '"
+ dataTableName
+ "', false),"
+ "('datatable', "
+ updatePermission
+ ", 'UPDATE', '"
+ dataTableName
+ "', true),"
+ "('datatable', "
+ updatePermissionChecker
+ ", 'UPDATE', '"
+ dataTableName
+ "', false),"
+ "('datatable', "
+ deletePermission
+ ", 'DELETE', '"
+ dataTableName
+ "', true),"
+ "('datatable', "
+ deletePermissionChecker
+ ", 'DELETE', '"
+ dataTableName + "', false)";
this.jdbcTemplate.update(permissionsSql);
} catch (DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException(
"error.msg.permissions.datatable.duplicate", "Permissions for datatable `" + dataTableName + "` already exist.",
"dataTableName", dataTableName); }
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
} }
@Override @Override
@@ -161,29 +190,53 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final String permissionList = "('CREATE_" + datatable + "', 'CREATE_" + datatable + "_CHECKER', 'READ_" + datatable + "', 'UPDATE_" final String permissionList = "('CREATE_" + datatable + "', 'CREATE_" + datatable + "_CHECKER', 'READ_" + datatable + "', 'UPDATE_"
+ datatable + "', 'UPDATE_" + datatable + "_CHECKER', 'DELETE_" + datatable + "', 'DELETE_" + datatable + "_CHECKER')"; + datatable + "', 'UPDATE_" + datatable + "_CHECKER', 'DELETE_" + datatable + "', 'DELETE_" + datatable + "_CHECKER')";
String sql = "delete from m_role_permission where m_role_permission.permission_id in (select id from m_permission where code in " final String deleteRolePermissionsSql = "delete from m_role_permission where m_role_permission.permission_id in (select id from m_permission where code in "
+ permissionList + ")"; + permissionList + ")";
this.jdbcTemplate.update(sql); this.jdbcTemplate.update(deleteRolePermissionsSql);
sql = "delete from m_permission where code in " + permissionList; final String deletePermissionsSql = "delete from m_permission where code in " + permissionList;
this.jdbcTemplate.update(sql); this.jdbcTemplate.update(deletePermissionsSql);
sql = "delete from x_registered_table where registered_table_name = '" + datatable + "'"; final String deleteRegisteredDatatableSql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
this.jdbcTemplate.update(sql); this.jdbcTemplate.update(deleteRegisteredDatatableSql);
} }
@Override @Override
public void newDatatableEntry(final String datatable, final Long appTableId, final JsonCommand command) { public void createNewDatatableEntry(final String dataTableName, final Long appTableId, final JsonCommand command) {
String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(datatable); try {
final String appTable = getWithinScopeApplicationTableName(dataTableName, appTableId);
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType(); final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(dataTableName);
final Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
final String sql = getAddSql(columnHeaders, datatable, getFKField(appTable), appTableId, dataParams); final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
final Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
this.jdbcTemplate.update(sql); final String sql = getAddSql(columnHeaders, dataTableName, getFKField(appTable), appTableId, dataParams);
this.jdbcTemplate.update(sql);
} catch (ConstraintViolationException dve) {
// NOTE: jdbctemplate throws a
// org.hibernate.exception.ConstraintViolationException even though
// it should be a DataAccessException?
Throwable realCause = dve.getCause();
if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException(
"error.msg.datatable.entry.duplicate", "An entry already exists for datatable `" + dataTableName
+ "` and application table with identifier `" + appTableId + "`.", "dataTableName", dataTableName, appTableId); }
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
} catch (DataAccessException dve) {
Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException(
"error.msg.datatable.entry.duplicate", "An entry already exists for datatable `" + dataTableName
+ "` and application table with identifier `" + appTableId + "`.", "dataTableName", dataTableName, appTableId); }
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
} }
@Override @Override
@@ -191,7 +244,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, null); GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, null);
if (grs.getData().size() == 0) throw new DataTableNotFoundException(datatable, appTableId); if (grs.getData().size() == 0) throw new DatatableNotFoundException(datatable, appTableId);
if (grs.getData().size() > 1) if (grs.getData().size() > 1)
throw new PlatformDataIntegrityException("error.msg.attempting.multiple.update", "Application Table: " + datatable throw new PlatformDataIntegrityException("error.msg.attempting.multiple.update", "Application Table: " + datatable
@@ -223,7 +276,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, datatableId); final GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, datatableId);
if (grs.getData().size() == 0) throw new DataTableNotFoundException(datatable, appTableId); if (grs.getData().size() == 0) throw new DatatableNotFoundException(datatable, appTableId);
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType(); final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
final Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json()); final Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
@@ -296,7 +349,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql); final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
if (!rs.next()) { throw new DataTableNotFoundException(appTable, appTableId); } if (!rs.next()) { throw new DatatableNotFoundException(appTable, appTableId); }
} }
private String dataScopedSQL(final String unscopedSQL, final String appTable) { private String dataScopedSQL(final String unscopedSQL, final String appTable) {
@@ -374,7 +427,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (rs.next()) { if (rs.next()) {
applicationTableName = rs.getString("application_table_name"); applicationTableName = rs.getString("application_table_name");
} else { } else {
throw new DataTableNotFoundException(datatable); throw new DatatableNotFoundException(datatable);
} }
return applicationTableName; return applicationTableName;