diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/CodesApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/CodesApiResource.java new file mode 100644 index 000000000..64b22c278 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/CodesApiResource.java @@ -0,0 +1,81 @@ +package org.mifosng.platform.api; + +import java.util.Collection; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.mifosng.platform.api.commands.CodeCommand; +import org.mifosng.platform.api.data.CodeData; +import org.mifosng.platform.api.data.EntityIdentifier; +import org.mifosng.platform.api.infrastructure.ApiDataConversionService; +import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService; +import org.mifosng.platform.api.infrastructure.ApiParameterHelper; +import org.mifosng.platform.organisation.service.CodeWritePlatformService; +import org.mifosng.platform.organisation.service.CodeReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/codes") +@Component +@Scope("singleton") +public class CodesApiResource { + + @Autowired + private CodeReadPlatformService readPlatformService; + + @Autowired + private CodeWritePlatformService writePlatformService; + + @Autowired + private ApiDataConversionService apiDataConversionService; + + @Autowired + private ApiJsonSerializerService apiJsonSerializerService; + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveCodes(@Context final UriInfo uriInfo) { + + Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); + boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + + final Collection codes = this.readPlatformService.retrieveAllCodes(); + + return this.apiJsonSerializerService.serializeCodeDataToJson(prettyPrint, responseParameters, codes); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response createCode(final String jsonRequestBody) { + + final CodeCommand command = this.apiDataConversionService.convertJsonToCodeCommand(null, jsonRequestBody); + + final Long id = this.writePlatformService.createCode(command); + + return Response.ok().entity(new EntityIdentifier(id)).build(); + } + + + @PUT + @Path("{Id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateCode(@PathParam("Id") final Long Id, final String jsonRequestBody) { + + return null; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/CodeCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/CodeCommand.java new file mode 100644 index 000000000..ed9537a81 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/CodeCommand.java @@ -0,0 +1,33 @@ +package org.mifosng.platform.api.commands; + +import java.util.Set; + +/** + * Immutable command for creating or updating details of a Code in mifos. + */ +public class CodeCommand { + + private final Long id; + private final String codeName; + + private final Set modifiedParameters; + + public CodeCommand(final Set modifiedParameters, final Long id, final String codeName) { + this.modifiedParameters = modifiedParameters; + this.id = id; + this.codeName = codeName; + } + + public Long getId() { + return id; + } + + public String getCodeName() { + return codeName; + } + + public boolean isNameChanged() { + return this.modifiedParameters.contains("codename"); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/CodeData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/CodeData.java new file mode 100644 index 000000000..cff3209eb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/CodeData.java @@ -0,0 +1,35 @@ +package org.mifosng.platform.api.data; + +import java.io.Serializable; + +public class CodeData implements Serializable { + + private Long id; + private String codeName; + + public CodeData() { + // + } + + public CodeData(final Long id, final String codeName) { + this.id = id; + this.codeName = codeName; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCodeName() { + return codeName; + } + + public void setName(String codeName) { + this.codeName = codeName; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java index d29e696d1..c9aa6a00a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java @@ -5,6 +5,7 @@ import org.mifosng.platform.api.commands.BranchMoneyTransferCommand; import org.mifosng.platform.api.commands.ChargeCommand; import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.ClientIdentifierCommand; +import org.mifosng.platform.api.commands.CodeCommand; import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountWithdrawInterestCommand; import org.mifosng.platform.api.commands.DepositAccountWithdrawalCommand; @@ -78,4 +79,7 @@ public interface ApiDataConversionService { DepositAccountWithdrawInterestCommand convertJsonToDepositAccountWithdrawInterestCommand(Long resourceIdentifier, String jsonRequestBody); ClientIdentifierCommand convertJsonToClientIdentifierCommand(Long resourceIdentifier, Long clientId, String jsonRequestBody); + + CodeCommand convertJsonToCodeCommand(Long resourceIdentifier, String json); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 008456f37..be73654f8 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -22,6 +22,7 @@ import org.mifosng.platform.api.commands.BranchMoneyTransferCommand; import org.mifosng.platform.api.commands.ChargeCommand; import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.ClientIdentifierCommand; +import org.mifosng.platform.api.commands.CodeCommand; import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountWithdrawInterestCommand; import org.mifosng.platform.api.commands.DepositAccountWithdrawalCommand; @@ -1476,5 +1477,33 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { return new ClientIdentifierCommand(modifiedParameters,resourceIdentifier,clientId,documentTypeId,documentKey,documentDescription); } + + @Override + public CodeCommand convertJsonToCodeCommand(Long resourceIdentifier,String json) { + // TODO Auto-generated method stub + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + + Set supportedParams = new HashSet( + Arrays.asList("name") + ); + + checkForUnsupportedParameters(requestMap, supportedParams); + + Set modifiedParameters = new HashSet(); + + String name = extractStringParameter("name", requestMap, modifiedParameters); + + return new CodeCommand(modifiedParameters, resourceIdentifier, name); + + + } + + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java index cd484c5c9..b1b05fa43 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java @@ -11,6 +11,7 @@ import org.mifosng.platform.api.data.ChargeData; import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData; import org.mifosng.platform.api.data.ClientData; import org.mifosng.platform.api.data.ClientIdentifierData; +import org.mifosng.platform.api.data.CodeData; import org.mifosng.platform.api.data.ConfigurationData; import org.mifosng.platform.api.data.DatatableData; import org.mifosng.platform.api.data.DepositAccountData; @@ -33,6 +34,8 @@ import org.mifosng.platform.api.data.RoleData; import org.mifosng.platform.api.data.SavingProductData; import org.mifosng.platform.api.data.StaffData; +import com.google.gson.Gson; + public interface ApiJsonSerializerService { String serializeAuthenticatedUserDataToJson(boolean prettyPrint, AuthenticatedUserData authenticatedUserData); @@ -125,4 +128,8 @@ public interface ApiJsonSerializerService { String serializeDocumentDataToJson(boolean prettyPrint, Set responseParameters, DocumentData documentData); + String serializeCodeDataToJson(boolean prettyPrint,Set responseParameters, Collection codes); + + String serializeCodeDataToJson(boolean prettyPrint,Set responseParameters, CodeData code); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java index e0e5606b6..1e80edc46 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java @@ -13,6 +13,7 @@ import org.mifosng.platform.api.data.ChargeData; import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData; import org.mifosng.platform.api.data.ClientData; import org.mifosng.platform.api.data.ClientIdentifierData; +import org.mifosng.platform.api.data.CodeData; import org.mifosng.platform.api.data.ConfigurationData; import org.mifosng.platform.api.data.DatatableData; import org.mifosng.platform.api.data.DepositAccountData; @@ -194,6 +195,9 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ private static final Set LOAN_CHARGES_DATA_PARAMETERS = new HashSet( Arrays.asList("chargeOptions")); + + private static final Set CODE_DATA_PARAMETERS = new HashSet( + Arrays.asList("id", "codename")); private final GoogleGsonSerializerHelper helper; @@ -662,4 +666,24 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ DOCUMENT_DATA_PARAMETERS, prettyPrint, responseParameters); return helper.serializedJsonFrom(gsonDeserializer, documentData); } + + @Override + public String serializeCodeDataToJson(boolean prettyPrint, + Set responseParameters, Collection codes) { + final Gson gsonDeserializer = helper + .createGsonBuilderWithParameterExclusionSerializationStrategy( + CODE_DATA_PARAMETERS, prettyPrint, responseParameters); + return helper.serializedJsonFrom(gsonDeserializer, + codes.toArray(new CodeData[codes.size()])); + } + + @Override + public String serializeCodeDataToJson(boolean prettyPrint, + Set responseParameters, CodeData code) { + final Gson gsonDeserializer = helper + .createGsonBuilderWithParameterExclusionSerializationStrategy( + CODE_DATA_PARAMETERS, prettyPrint, responseParameters); + return helper.serializedJsonFrom(gsonDeserializer, code); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/CodeRepository.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/CodeRepository.java index ff70ef8b8..c6f841cc7 100755 --- a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/CodeRepository.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/CodeRepository.java @@ -3,7 +3,7 @@ package org.mifosng.platform.organisation.domain; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface CodeRepository extends JpaRepository, +public interface CodeRepository extends JpaRepository, JpaSpecificationExecutor { // no added behaviour } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeReadPlatformService.java new file mode 100644 index 000000000..f3b6c78b7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeReadPlatformService.java @@ -0,0 +1,11 @@ +package org.mifosng.platform.organisation.service; + +import java.util.Collection; + +import org.mifosng.platform.api.data.CodeData; + +public interface CodeReadPlatformService { + Collection retrieveAllCodes(); + + CodeData retrieveCode(Long codeId); +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeReadPlatformServiceImpl.java new file mode 100644 index 000000000..15db7828b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeReadPlatformServiceImpl.java @@ -0,0 +1,77 @@ +package org.mifosng.platform.organisation.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.mifosng.platform.api.data.CodeData; +import org.mifosng.platform.api.data.FundData; +import org.mifosng.platform.exceptions.FundNotFoundException; +import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource; +import org.mifosng.platform.security.PlatformSecurityContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class CodeReadPlatformServiceImpl implements CodeReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PlatformSecurityContext context; + + @Autowired + public CodeReadPlatformServiceImpl(final PlatformSecurityContext context, + final TenantAwareRoutingDataSource dataSource) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class CodeMapper implements RowMapper { + public String schema() { + return " c.id as id, c.code_name as code_name from m_code c "; + } + + @Override + public CodeData mapRow(final ResultSet rs, final int rowNum) + throws SQLException { + + Long id = rs.getLong("id"); + String code_name = rs.getString("code_name"); + + return new CodeData(id, code_name); + } + } + + @Override + public Collection retrieveAllCodes() + { + context.authenticatedUser(); + + CodeMapper rm = new CodeMapper(); + String sql = "select " + + rm.schema() + + " order by c.code_name"; + + return this.jdbcTemplate.query(sql,rm,new Object[] {} ); + } + + @Override + public CodeData retrieveCode(Long codeId) + { + try { + context.authenticatedUser(); + + CodeMapper rm = new CodeMapper(); + String sql = "select " + rm.schema() + " where c.id = ?"; + + CodeData selectedCode = this.jdbcTemplate.queryForObject(sql, rm, new Object[] {codeId}); + + return selectedCode; + } catch (EmptyResultDataAccessException e) { + throw new FundNotFoundException(codeId); + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeWritePlatformService.java new file mode 100644 index 000000000..50b28d57f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeWritePlatformService.java @@ -0,0 +1,13 @@ +package org.mifosng.platform.organisation.service; + +import org.mifosng.platform.api.commands.CodeCommand; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface CodeWritePlatformService { + + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + Long createCode(final CodeCommand command); + + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + Long updateCode(final CodeCommand command); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..bc934020c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/CodeWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,70 @@ +package org.mifosng.platform.organisation.service; + +import org.mifosng.platform.api.commands.CodeCommand; +import org.mifosng.platform.api.commands.FundCommand; +import org.mifosng.platform.organisation.domain.Code; +import org.mifosng.platform.organisation.domain.CodeRepository; +import org.mifosng.platform.exceptions.FundNotFoundException; +import org.mifosng.platform.exceptions.PlatformDataIntegrityException; +import org.mifosng.platform.fund.domain.Fund; +import org.mifosng.platform.fund.domain.FundRepository; +import org.mifosng.platform.security.PlatformSecurityContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CodeWritePlatformServiceJpaRepositoryImpl implements CodeWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(CodeWritePlatformServiceJpaRepositoryImpl.class); + + private final PlatformSecurityContext context; + private final CodeRepository codeRepository; + + @Autowired + public CodeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final CodeRepository codeRepository) { + this.context = context; + this.codeRepository = codeRepository; + } + + + @Transactional + @Override + public Long createCode(final CodeCommand command) { + + try { + context.authenticatedUser(); + + + Code code = Code.createNew(command.getCodeName()); + + this.codeRepository.saveAndFlush(code); + + return code.getId(); + } catch (DataIntegrityViolationException dve) { + return Long.valueOf(-1); + } + } + + @Transactional + @Override + public Long updateCode(final CodeCommand command) { + + /** + * TODO + **/ + + return null; + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue is. + */ + private void handleCodeDataIntegrityIssues(final FundCommand command, DataIntegrityViolationException dve) { + + } + +} \ No newline at end of file