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
@@ -19,13 +19,17 @@ public class CommandProcessingResult {
private final Long groupId;
private final Long clientId;
private final Long loanId;
private Long resourceId;
private final Long resourceId;
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,
final Long loanId, final Long entityId, final String transactionId, final Map<String, Object> changes) {
return new CommandProcessingResult(commandId, officeId, groupId, clientId, loanId, entityId, transactionId, changes);
final Long loanId, final String resourceIdentifier, final Long entityId, final String transactionId,
final Map<String, Object> changes) {
return new CommandProcessingResult(commandId, officeId, groupId, clientId, loanId, resourceIdentifier, entityId, transactionId,
changes);
}
public static CommandProcessingResult commandOnlyResult(final Long commandId) {
@@ -54,10 +58,18 @@ public class CommandProcessingResult {
}
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) {
if (entityId != null) {
this.resourceIdentifier = entityId.toString();
} else {
this.resourceIdentifier = null;
}
this.resourceId = entityId;
this.officeId = 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,
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.officeId = officeId;
this.groupId = groupId;
this.clientId = clientId;
this.loanId = loanId;
this.resourceIdentifier = resourceIdentifier;
this.resourceId = resourceId;
this.changes = changesOnly;
this.transactionId = transactionId;
}
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.officeId = officeId;
this.groupId = null;
@@ -102,7 +120,7 @@ public class CommandProcessingResult {
return this.officeId;
}
public void setOfficeId(Long officeId) {
public void setOfficeId(final Long officeId) {
this.officeId = officeId;
}
@@ -18,12 +18,14 @@ public class CommandProcessingResultBuilder {
private Long groupId;
private Long clientId;
private Long loanId;
private String resourceIdentifier;
private Long entityId;
private String transactionId;
private Map<String, Object> changes;
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) {
@@ -36,6 +38,11 @@ public class CommandProcessingResultBuilder {
return this;
}
public CommandProcessingResultBuilder withResourceIdAsString(final String withResourceIdentifier) {
this.resourceIdentifier = withResourceIdentifier;
return this;
}
public CommandProcessingResultBuilder withEntityId(final Long withEntityId) {
this.entityId = withEntityId;
return this;
@@ -25,6 +25,7 @@ import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
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.dataqueries.data.DatatableData;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
@@ -38,7 +39,7 @@ import org.springframework.stereotype.Component;
@Path("/datatables")
@Component
@Scope("singleton")
public class DataTableApiResource {
public class DatatablesApiResource {
private final PlatformSecurityContext context;
private final GenericDataService genericDataService;
@@ -47,7 +48,7 @@ public class DataTableApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public DataTableApiResource(final PlatformSecurityContext context, final GenericDataService genericDataService,
public DatatablesApiResource(final PlatformSecurityContext context, final GenericDataService genericDataService,
final ReadWriteNonCoreDataService readWriteNonCoreDataService,
final ToApiJsonSerializer<GenericResultsetData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
@@ -77,7 +78,9 @@ public class DataTableApiResource {
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
@@ -88,7 +91,9 @@ public class DataTableApiResource {
this.readWriteNonCoreDataService.deregisterDatatable(datatable);
return this.toApiJsonSerializer.serialize(CommandProcessingResult.empty());
final CommandProcessingResult result = new CommandProcessingResultBuilder().withResourceIdAsString(datatable).build();
return this.toApiJsonSerializer.serialize(result);
}
@GET
@@ -143,7 +148,7 @@ public class DataTableApiResource {
@Path("{datatable}/{apptableId}")
@Consumes({ 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 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.
*/
public class DataTableNotFoundException extends AbstractPlatformResourceNotFoundException {
public class DatatableNotFoundException extends AbstractPlatformResourceNotFoundException {
public DataTableNotFoundException(final String datatable, Long id) {
super("error.msg.datatable.data.not.found", "Data Not Found for Data Table: ",
datatable + " Id:" + id);
}
public DataTableNotFoundException(final String datatable) {
super("error.msg.datatable.not.found", "Data Table Not Found",
datatable);
}
public DatatableNotFoundException(final String datatable, final Long id) {
super("error.msg.datatable.data.not.found", "Data not found for datatable: ", datatable + " Id:" + id);
}
public DatatableNotFoundException(final String datatable) {
super("error.msg.datatable.not.found", "Datatable not found.", datatable);
}
}
@@ -13,6 +13,6 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
public class ReportNotFoundException extends AbstractPlatformResourceNotFoundException {
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CreateDatatableEntryCommandHandler implements NewCommandSourceHandler {
@@ -24,11 +23,10 @@ public class CreateDatatableEntryCommandHandler implements NewCommandSourceHandl
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
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() //
.withCommandId(command.commandId()) //
@@ -20,7 +20,5 @@ public interface GenericDataService {
String wrapSQL(String sql);
String getDatabaseName();
List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(String datatable);
}
@@ -5,20 +5,18 @@
*/
package org.mifosplatform.infrastructure.dataqueries.service;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnValueData;
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.jdbc.core.JdbcTemplate;
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
public List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(final String datatable) {
@@ -249,6 +238,6 @@ public class GenericDataServiceImpl implements GenericDataService {
final SqlRowSet columnDefinitions = this.jdbcTemplate.queryForRowSet(sql);
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);
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);
@@ -17,6 +17,7 @@ import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
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.ResultsetColumnHeaderData;
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.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
@@ -96,63 +99,89 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
return datatables;
}
private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
logger.error(dve.getMessage(), dve);
}
@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 + "'";
final String createPermissionChecker = "'CREATE_" + datatable + "_CHECKER'";
final String readPermission = "'READ_" + datatable + "'";
final String updatePermission = "'UPDATE_" + datatable + "'";
final String updatePermissionChecker = "'UPDATE_" + datatable + "_CHECKER'";
final String deletePermission = "'DELETE_" + datatable + "'";
final String deletePermissionChecker = "'DELETE_" + datatable + "_CHECKER'";
try {
// TODO - JW - put in batch command later
final String registerDatatableSql = "insert into x_registered_table (registered_table_name, application_table_name) values ('"
+ dataTableName + "', '" + applicationTableName + "')";
// TODO - JW - put in batch command later
final String registerDatatableSql = "insert into x_registered_table (registered_table_name, application_table_name) values ('"
+ datatable + "', '" + appTable + "')";
this.jdbcTemplate.update(registerDatatableSql);
} catch (DataIntegrityViolationException dve) {
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 "
+ "('datatable', "
+ createPermission
+ ", 'CREATE', '"
+ datatable
+ "', true),"
+ "('datatable', "
+ createPermissionChecker
+ ", '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)";
try {
final String createPermission = "'CREATE_" + dataTableName + "'";
final String createPermissionChecker = "'CREATE_" + dataTableName + "_CHECKER'";
final String readPermission = "'READ_" + dataTableName + "'";
final String updatePermission = "'UPDATE_" + dataTableName + "'";
final String updatePermissionChecker = "'UPDATE_" + dataTableName + "_CHECKER'";
final String deletePermission = "'DELETE_" + dataTableName + "'";
final String deletePermissionChecker = "'DELETE_" + dataTableName + "_CHECKER'";
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
@@ -161,29 +190,53 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final String permissionList = "('CREATE_" + datatable + "', 'CREATE_" + datatable + "_CHECKER', 'READ_" + datatable + "', 'UPDATE_"
+ 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 + ")";
this.jdbcTemplate.update(sql);
this.jdbcTemplate.update(deleteRolePermissionsSql);
sql = "delete from m_permission where code in " + permissionList;
this.jdbcTemplate.update(sql);
final String deletePermissionsSql = "delete from m_permission where code in " + permissionList;
this.jdbcTemplate.update(deletePermissionsSql);
sql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
this.jdbcTemplate.update(sql);
final String deleteRegisteredDatatableSql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
this.jdbcTemplate.update(deleteRegisteredDatatableSql);
}
@Override
public void newDatatableEntry(final String datatable, final Long appTableId, final JsonCommand command) {
String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
public void createNewDatatableEntry(final String dataTableName, final Long appTableId, final JsonCommand command) {
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 Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(dataTableName);
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
@@ -191,7 +244,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
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)
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);
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 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);
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) {
@@ -374,7 +427,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (rs.next()) {
applicationTableName = rs.getString("application_table_name");
} else {
throw new DataTableNotFoundException(datatable);
throw new DatatableNotFoundException(datatable);
}
return applicationTableName;