Merge pull request #1001 from rishy/Batch-API

added create and collect charges command
This commit is contained in:
Vishwas Babu A J
2014-07-16 15:39:17 -07:00
27 changed files with 2072 additions and 1515 deletions
@@ -24,9 +24,10 @@ import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification; import com.jayway.restassured.specification.ResponseSpecification;
/** /**
* Test class for {@link org.mifosplatform.batch.command.CommandStrategyProvider}. * Test class for
* This tests the response provided by commandStrategy by injecting it with * {@link org.mifosplatform.batch.command.CommandStrategyProvider}. This tests
* a {@code BatchRequest}. * the response provided by commandStrategy by injecting it with a
* {@code BatchRequest}.
* *
* @author RishabhShukla * @author RishabhShukla
* *
@@ -35,231 +36,280 @@ import com.jayway.restassured.specification.ResponseSpecification;
*/ */
public class BatchApiTest { public class BatchApiTest {
private ResponseSpecification responseSpec; private ResponseSpecification responseSpec;
private RequestSpecification requestSpec; private RequestSpecification requestSpec;
public BatchApiTest() { public BatchApiTest() {
super(); super();
} }
/** /**
* Sets up the essential settings for the TEST like contentType, expectedStatusCode. * Sets up the essential settings for the TEST like contentType,
* It uses the '@Before' annotation provided by jUnit. * expectedStatusCode. It uses the '@Before' annotation provided by jUnit.
*/ */
@Before @Before
public void setup() { public void setup() {
Utils.initializeRESTAssured(); Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
} }
/** /**
* Tests for the unimplemented command Strategies by returning 501 status code. * Tests for the unimplemented command Strategies by returning 501 status
* For a unknownRequest a statusCode 501 is returned back with response. * code. For a unknownRequest a statusCode 501 is returned back with
* * response.
* @see org.mifosplatform.batch.command.internal.UnknownCommandStrategy *
*/ * @see org.mifosplatform.batch.command.internal.UnknownCommandStrategy
@Test */
public void shouldReturnStatusNotImplementedUnknownCommand() { @Test
public void shouldReturnStatusNotImplementedUnknownCommand() {
final BatchRequest br = new BatchRequest(); final BatchRequest br = new BatchRequest();
br.setRequestId(4711L); br.setRequestId(4711L);
br.setRelativeUrl("/nirvana"); br.setRelativeUrl("/nirvana");
br.setMethod("POST"); br.setMethod("POST");
final List<BatchResponse> response = BatchHelper.postWithSingleRequest(this.requestSpec, this.responseSpec, br); final List<BatchResponse> response = BatchHelper.postWithSingleRequest(this.requestSpec, this.responseSpec, br);
//Verify that only 501 is returned as the status code // Verify that only 501 is returned as the status code
for(BatchResponse resp : response) { for (BatchResponse resp : response) {
Assert.assertEquals("Verifying Status code 501",(long) 501,(long) resp.getStatusCode()); Assert.assertEquals("Verify Status code 501", (long) 501, (long) resp.getStatusCode());
} }
} }
/** /**
* Tests for the successful response for a createClient request from createClientCommand. * Tests for the successful response for a createClient request from
* A successful response with statusCode '200' is returned back. * createClientCommand. A successful response with statusCode '200' is
* * returned back.
* @see org.mifosplatform.batch.command.internal.CreateClientCommandStrategy *
*/ * @see org.mifosplatform.batch.command.internal.CreateClientCommandStrategy
@Test */
public void shouldReturnOkStatusForCreateClientCommand() { @Test
public void shouldReturnOkStatusForCreateClientCommand() {
final BatchRequest br = BatchHelper.createClientRequest(4712L, ""); final BatchRequest br = BatchHelper.createClientRequest(4712L, "");
final List<BatchResponse> response = BatchHelper.postWithSingleRequest(this.requestSpec, this.responseSpec, br); final List<BatchResponse> response = BatchHelper.postWithSingleRequest(this.requestSpec, this.responseSpec, br);
//Verify that a 200 response is returned as the status code // Verify that a 200 response is returned as the status code
for(BatchResponse resp : response) { for (BatchResponse resp : response) {
Assert.assertEquals("Verifying Status code 200",(long) 200,(long) resp.getStatusCode()); Assert.assertEquals("Verify Status code 200", (long) 200, (long) resp.getStatusCode());
} }
} }
/** /**
* Tests for an erroneous response with statusCode '501' if transaction fails. * Tests for an erroneous response with statusCode '501' if transaction
* If Query Parameter 'enclosingTransaction' is set to 'true' and if one of the * fails. If Query Parameter 'enclosingTransaction' is set to 'true' and if
* request in BatchRequest fails then all transactions are rolled back. * one of the request in BatchRequest fails then all transactions are rolled
* * back.
* @see org.mifosplatform.batch.command.internal.CreateClientCommandStrategy *
* @see org.mifosplatform.batch.api.BatchApiResource * @see org.mifosplatform.batch.command.internal.CreateClientCommandStrategy
* @see org.mifosplatform.batch.service.BatchApiService * @see org.mifosplatform.batch.api.BatchApiResource
*/ * @see org.mifosplatform.batch.service.BatchApiService
@Test */
public void shouldRollBackAllTransactionsOnFailure() { @Test
public void shouldRollBackAllTransactionsOnFailure() {
//Create first client request // Create first client request
final BatchRequest br1 = BatchHelper.createClientRequest(4713L, "TestExtId11"); final BatchRequest br1 = BatchHelper.createClientRequest(4713L, "TestExtId11");
//Create second client request // Create second client request
final BatchRequest br2 = BatchHelper.createClientRequest(4714L, "TestExtId12"); final BatchRequest br2 = BatchHelper.createClientRequest(4714L, "TestExtId12");
//Create third client request, having same externalID as second client, hence cause of error // Create third client request, having same externalID as second client,
final BatchRequest br3 = BatchHelper.createClientRequest(4715L, "TestExtId11"); // hence cause of error
final BatchRequest br3 = BatchHelper.createClientRequest(4715L, "TestExtId11");
final List<BatchRequest> batchRequests = new ArrayList<>(); final List<BatchRequest> batchRequests = new ArrayList<>();
batchRequests.add(br1); batchRequests.add(br1);
batchRequests.add(br2); batchRequests.add(br2);
batchRequests.add(br3); batchRequests.add(br3);
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, final List<BatchResponse> response = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec,
this.responseSpec, jsonifiedRequest); jsonifiedRequest);
//Verifies that none of the client in BatchRequest is created on the server // Verifies that none of the client in BatchRequest is created on the
BatchHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, "TestExtId11"); // server
BatchHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, "TestExtId12"); BatchHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, "TestExtId11");
BatchHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, "TestExtId12");
//Asserts that all the transactions have been successfully rolled back // Asserts that all the transactions have been successfully rolled back
Assert.assertEquals(response.size(), 1); Assert.assertEquals(response.size(), 1);
Assert.assertEquals("Verifying Status code 400",(long) 400,(long) response.get(0).getStatusCode()); Assert.assertEquals("Verify Status code 400", (long) 400, (long) response.get(0).getStatusCode());
} }
/** /**
* Tests that a client information was successfully updated through updateClientCommand. * Tests that a client information was successfully updated through
* A 'changes' parameter is returned in the response after successful update of client * updateClientCommand. A 'changes' parameter is returned in the response
* information. * after successful update of client information.
* *
* @see org.mifosplatform.batch.command.internal.UpdateClientCommandStrategy * @see org.mifosplatform.batch.command.internal.UpdateClientCommandStrategy
*/ */
@Test @Test
public void shouldReflectChangesOnClientUpdate() { public void shouldReflectChangesOnClientUpdate() {
//Create a createClient Request // Create a createClient Request
final BatchRequest br1 = BatchHelper.createClientRequest(4716L, ""); final BatchRequest br1 = BatchHelper.createClientRequest(4716L, "");
//Create a clientUpdate Request // Create a clientUpdate Request
final BatchRequest br2 = BatchHelper.updateClientRequest(4717L, 4716L); final BatchRequest br2 = BatchHelper.updateClientRequest(4717L, 4716L);
final List<BatchRequest> batchRequests = new ArrayList<>(); final List<BatchRequest> batchRequests = new ArrayList<>();
batchRequests.add(br1); batchRequests.add(br1);
batchRequests.add(br2); batchRequests.add(br2);
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
this.responseSpec, jsonifiedRequest); jsonifiedRequest);
//Get the changes parameter from updateClient Response // Get the changes parameter from updateClient Response
final JsonObject changes = new FromJsonHelper().parse(response.get(1).getBody()) final JsonObject changes = new FromJsonHelper().parse(response.get(1).getBody()).getAsJsonObject().get("changes").getAsJsonObject();
.getAsJsonObject().get("changes").getAsJsonObject();
//Asserts the client information is successfully updated // Asserts the client information is successfully updated
Assert.assertEquals("Firstname updated", "TestFirstName",changes.get("firstname").getAsString()); Assert.assertEquals("Verify Firstname", "TestFirstName", changes.get("firstname").getAsString());
Assert.assertEquals("Lastname updated", "TestLastName", changes.get("lastname").getAsString()); Assert.assertEquals("Verify Lastname", "TestLastName", changes.get("lastname").getAsString());
} }
/** /**
* Tests that a ApplyLoanCommand was successfully executed and returned a 200(OK) * Tests that a ApplyLoanCommand was successfully executed and returned a
* status. It creates a new client and apply a loan to that client. This also * 200(OK) status. It creates a new client and apply a loan to that client.
* verifies the successful resolution of dependencies among two requests. * This also verifies the successful resolution of dependencies among two
* * requests.
* @see org.mifosplatform.batch.command.internal.ApplyLoanCommandStrategy *
*/ * @see org.mifosplatform.batch.command.internal.ApplyLoanCommandStrategy
@Test */
public void shouldReturnOkStatusForApplyLoanCommand(){ @Test
public void shouldReturnOkStatusForApplyLoanCommand() {
final String loanProductJSON = new LoanProductTestBuilder() // final String loanProductJSON = new LoanProductTestBuilder() //
.withPrincipal("10000000.00") // .withPrincipal("10000000.00") //
.withNumberOfRepayments("24") // .withNumberOfRepayments("24") //
.withRepaymentAfterEvery("1") // .withRepaymentAfterEvery("1") //
.withRepaymentTypeAsMonth() // .withRepaymentTypeAsMonth() //
.withinterestRatePerPeriod("2") // .withinterestRatePerPeriod("2") //
.withInterestRateFrequencyTypeAsMonths() // .withInterestRateFrequencyTypeAsMonths() //
.withAmortizationTypeAsEqualPrincipalPayment() // .withAmortizationTypeAsEqualPrincipalPayment() //
.withInterestTypeAsDecliningBalance() // .withInterestTypeAsDecliningBalance() //
.currencyDetails("0", "100").build(null); .currencyDetails("0", "100").build(null);
final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec) final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
.getLoanProductId(loanProductJSON);
//Create a createClient Request // Create a createClient Request
final BatchRequest br1 = BatchHelper.createClientRequest(4718L, ""); final BatchRequest br1 = BatchHelper.createClientRequest(4718L, "");
//Create a ApplyLoan Request // Create a ApplyLoan Request
final BatchRequest br2 = BatchHelper.applyLoanRequest(4719L, 4718L, productId); final BatchRequest br2 = BatchHelper.applyLoanRequest(4719L, 4718L, productId);
final List<BatchRequest> batchRequests = new ArrayList<>(); final List<BatchRequest> batchRequests = new ArrayList<>();
batchRequests.add(br1); batchRequests.add(br1);
batchRequests.add(br2); batchRequests.add(br2);
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
this.responseSpec, jsonifiedRequest); jsonifiedRequest);
//Get the clientId parameter from createClient Response // Get the clientId parameter from createClient Response
final JsonElement clientId = new FromJsonHelper().parse(response.get(0).getBody()) final JsonElement clientId = new FromJsonHelper().parse(response.get(0).getBody()).getAsJsonObject().get("clientId");
.getAsJsonObject().get("clientId");
Assert.assertEquals("Loan Successfully applied to client " + clientId.getAsString(), Assert.assertEquals("Verify Status Code 200" + clientId.getAsString(), 200L, (long) response.get(1).getStatusCode());
200L, (long) response.get(1).getStatusCode()); }
}
/** /**
* Tests that a new savings accounts was applied to an existing client and a * Tests that a new savings accounts was applied to an existing client and a
* 200(OK) status was returned. It first creates a new client and a savings product, * 200(OK) status was returned. It first creates a new client and a savings
* then uses the cliendId and ProductId to apply a savings account. * product, then uses the cliendId and ProductId to apply a savings account.
* *
* @see org.mifosplatform.batch.command.internal.ApplySavingsCommandStrategy * @see org.mifosplatform.batch.command.internal.ApplySavingsCommandStrategy
*/ */
@Test @Test
public void shouldReturnOkStatusForApplySavingsCommand(){ public void shouldReturnOkStatusForApplySavingsCommand() {
final SavingsProductHelper savingsProductHelper = new SavingsProductHelper(); final SavingsProductHelper savingsProductHelper = new SavingsProductHelper();
final String savingsProductJSON = savingsProductHelper // final String savingsProductJSON = savingsProductHelper //
.withInterestCompoundingPeriodTypeAsDaily() // .withInterestCompoundingPeriodTypeAsDaily() //
.withInterestPostingPeriodTypeAsMonthly() // .withInterestPostingPeriodTypeAsMonthly() //
.withInterestCalculationPeriodTypeAsDailyBalance() // .withInterestCalculationPeriodTypeAsDailyBalance() //
.withMinimumOpenningBalance("5000").build(); .withMinimumOpenningBalance("5000").build();
final Integer productId = SavingsProductHelper.createSavingsProduct(savingsProductJSON, final Integer productId = SavingsProductHelper.createSavingsProduct(savingsProductJSON, this.requestSpec, this.responseSpec);
this.requestSpec, this.responseSpec);
//Create a createClient Request // Create a createClient Request
final BatchRequest br1 = BatchHelper.createClientRequest(4720L, ""); final BatchRequest br1 = BatchHelper.createClientRequest(4720L, "");
//Create a applySavings Request // Create a applySavings Request
final BatchRequest br2 = BatchHelper.applySavingsRequest(4721L, 4720L, productId); final BatchRequest br2 = BatchHelper.applySavingsRequest(4721L, 4720L, productId);
final List<BatchRequest> batchRequests = new ArrayList<>(); final List<BatchRequest> batchRequests = new ArrayList<>();
batchRequests.add(br1); batchRequests.add(br1);
batchRequests.add(br2); batchRequests.add(br2);
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
this.responseSpec, jsonifiedRequest); jsonifiedRequest);
//Get the clientId parameter from createClient Response Assert.assertEquals("Verify Status Code 200", 200L, (long) response.get(1).getStatusCode());
final JsonElement clientId = new FromJsonHelper().parse(response.get(0).getBody()) }
.getAsJsonObject().get("clientId");
Assert.assertEquals("Savings Successfully applied to client " + clientId.getAsString(), /**
200L, (long) response.get(1).getStatusCode()); * Tests that a new charge was added to a newly created loan and charges are
} * Collected properly 200(OK) status was returned for successful responses.
* It first creates a new client and apply a loan, then creates a new charge
* for the create loan and then fetches all the applied charges
*
* @see org.mifosplatform.batch.command.internal.CollectChargesCommandStrategy
* @see org.mifosplatform.batch.command.internal.CreateChargeCommandStrategy
*/
@Test
public void shouldReturnOkStatusForCollectChargesCommand() {
final String loanProductJSON = new LoanProductTestBuilder() //
.withPrincipal("10000000.00") //
.withNumberOfRepayments("24") //
.withRepaymentAfterEvery("1") //
.withRepaymentTypeAsMonth() //
.withinterestRatePerPeriod("2") //
.withInterestRateFrequencyTypeAsMonths() //
.withAmortizationTypeAsEqualPrincipalPayment() //
.withInterestTypeAsDecliningBalance() //
.currencyDetails("0", "100").build(null);
final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
// Create a createClient Request
final BatchRequest br1 = BatchHelper.createClientRequest(4722L, "");
// Create a ApplyLoan Request
final BatchRequest br2 = BatchHelper.applyLoanRequest(4723L, 4722L, productId);
// Create a createCharge Request
final BatchRequest br3 = BatchHelper.createChargeRequest(4724L, 4723L);
// Create a Collect Charges Request
final BatchRequest br4 = BatchHelper.collectChargesRequest(4725L, 4723L);
final List<BatchRequest> batchRequests = new ArrayList<>();
batchRequests.add(br1);
batchRequests.add(br2);
batchRequests.add(br3);
batchRequests.add(br4);
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
jsonifiedRequest);
Assert.assertEquals("Verify Status Code 200 for Create Charge", 200L, (long) response.get(2).getStatusCode());
Assert.assertEquals("Verify Status Code 200 for Collect Charges", 200L, (long) response.get(3).getStatusCode());
}
} }
@@ -0,0 +1,122 @@
package org.mifosplatform.integrationtests;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse;
import org.mifosplatform.integrationtests.common.BatchHelper;
import org.mifosplatform.integrationtests.common.ClientHelper;
import org.mifosplatform.integrationtests.common.GroupHelper;
import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
/**
* Test class for testing the integration of Batch API with custom batch
* requests and various user defined workflow. Like in the case of mifos
* community-app
*
* @author Rishabh Shukla
*/
public class BatchRequestsIntegrationTest {
private ResponseSpecification responseSpec;
private RequestSpecification requestSpec;
public BatchRequestsIntegrationTest() {
super();
}
/**
* Sets up the essential settings for the TEST like contentType,
* expectedStatusCode. It uses the '@Before' annotation provided by jUnit.
*/
@Before
public void setup() {
Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
}
@Test
/**
* Tests that a loan is successfully applied to client members of a group.
* Firstly, it'll create a few new clients and then will add those clients
* to the group. Then a few loans will be created and one of those loans
* will be chosen at random and similarily a few of the created clients will
* be chosen on random. Now, the selected loan will be applied to these
* clients through Batch - API ApplyLoanCommandStrategy.
*/
public void shouldReturnOkStatusForLoansAppliedToSelectedClients() {
// Generate a random count of number of clients to be created
final Integer clientsCount = (int) Math.ceil(Math.random() * 7) + 3;
final Integer[] clientIDs = new Integer[clientsCount];
// Create a new group and get its groupId
Integer groupID = GroupHelper.createGroup(this.requestSpec, this.responseSpec, true);
// Create new clients and add those to this group
for (Integer i = 0; i < clientsCount; i++) {
clientIDs[i] = ClientHelper.createClient(this.requestSpec, this.responseSpec);
groupID = GroupHelper.associateClient(this.requestSpec, this.responseSpec, groupID.toString(), clientIDs[i].toString());
System.out.println("client " + clientIDs[i] + " has been added to the group " + groupID);
}
// Generate a random count of number of new loan products to be created
final Integer loansCount = (int) Math.ceil(Math.random() * 4) + 1;
final Integer[] loanProducts = new Integer[loansCount];
// Create new loan Products
for (Integer i = 0; i < loansCount; i++) {
final String loanProductJSON = new LoanProductTestBuilder() //
.withPrincipal(String.valueOf(10000.00 + Math.ceil(Math.random() * 1000000.00))) //
.withNumberOfRepayments(String.valueOf(2 + (int) Math.ceil(Math.random() * 36))) //
.withRepaymentAfterEvery(String.valueOf(1 + (int) Math.ceil(Math.random() * 3))) //
.withRepaymentTypeAsMonth() //
.withinterestRatePerPeriod(String.valueOf(1 + (int) Math.ceil(Math.random() * 4))) //
.withInterestRateFrequencyTypeAsMonths() //
.withAmortizationTypeAsEqualPrincipalPayment() //
.withInterestTypeAsDecliningBalance() //
.currencyDetails("0", "100").build(null);
loanProducts[i] = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
}
// Select anyone of the loan products at random
final Integer loanProductID = loanProducts[(int) Math.floor(Math.random() * (loansCount - 1))];
final List<BatchRequest> batchRequests = new ArrayList<>();
// Select a few clients from created group at random
Integer selClientsCount = (int) Math.ceil(Math.random() * clientsCount) + 2;
for (int i = 0; i < selClientsCount; i++) {
BatchRequest br = BatchHelper.applyLoanRequest((long) selClientsCount, null, loanProductID);
br.setBody(br.getBody().replace("$.clientId", String.valueOf(clientIDs[(int) Math.floor(Math.random() * (clientsCount - 1))])));
batchRequests.add(br);
}
// Send the request to Batch - API
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
jsonifiedRequest);
// Verify that each loan has been applied successfully
for (BatchResponse res : response) {
Assert.assertEquals("Verify Status Code 200", 200L, (long) res.getStatusCode());
}
}
}
@@ -13,9 +13,9 @@ import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification; import com.jayway.restassured.specification.ResponseSpecification;
/** /**
* Helper class for {@link org.mifosplatform.integrationtests.BatchApiTest}. * Helper class for {@link org.mifosplatform.integrationtests.BatchApiTest}. It
* It takes care of creation of {@code BatchRequest} list and posting this * takes care of creation of {@code BatchRequest} list and posting this list to
* list to the server. * the server.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -23,210 +23,259 @@ import com.jayway.restassured.specification.ResponseSpecification;
*/ */
public class BatchHelper { public class BatchHelper {
private static final String BATCH_API_URL = "/mifosng-provider/api/v1/batches?tenantIdentifier=default"; private static final String BATCH_API_URL = "/mifosng-provider/api/v1/batches?tenantIdentifier=default";
private static final String BATCH_API_URL_EXT = BATCH_API_URL + "&enclosingTransaction=true"; private static final String BATCH_API_URL_EXT = BATCH_API_URL + "&enclosingTransaction=true";
private BatchHelper() { private BatchHelper() {
super(); super();
}
/**
* Returns a JSON String for a list of {@code BatchRequest}s
*
* @param batchRequests
* @return JSON String of BatchRequest
*/
public static String toJsonString(final List<BatchRequest> batchRequests) {
return new Gson().toJson(batchRequests);
}
/**
* Returns the converted string response into JSON.
*
* @param json
* @return List<BatchResponse>
*/
private static List<BatchResponse> fromJsonString(final String json) {
return new Gson().fromJson(json, new TypeToken<List<BatchResponse>>(){}.getType());
} }
/**
* Returns a JSON String for a list of {@code BatchRequest}s
*
* @param batchRequests
* @return JSON String of BatchRequest
*/
public static String toJsonString(final List<BatchRequest> batchRequests) {
return new Gson().toJson(batchRequests);
}
/** /**
* Returns a list of BatchResponse with query parameter enclosing transaction set to * Returns the converted string response into JSON.
* false by posting the jsonified BatchRequest to the server. *
* * @param json
* @param requestSpec * @return List<BatchResponse>
* @param responseSpec */
* @param jsonifiedBatchRequests private static List<BatchResponse> fromJsonString(final String json) {
* @return a list of BatchResponse return new Gson().fromJson(json, new TypeToken<List<BatchResponse>>() {}.getType());
*/ }
public static List<BatchResponse> postBatchRequestsWithoutEnclosingTransaction(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final String jsonifiedBatchRequests) {
final String response = Utils.performServerPost(requestSpec, responseSpec, BATCH_API_URL, jsonifiedBatchRequests, null);
return BatchHelper.fromJsonString(response);
}
/** /**
* Returns a list of BatchResponse with query parameter enclosing transaction set to * Returns a list of BatchResponse with query parameter enclosing
* true by posting the jsonified BatchRequest to the server. * transaction set to false by posting the jsonified BatchRequest to the
* * server.
* @param requestSpec *
* @param responseSpec * @param requestSpec
* @param jsonifiedBatchRequests * @param responseSpec
* @return a list of BatchResponse * @param jsonifiedBatchRequests
*/ * @return a list of BatchResponse
public static List<BatchResponse> postBatchRequestsWithEnclosingTransaction(final RequestSpecification requestSpec, */
final ResponseSpecification responseSpec, final String jsonifiedBatchRequests) { public static List<BatchResponse> postBatchRequestsWithoutEnclosingTransaction(final RequestSpecification requestSpec,
final String response = Utils.performServerPost(requestSpec, responseSpec, BATCH_API_URL_EXT, jsonifiedBatchRequests, null); final ResponseSpecification responseSpec, final String jsonifiedBatchRequests) {
return BatchHelper.fromJsonString(response); final String response = Utils.performServerPost(requestSpec, responseSpec, BATCH_API_URL, jsonifiedBatchRequests, null);
} return BatchHelper.fromJsonString(response);
}
/** /**
* Returns a BatchResponse based on the given BatchRequest, by posting the request * Returns a list of BatchResponse with query parameter enclosing
* to the server. * transaction set to true by posting the jsonified BatchRequest to the
* server.
*
* @param requestSpec
* @param responseSpec
* @param jsonifiedBatchRequests
* @return a list of BatchResponse
*/
public static List<BatchResponse> postBatchRequestsWithEnclosingTransaction(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final String jsonifiedBatchRequests) {
final String response = Utils.performServerPost(requestSpec, responseSpec, BATCH_API_URL_EXT, jsonifiedBatchRequests, null);
return BatchHelper.fromJsonString(response);
}
/**
* Returns a BatchResponse based on the given BatchRequest, by posting the
* request to the server.
* *
* @param BatchRequest * @param BatchRequest
* @return List<BatchResponse> * @return List<BatchResponse>
*/ */
public static List<BatchResponse> postWithSingleRequest(final RequestSpecification requestSpec, public static List<BatchResponse> postWithSingleRequest(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec,final BatchRequest br) { final ResponseSpecification responseSpec, final BatchRequest br) {
final List<BatchRequest> batchRequests = new ArrayList<>(); final List<BatchRequest> batchRequests = new ArrayList<>();
batchRequests.add(br); batchRequests.add(br);
final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(requestSpec, final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(requestSpec, responseSpec,
responseSpec, jsonifiedRequest); jsonifiedRequest);
//Verifies that the response result is there // Verifies that the response result is there
Assert.assertNotNull(response); Assert.assertNotNull(response);
Assert.assertTrue(response.size() > 0); Assert.assertTrue(response.size() > 0);
return response; return response;
} }
/** /**
* Creates and returns a {@link org.mifosplatform.batch.command.internal.CreateClientCommandStrategy} * Creates and returns a
* Request as one of the request in Batch. * {@link org.mifosplatform.batch.command.internal.CreateClientCommandStrategy}
* * Request as one of the request in Batch.
* @param reqId *
* @param externalId * @param reqId
* @return BatchRequest * @param externalId
*/ * @return BatchRequest
public static BatchRequest createClientRequest(final Long requestId, final String externalId) { */
public static BatchRequest createClientRequest(final Long requestId, final String externalId) {
final BatchRequest br = new BatchRequest(); final BatchRequest br = new BatchRequest();
br.setRequestId(requestId); br.setRequestId(requestId);
br.setRelativeUrl("clients"); br.setRelativeUrl("clients");
br.setMethod("POST"); br.setMethod("POST");
final String extId; final String extId;
if(externalId.equals("")) { if (externalId.equals("")) {
extId = "ext" + String.valueOf((10000 * Math.random())) + String.valueOf((10000 * Math.random())); extId = "ext" + String.valueOf((10000 * Math.random())) + String.valueOf((10000 * Math.random()));
} } else {
else { extId = externalId;
extId = externalId; }
}
final String body = "{ \"officeId\": 1, \"firstname\": \"Petra\", \"lastname\": \"Yton\"," + final String body = "{ \"officeId\": 1, \"firstname\": \"Petra\", \"lastname\": \"Yton\"," + "\"externalId\": " + extId
"\"externalId\": " + extId + ", \"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en\"," + + ", \"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en\","
"\"active\": true, \"activationDate\": \"04 March 2009\", \"submittedOnDate\": \"04 March 2009\"}"; + "\"active\": true, \"activationDate\": \"04 March 2009\", \"submittedOnDate\": \"04 March 2009\"}";
br.setBody(body); br.setBody(body);
return br; return br;
} }
/** /**
* Creates and returns a {@link org.mifosplatform.batch.command.internal.UpdateClientCommandStrategy} * Creates and returns a
* Request with given requestId and reference. * {@link org.mifosplatform.batch.command.internal.UpdateClientCommandStrategy}
* * Request with given requestId and reference.
* @param reqId *
* @param clientId * @param reqId
* @return BatchRequest * @param clientId
*/ * @return BatchRequest
public static BatchRequest updateClientRequest(final Long requestId, final Long reference) { */
public static BatchRequest updateClientRequest(final Long requestId, final Long reference) {
final BatchRequest br = new BatchRequest(); final BatchRequest br = new BatchRequest();
br.setRequestId(requestId); br.setRequestId(requestId);
br.setRelativeUrl("clients/$.clientId"); br.setRelativeUrl("clients/$.clientId");
br.setMethod("PUT"); br.setMethod("PUT");
br.setReference(reference); br.setReference(reference);
br.setBody("{\"firstname\": \"TestFirstName\", \"lastname\": \"TestLastName\"}"); br.setBody("{\"firstname\": \"TestFirstName\", \"lastname\": \"TestLastName\"}");
return br; return br;
} }
/** /**
* Creates and returns a {@link org.mifosplatform.batch.command.internal.ApplyLoanCommandStrategy} * Creates and returns a
* Request with given requestId and reference. * {@link org.mifosplatform.batch.command.internal.ApplyLoanCommandStrategy}
* * Request with given requestId and reference.
* @param requestId *
* @param reference * @param requestId
* @param productId * @param reference
* @return BatchRequest * @param productId
*/ * @return BatchRequest
public static BatchRequest applyLoanRequest(final Long requestId, final Long reference, */
final Integer productId) { public static BatchRequest applyLoanRequest(final Long requestId, final Long reference, final Integer productId) {
final BatchRequest br = new BatchRequest(); final BatchRequest br = new BatchRequest();
br.setRequestId(requestId); br.setRequestId(requestId);
br.setRelativeUrl("loans"); br.setRelativeUrl("loans");
br.setMethod("POST"); br.setMethod("POST");
br.setReference(reference); br.setReference(reference);
final String body = "{\"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en_GB\", \"clientId\": \"$.clientId\"," + final String body = "{\"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en_GB\", \"clientId\": \"$.clientId\"," + "\"productId\": "
"\"productId\": "+productId+", \"principal\": \"10,000.00\", \"loanTermFrequency\": 12," + + productId + ", \"principal\": \"10,000.00\", \"loanTermFrequency\": 12,"
"\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10," + + "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10,"
"\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10," + + "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10,"
"\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1," + + "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1,"
"\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"10 Jun 2013\"," + + "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"10 Jun 2013\","
"\"submittedOnDate\": \"10 Jun 2013\"}"; + "\"submittedOnDate\": \"10 Jun 2013\"}";
br.setBody(body); br.setBody(body);
return br; return br;
} }
/** /**
* Creates and returns a {@link org.mifosplatform.batch.command.internal.ApplySavingsCommandStrategy} * Creates and returns a
* Request with given requestId and reference. * {@link org.mifosplatform.batch.command.internal.ApplySavingsCommandStrategy}
* * Request with given requestId and reference.
* @param requestId *
* @param reference * @param requestId
* @param productId * @param reference
* @return BatchRequest * @param productId
*/ * @return BatchRequest
public static BatchRequest applySavingsRequest(final Long requestId, final Long reference, */
final Integer productId){ public static BatchRequest applySavingsRequest(final Long requestId, final Long reference, final Integer productId) {
final BatchRequest br = new BatchRequest(); final BatchRequest br = new BatchRequest();
br.setRequestId(requestId); br.setRequestId(requestId);
br.setRelativeUrl("savingsaccounts"); br.setRelativeUrl("savingsaccounts");
br.setMethod("POST"); br.setMethod("POST");
br.setReference(reference); br.setReference(reference);
final String body = "{\"clientId\": \"$.clientId\", \"productId\": "+productId+ "," + final String body = "{\"clientId\": \"$.clientId\", \"productId\": " + productId + ","
"\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"submittedOnDate\": \"01 March 2011\"}"; + "\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"submittedOnDate\": \"01 March 2011\"}";
br.setBody(body); br.setBody(body);
return br; return br;
} }
/** /**
* Checks that the client with given externalId is not created on the server. * Creates and returns a
* * {@link org.mifosplatform.batch.command.internal.CreateChargeCommandStrategy}
* @param requestSpec * Request with given requestId and reference
* @param responseSpec *
* @param externalId * @param requestId
*/ * @param reference
* @return BatchRequest
*/
public static BatchRequest createChargeRequest(final Long requestId, final Long reference) {
final BatchRequest br = new BatchRequest();
br.setRequestId(requestId);
br.setRelativeUrl("loans/$.loanId/charges");
br.setMethod("POST");
br.setReference(reference);
final String body = "{\"chargeId\": \"2\", \"locale\": \"en\", \"amount\": \"100\", "
+ "\"dateFormat\": \"dd MMMM yyyy\", \"dueDate\": \"29 April 2013\"}";
br.setBody(body);
return br;
}
/**
* Creates and returns a
* {@link org.mifosplatform.batch.command.internal.CollectChargesCommandStrategy}
* Request with given requestId and reference
*
* @param requestId
* @param reference
* @return BatchRequest
*/
public static BatchRequest collectChargesRequest(final Long requestId, final Long reference) {
final BatchRequest br = new BatchRequest();
br.setRequestId(requestId);
br.setRelativeUrl("loans/$.loanId/charges");
br.setReference(reference);
br.setMethod("GET");
br.setBody("{ }");
return br;
}
/**
* Checks that the client with given externalId is not created on the
* server.
*
* @param requestSpec
* @param responseSpec
* @param externalId
*/
public static void verifyClientCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, public static void verifyClientCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String externalId) { final String externalId) {
System.out.println("------------------------------CHECK CLIENT DETAILS------------------------------------\n"); System.out.println("------------------------------CHECK CLIENT DETAILS------------------------------------\n");
final String CLIENT_URL = "/mifosng-provider/api/v1/clients?externalId=" + externalId + "&tenantIdentifier=default"; final String CLIENT_URL = "/mifosng-provider/api/v1/clients?externalId=" + externalId + "&tenantIdentifier=default";
final Integer responseRecords = Utils.performServerGet(requestSpec, responseSpec, CLIENT_URL, "totalFilteredRecords"); final Integer responseRecords = Utils.performServerGet(requestSpec, responseSpec, CLIENT_URL, "totalFilteredRecords");
Assert.assertEquals("No records found with given externalId",(long) responseRecords, (long) 0); Assert.assertEquals("No records found with given externalId", (long) responseRecords, (long) 0);
} }
} }
@@ -9,12 +9,12 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
import org.mifosplatform.batch.exception.ErrorHandler;
import org.mifosplatform.batch.exception.ErrorInfo;
import org.mifosplatform.batch.serialization.BatchRequestJsonHelper; import org.mifosplatform.batch.serialization.BatchRequestJsonHelper;
import org.mifosplatform.batch.service.BatchApiService; import org.mifosplatform.batch.service.BatchApiService;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
@@ -25,11 +25,12 @@ import org.springframework.stereotype.Component;
/** /**
* Provides a REST resource for Batch Requests. This class acts as a proxy to * Provides a REST resource for Batch Requests. This class acts as a proxy to
* {@link org.mifosplatform.batch.service.BatchApiService} and de-serializes * {@link org.mifosplatform.batch.service.BatchApiService} and de-serializes the
* the incoming JSON string to a list of {@link org.mifosplatform.batch.domain * incoming JSON string to a list of
* .BatchRequest} type. This list is forwarded to BatchApiService which finally * {@link org.mifosplatform.batch.domain .BatchRequest} type. This list is
* returns a list of {@link org.mifosplatform.batch.domain.BatchResponse} type * forwarded to BatchApiService which finally returns a list of
* which is then serialized into JSON response by this Resource class. * {@link org.mifosplatform.batch.domain.BatchResponse} type which is then
* serialized into JSON response by this Resource class.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -42,79 +43,63 @@ import org.springframework.stereotype.Component;
@Scope("singleton") @Scope("singleton")
public class BatchApiResource { public class BatchApiResource {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ToApiJsonSerializer<BatchResponse> toApiJsonSerializer; private final ToApiJsonSerializer<BatchResponse> toApiJsonSerializer;
private final BatchApiService service; private final BatchApiService service;
private final BatchRequestJsonHelper batchRequestJsonHelper; private final BatchRequestJsonHelper batchRequestJsonHelper;
/** /**
* Constructs a 'BatchApiService' with context, toApiJsonSerializer, service * Constructs a 'BatchApiService' with context, toApiJsonSerializer, service
* and batchRequestJsonHelper. * and batchRequestJsonHelper.
* *
* @param context * @param context
* @param toApiJsonSerializer * @param toApiJsonSerializer
* @param service * @param service
* @param batchRequestJsonHelper * @param batchRequestJsonHelper
*/ */
@Autowired @Autowired
public BatchApiResource(final PlatformSecurityContext context, final ToApiJsonSerializer<BatchResponse> public BatchApiResource(final PlatformSecurityContext context, final ToApiJsonSerializer<BatchResponse> toApiJsonSerializer,
toApiJsonSerializer, final BatchApiService service, final BatchRequestJsonHelper batchRequestJsonHelper) { final BatchApiService service, final BatchRequestJsonHelper batchRequestJsonHelper) {
this.context = context; this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer; this.toApiJsonSerializer = toApiJsonSerializer;
this.service = service; this.service = service;
this.batchRequestJsonHelper = batchRequestJsonHelper; this.batchRequestJsonHelper = batchRequestJsonHelper;
} }
/** /**
* Rest assured POST method to get {@link BatchRequest} and returns back the * Rest assured POST method to get {@link BatchRequest} and returns back the
* consolidated {@link BatchResponse} * consolidated {@link BatchResponse}
* *
* @param jsonRequestString * @param jsonRequestString
* @return serialized JSON * @param enclosingTransaction
*/ * @param uriInfo
@POST * @return serialized JSON
@Consumes({ MediaType.APPLICATION_JSON }) */
@Produces({ MediaType.APPLICATION_JSON }) @POST
public String handleBatchRequests(@DefaultValue("false") @QueryParam("enclosingTransaction") @Consumes({ MediaType.APPLICATION_JSON })
final boolean enclosingTransaction, final String jsonRequestString) { @Produces({ MediaType.APPLICATION_JSON })
public String handleBatchRequests(@DefaultValue("false") @QueryParam("enclosingTransaction") final boolean enclosingTransaction,
final String jsonRequestString, @Context UriInfo uriInfo) {
//Handles user authentication // Handles user authentication
this.context.authenticatedUser(); this.context.authenticatedUser();
//Converts request array into BatchRequest List // Converts request array into BatchRequest List
final List<BatchRequest> requestList = this.batchRequestJsonHelper.extractList(jsonRequestString); final List<BatchRequest> requestList = this.batchRequestJsonHelper.extractList(jsonRequestString);
//Gets back the consolidated BatchResponse from BatchApiservice // Gets back the consolidated BatchResponse from BatchApiservice
List<BatchResponse> result = new ArrayList<>(); List<BatchResponse> result = new ArrayList<>();
//If the request is to be handled as a Transaction. All requests will be rolled back on error // If the request is to be handled as a Transaction. All requests will
if(enclosingTransaction) { // be rolled back on error
if (enclosingTransaction) {
result = service.handleBatchRequestsWithEnclosingTransaction(requestList, uriInfo);
} else {
result = service.handleBatchRequestsWithoutEnclosingTransaction(requestList, uriInfo);
}
try { return this.toApiJsonSerializer.serialize(result);
result = service.handleBatchRequestsWithEnclosingTransaction(requestList);
}
catch(RuntimeException e) {
BatchResponse response = new BatchResponse(); }
//Gets an object of type ErrorInfo, containing information about raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage());
result.add(response);
return this.toApiJsonSerializer.serialize(result);
}
}
else {
result = service.handleBatchRequestsWithoutEnclosingTransaction(requestList);
}
return this.toApiJsonSerializer.serialize(result);
}
} }
@@ -1,9 +1,9 @@
package org.mifosplatform.batch.command; package org.mifosplatform.batch.command;
/** /**
* Provides an object to {@link org.mifosplatform.batch.service.BatchApiService} to * Provides an object to {@link org.mifosplatform.batch.service.BatchApiService}
* get the proper commandStrategy for each request in BatchRequest. It uses Builder * to get the proper commandStrategy for each request in BatchRequest. It uses
* pattern to create object of this type. * Builder pattern to create object of this type.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -11,92 +11,80 @@ package org.mifosplatform.batch.command;
*/ */
public class CommandContext { public class CommandContext {
/** /**
* Static Builder class to provide a Build method for CommandContext. * Static Builder class to provide a Build method for CommandContext.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
*/ */
public static class Builder{ public static class Builder {
private String resource; private String resource;
private String method; private String method;
private Builder(final String resource) { private Builder(final String resource) {
this.resource = resource; this.resource = resource;
} }
public Builder method(final String method) { public Builder method(final String method) {
this.method = method; this.method = method;
return this; return this;
} }
public CommandContext build() { public CommandContext build() {
return new CommandContext(this.resource, this.method); return new CommandContext(this.resource, this.method);
} }
} }
private final String resource; private final String resource;
private final String method; private final String method;
private CommandContext(final String resource, final String method) { private CommandContext(final String resource, final String method) {
this.resource = resource; this.resource = resource;
this.method = method; this.method = method;
} }
public static Builder resource(final String resource) { public static Builder resource(final String resource) {
return new Builder(resource); return new Builder(resource);
} }
/** /**
* Returns a boolean value if the relativeUrl 'matches' one of the regex keys in * Returns a boolean value if the relativeUrl 'matches' one of the regex
* the available commandStrategies. It take CommandContext object as parameter * keys in the available commandStrategies. It take CommandContext object as
* which contains a 'resource' member as a regex key for available commandStrategies. * parameter which contains a 'resource' member as a regex key for available
* * commandStrategies.
* @param other *
* @return boolean * @param other
*/ * @return boolean
public boolean matcher(CommandContext other) { */
if(this.resource.matches(other.resource) && this.method.equals(other.method)) { public boolean matcher(CommandContext other) {
return true; if (this.resource.matches(other.resource) && this.method.equals(other.method)) { return true; }
} return false;
return false; }
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((this.method == null) ? 0 : this.method.hashCode());
result = prime * result
+ ((this.resource == null) ? 0 : this.resource.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CommandContext other = (CommandContext) obj;
if (this.method == null) {
if (other.method != null)
return false;
} else if (!this.method.equals(other.method))
return false;
if (this.resource == null) {
if (other.resource != null)
return false;
} else if (!this.resource.equals(other.resource))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.method == null) ? 0 : this.method.hashCode());
result = prime * result + ((this.resource == null) ? 0 : this.resource.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
CommandContext other = (CommandContext) obj;
if (this.method == null) {
if (other.method != null) return false;
} else if (!this.method.equals(other.method)) return false;
if (this.resource == null) {
if (other.resource != null) return false;
} else if (!this.resource.equals(other.resource)) return false;
return true;
}
} }
@@ -1,5 +1,7 @@
package org.mifosplatform.batch.command; package org.mifosplatform.batch.command;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -13,13 +15,15 @@ import org.mifosplatform.batch.domain.BatchResponse;
*/ */
public interface CommandStrategy { public interface CommandStrategy {
/** /**
* Returns an object of type {@link org.mifosplatform.batch.domain.BatchResponse}. * Returns an object of type
* This takes {@link org.mifosplatform.batch.domain.BatchRequest} as it's single * {@link org.mifosplatform.batch.domain.BatchResponse}. This takes
* argument and provides appropriate response. * {@link org.mifosplatform.batch.domain.BatchRequest} as it's single
* * argument and provides appropriate response.
* @param batchRequest *
* @return BatchResponse * @param batchRequest
*/ * @param uriInfo
public BatchResponse execute(BatchRequest batchRequest); * @return BatchResponse
*/
public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo);
} }
@@ -18,59 +18,62 @@ import org.springframework.stereotype.Component;
@Component @Component
public class CommandStrategyProvider { public class CommandStrategyProvider {
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final ConcurrentHashMap<CommandContext, String> commandStrategies = new ConcurrentHashMap<>(); private final ConcurrentHashMap<CommandContext, String> commandStrategies = new ConcurrentHashMap<>();
/** /**
* Constructs a CommandStrategyProvider with argument of ApplicationContext type. * Constructs a CommandStrategyProvider with argument of ApplicationContext
* It also initialize commandStrategies using init() function by filling it * type. It also initialize commandStrategies using init() function by
* with available CommandStrategies in {@link org.mifosplatform.batch.command.internal}. * filling it with available CommandStrategies in
* * {@link org.mifosplatform.batch.command.internal}.
* @param applicationContext *
*/ * @param applicationContext
@Autowired */
public CommandStrategyProvider(final ApplicationContext applicationContext) { @Autowired
public CommandStrategyProvider(final ApplicationContext applicationContext) {
//calls init() function of this class. // calls init() function of this class.
init(); init();
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
} }
/** /**
* Returns an appropriate commandStrategy after determining it using * Returns an appropriate commandStrategy after determining it using the
* the CommandContext of the request. If no such Strategy is found then * CommandContext of the request. If no such Strategy is found then a
* a default strategy is returned back. * default strategy is returned back.
* *
* @param commandContext * @param commandContext
* @return CommandStrategy * @return CommandStrategy
* @see org.mifosplatform.batch.command.internal.UnknownCommandStrategy * @see org.mifosplatform.batch.command.internal.UnknownCommandStrategy
*/ */
public CommandStrategy getCommandStrategy(final CommandContext commandContext) { public CommandStrategy getCommandStrategy(final CommandContext commandContext) {
if(this.commandStrategies.containsKey(commandContext)) { if (this.commandStrategies.containsKey(commandContext)) { return (CommandStrategy) this.applicationContext
return (CommandStrategy) this.applicationContext.getBean(this.commandStrategies.get(commandContext)); .getBean(this.commandStrategies.get(commandContext)); }
}
for(ConcurrentHashMap.Entry<CommandContext,String> entry : this.commandStrategies.entrySet()) { for (ConcurrentHashMap.Entry<CommandContext, String> entry : this.commandStrategies.entrySet()) {
if(commandContext.matcher(entry.getKey())) { if (commandContext.matcher(entry.getKey())) { return (CommandStrategy) this.applicationContext.getBean(this.commandStrategies
return (CommandStrategy) this.applicationContext.getBean(this.commandStrategies.get(entry.getKey())); .get(entry.getKey())); }
} }
}
return new UnknownCommandStrategy(); return new UnknownCommandStrategy();
} }
/** /**
* Contains various available command strategies in {@link org.mifosplatform.batch.command.internal}. * Contains various available command strategies in
* Any new command Strategy will have to be added within this function in order to initiate it * {@link org.mifosplatform.batch.command.internal}. Any new command
* within the constructor. * Strategy will have to be added within this function in order to initiate
*/ * it within the constructor.
private void init() { */
this.commandStrategies.put(CommandContext.resource("clients").method("POST").build(), "createClientCommandStrategy"); private void init() {
this.commandStrategies.put(CommandContext.resource("clients\\/\\d+").method("PUT").build(), "updateClientCommandStrategy"); this.commandStrategies.put(CommandContext.resource("clients").method("POST").build(), "createClientCommandStrategy");
this.commandStrategies.put(CommandContext.resource("loans").method("POST").build(), "applyLoanCommandStrategy"); this.commandStrategies.put(CommandContext.resource("clients\\/\\d+").method("PUT").build(), "updateClientCommandStrategy");
this.commandStrategies.put(CommandContext.resource("savingsaccounts").method("POST").build(), "applySavingsCommandStrategy"); this.commandStrategies.put(CommandContext.resource("loans").method("POST").build(), "applyLoanCommandStrategy");
} this.commandStrategies.put(CommandContext.resource("savingsaccounts").method("POST").build(), "applySavingsCommandStrategy");
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges").method("POST").build(), "createChargeCommandStrategy");
this.commandStrategies
.put(CommandContext.resource("loans\\/\\d+\\/charges").method("GET").build(), "collectChargesCommandStrategy");
}
} }
@@ -1,5 +1,7 @@
package org.mifosplatform.batch.command.internal; package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -10,12 +12,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* Implements {@link org.mifosplatform.batch.command.CommandStrategy} and applies a new loan * Implements {@link org.mifosplatform.batch.command.CommandStrategy} and
* on an existing client. It passes the contents of the body from the BatchRequest * applies a new loan on an existing client. It passes the contents of the body
* to {@link org.mifosplatform.portfolio.client.api.LoansApiResource} and gets back * from the BatchRequest to
* {@link org.mifosplatform.portfolio.client.api.LoansApiResource} and gets back
* the response. This class will also catch any errors raised by * the response. This class will also catch any errors raised by
* {@link org.mifosplatform.portfolio.client.api.LoansApiResource} and map those errors * {@link org.mifosplatform.portfolio.client.api.LoansApiResource} and map those
* to appropriate status codes in BatchResponse. * errors to appropriate status codes in BatchResponse.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -24,44 +27,45 @@ import org.springframework.stereotype.Component;
* @see org.mifosplatform.batch.domain.BatchResponse * @see org.mifosplatform.batch.domain.BatchResponse
*/ */
@Component @Component
public class ApplyLoanCommandStrategy implements CommandStrategy{ public class ApplyLoanCommandStrategy implements CommandStrategy {
private final LoansApiResource loansApiResource; private final LoansApiResource loansApiResource;
@Autowired @Autowired
public ApplyLoanCommandStrategy(final LoansApiResource loansApiResource) { public ApplyLoanCommandStrategy(final LoansApiResource loansApiResource) {
this.loansApiResource = loansApiResource; this.loansApiResource = loansApiResource;
} }
@Override @Override
public BatchResponse execute(BatchRequest request) { public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse response = new BatchResponse(); final BatchResponse response = new BatchResponse();
final String responseBody; final String responseBody;
response.setRequestId(request.getRequestId()); response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders()); response.setHeaders(request.getHeaders());
//Try-catch blocks to map exceptions to appropriate status codes // Try-catch blocks to map exceptions to appropriate status codes
try { try {
//Calls 'SubmitLoanFunction' function from 'LoansApiResource' to Apply Loan to an existing client // Calls 'SubmitLoanFunction' function from 'LoansApiResource' to
responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody()); // Apply Loan to an existing client
responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody());
response.setStatusCode(200); response.setStatusCode(200);
//Sets the body of the response after loan is successfully applied // Sets the body of the response after loan is successfully applied
response.setBody(responseBody); response.setBody(responseBody);
} } catch (RuntimeException e) {
catch (RuntimeException e) {
//Gets an object of type ErrorInfo, containing information about raised exception // Gets an object of type ErrorInfo, containing information about
ErrorInfo ex = ErrorHandler.handler(e); // raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode()); response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage()); response.setBody(ex.getMessage());
} }
return response; return response;
} }
} }
@@ -1,5 +1,7 @@
package org.mifosplatform.batch.command.internal; package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -10,12 +12,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* Implements {@link org.mifosplatform.batch.command.CommandStrategy} and applies a new savings * Implements {@link org.mifosplatform.batch.command.CommandStrategy} and
* on an existing client. It passes the contents of the body from the BatchRequest * applies a new savings on an existing client. It passes the contents of the
* to {@link org.mifosplatform.portfolio.client.api.SavingsAccountsApiResource} and gets back * body from the BatchRequest to
* the response. This class will also catch any errors raised by * {@link org.mifosplatform.portfolio.client.api.SavingsAccountsApiResource} and
* {@link org.mifosplatform.portfolio.client.api.SavingsAccountsApiResource} and map those errors * gets back the response. This class will also catch any errors raised by
* to appropriate status codes in BatchResponse. * {@link org.mifosplatform.portfolio.client.api.SavingsAccountsApiResource} and
* map those errors to appropriate status codes in BatchResponse.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -24,44 +27,47 @@ import org.springframework.stereotype.Component;
* @see org.mifosplatform.batch.domain.BatchResponse * @see org.mifosplatform.batch.domain.BatchResponse
*/ */
@Component @Component
public class ApplySavingsCommandStrategy implements CommandStrategy{ public class ApplySavingsCommandStrategy implements CommandStrategy {
private final SavingsAccountsApiResource savingsAccountsApiResource; private final SavingsAccountsApiResource savingsAccountsApiResource;
@Autowired @Autowired
public ApplySavingsCommandStrategy(final SavingsAccountsApiResource savingsAccountsApiResource) { public ApplySavingsCommandStrategy(final SavingsAccountsApiResource savingsAccountsApiResource) {
this.savingsAccountsApiResource = savingsAccountsApiResource; this.savingsAccountsApiResource = savingsAccountsApiResource;
} }
@Override @Override
public BatchResponse execute(BatchRequest request) { public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse response = new BatchResponse(); final BatchResponse response = new BatchResponse();
final String responseBody; final String responseBody;
response.setRequestId(request.getRequestId()); response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders()); response.setHeaders(request.getHeaders());
//Try-catch blocks to map exceptions to appropriate status codes // Try-catch blocks to map exceptions to appropriate status codes
try { try {
//Calls 'submitApplication' function from 'SavingsAccountsApiResource' to Apply Savings to an existing client // Calls 'submitApplication' function from
responseBody = savingsAccountsApiResource.submitApplication(request.getBody()); // 'SavingsAccountsApiResource' to Apply Savings to an existing
// client
responseBody = savingsAccountsApiResource.submitApplication(request.getBody());
response.setStatusCode(200); response.setStatusCode(200);
//Sets the body of the response after savings is successfully applied // Sets the body of the response after savings is successfully
response.setBody(responseBody); // applied
response.setBody(responseBody);
} } catch (RuntimeException e) {
catch (RuntimeException e) {
//Gets an object of type ErrorInfo, containing information about raised exception // Gets an object of type ErrorInfo, containing information about
ErrorInfo ex = ErrorHandler.handler(e); // raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode()); response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage()); response.setBody(ex.getMessage());
} }
return response; return response;
} }
} }
@@ -0,0 +1,78 @@
package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse;
import org.mifosplatform.batch.exception.ErrorHandler;
import org.mifosplatform.batch.exception.ErrorInfo;
import org.mifosplatform.portfolio.loanaccount.api.LoanChargesApiResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Implements {@link org.mifosplatform.batch.command.CommandStrategy} and
* Collect Charges for a Loan. It passes the contents of the body from the
* BatchRequest to
* {@link org.mifosplatform.portfolio.client.api.LoanChargesApiResource} and
* gets back the response. This class will also catch any errors raised by
* {@link org.mifosplatform.portfolio.client.api.LoanChargesApiResource} and map
* those errors to appropriate status codes in BatchResponse.
*
* @author Rishabh Shukla
*
* @see org.mifosplatform.batch.command.CommandStrategy
* @see org.mifosplatform.batch.domain.BatchRequest
* @see org.mifosplatform.batch.domain.BatchResponse
*/
@Component
public class CollectChargesCommandStrategy implements CommandStrategy {
private final LoanChargesApiResource loanChargesApiResource;
@Autowired
public CollectChargesCommandStrategy(final LoanChargesApiResource loanChargesApiResource) {
this.loanChargesApiResource = loanChargesApiResource;
}
@Override
public BatchResponse execute(BatchRequest request, UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
final String responseBody;
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
final String[] pathParameters = request.getRelativeUrl().split("/");
// Pluck out the loanId out of the relative path
Long loanId = Long.parseLong(pathParameters[1]);
// Try-catch blocks to map exceptions to appropriate status codes
try {
// Calls 'retrieveAllLoanCharges' function from
// 'LoanChargesApiResource' to Collect
// Charges for a loan
responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanId, uriInfo);
response.setStatusCode(200);
// Sets the body of the response after Charges have been
// successfully collected
response.setBody(responseBody);
} catch (RuntimeException e) {
// Gets an object of type ErrorInfo, containing information about
// raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage());
}
return response;
}
}
@@ -0,0 +1,75 @@
package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse;
import org.mifosplatform.batch.exception.ErrorHandler;
import org.mifosplatform.batch.exception.ErrorInfo;
import org.mifosplatform.portfolio.loanaccount.api.LoanChargesApiResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Implements {@link org.mifosplatform.batch.command.CommandStrategy} and Create
* Charge for a Loan. It passes the contents of the body from the BatchRequest
* to {@link org.mifosplatform.portfolio.client.api.LoanChargesApiResource} and
* gets back the response. This class will also catch any errors raised by
* {@link org.mifosplatform.portfolio.client.api.LoanChargesApiResource} and map
* those errors to appropriate status codes in BatchResponse.
*
* @author Rishabh Shukla
*
* @see org.mifosplatform.batch.command.CommandStrategy
* @see org.mifosplatform.batch.domain.BatchRequest
* @see org.mifosplatform.batch.domain.BatchResponse
*/
@Component
public class CreateChargeCommandStrategy implements CommandStrategy {
private final LoanChargesApiResource loanChargesApiResource;
@Autowired
public CreateChargeCommandStrategy(final LoanChargesApiResource loanChargesApiResource) {
this.loanChargesApiResource = loanChargesApiResource;
}
@Override
public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
final String responseBody;
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
final String[] pathParameters = request.getRelativeUrl().split("/");
Long loanId = Long.parseLong(pathParameters[1]);
// Try-catch blocks to map exceptions to appropriate status codes
try {
// Calls 'executeLoanCharge' function from 'LoanChargesApiResource'
// to create
// a new charge for a loan
responseBody = loanChargesApiResource.executeLoanCharge(loanId, null, request.getBody());
response.setStatusCode(200);
// Sets the body of the response after Charge has been successfully
// created
response.setBody(responseBody);
} catch (RuntimeException e) {
// Gets an object of type ErrorInfo, containing information about
// raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage());
}
return response;
}
}
@@ -1,5 +1,7 @@
package org.mifosplatform.batch.command.internal; package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -11,11 +13,12 @@ import org.springframework.stereotype.Component;
/** /**
* Implements {@link org.mifosplatform.batch.command.CommandStrategy} to handle * Implements {@link org.mifosplatform.batch.command.CommandStrategy} to handle
* creation of a new client. It passes the contents of the body from the BatchRequest * creation of a new client. It passes the contents of the body from the
* to {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and gets back * BatchRequest to
* the response. This class will also catch any errors raised by * {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and gets
* {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and map those errors * back the response. This class will also catch any errors raised by
* to appropriate status codes in BatchResponse. * {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and map
* those errors to appropriate status codes in BatchResponse.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -24,45 +27,47 @@ import org.springframework.stereotype.Component;
* @see org.mifosplatform.batch.domain.BatchResponse * @see org.mifosplatform.batch.domain.BatchResponse
*/ */
@Component @Component
public class CreateClientCommandStrategy implements CommandStrategy{ public class CreateClientCommandStrategy implements CommandStrategy {
private final ClientsApiResource clientsApiResource; private final ClientsApiResource clientsApiResource;
@Autowired @Autowired
public CreateClientCommandStrategy(final ClientsApiResource clientsApiResource) { public CreateClientCommandStrategy(final ClientsApiResource clientsApiResource) {
this.clientsApiResource = clientsApiResource; this.clientsApiResource = clientsApiResource;
} }
@Override @Override
public BatchResponse execute(final BatchRequest request) { public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse response = new BatchResponse(); final BatchResponse response = new BatchResponse();
final String responseBody; final String responseBody;
response.setRequestId(request.getRequestId()); response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders()); response.setHeaders(request.getHeaders());
//Try-catch blocks to map exceptions to appropriate status codes // Try-catch blocks to map exceptions to appropriate status codes
try { try {
//Calls 'create' function from 'ClientsApiResource' to create a new client // Calls 'create' function from 'ClientsApiResource' to create a new
responseBody = clientsApiResource.create(request.getBody()); // client
responseBody = clientsApiResource.create(request.getBody());
response.setStatusCode(200); response.setStatusCode(200);
//Sets the body of the response after the successful creation of the client // Sets the body of the response after the successful creation of
response.setBody(responseBody); // the client
response.setBody(responseBody);
} } catch (RuntimeException e) {
catch (RuntimeException e) {
//Gets an object of type ErrorInfo, containing information about raised exception // Gets an object of type ErrorInfo, containing information about
ErrorInfo ex = ErrorHandler.handler(e); // raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode()); response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage()); response.setBody(ex.getMessage());
} }
return response; return response;
} }
} }
@@ -1,5 +1,7 @@
package org.mifosplatform.batch.command.internal; package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -7,25 +9,25 @@ import org.springframework.stereotype.Component;
/** /**
* Provides a default CommandStrategy by implementing * Provides a default CommandStrategy by implementing
* {@link org.mifosplatform.batch.command.CommandStrategy} * {@link org.mifosplatform.batch.command.CommandStrategy} in case there is no
* in case there is no appropriate command strategy with requested * appropriate command strategy with requested 'method' and 'resoureUrl'.
* 'method' and 'resoureUrl'.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
*/ */
@Component @Component
public class UnknownCommandStrategy implements CommandStrategy { public class UnknownCommandStrategy implements CommandStrategy {
@Override @Override
public BatchResponse execute(BatchRequest batchRequest) { public BatchResponse execute(BatchRequest batchRequest, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse batchResponse = new BatchResponse(); final BatchResponse batchResponse = new BatchResponse();
batchResponse.setRequestId(batchRequest.getRequestId()); batchResponse.setRequestId(batchRequest.getRequestId());
batchResponse.setStatusCode(501); batchResponse.setStatusCode(501);
batchResponse.setBody("Resource with method " + batchRequest.getMethod() + " and relativeUrl " + batchRequest.getRelativeUrl()
return batchResponse; + " doesn't exist");
}
return batchResponse;
}
} }
@@ -1,5 +1,7 @@
package org.mifosplatform.batch.command.internal; package org.mifosplatform.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -10,12 +12,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* Implements {@link org.mifosplatform.batch.command.CommandStrategy} and updates the * Implements {@link org.mifosplatform.batch.command.CommandStrategy} and
* information of an existing client. It passes the contents of the body from the BatchRequest * updates the information of an existing client. It passes the contents of the
* to {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and gets back * body from the BatchRequest to
* the response. This class will also catch any errors raised by * {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and gets
* {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and map those errors * back the response. This class will also catch any errors raised by
* to appropriate status codes in BatchResponse. * {@link org.mifosplatform.portfolio.client.api.ClientsApiResource} and map
* those errors to appropriate status codes in BatchResponse.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -26,46 +29,49 @@ import org.springframework.stereotype.Component;
@Component @Component
public class UpdateClientCommandStrategy implements CommandStrategy { public class UpdateClientCommandStrategy implements CommandStrategy {
private final ClientsApiResource clientsApiResource; private final ClientsApiResource clientsApiResource;
@Autowired @Autowired
public UpdateClientCommandStrategy(final ClientsApiResource clientsApiResource) { public UpdateClientCommandStrategy(final ClientsApiResource clientsApiResource) {
this.clientsApiResource = clientsApiResource; this.clientsApiResource = clientsApiResource;
} }
@Override @Override
public BatchResponse execute(BatchRequest request) { public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse response = new BatchResponse(); final BatchResponse response = new BatchResponse();
final String responseBody; final String responseBody;
response.setRequestId(request.getRequestId()); response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders()); response.setHeaders(request.getHeaders());
//Get the clientID // Get the clientID
final String relativeUrl = request.getRelativeUrl(); final String relativeUrl = request.getRelativeUrl();
final Long clientId = Long.parseLong(relativeUrl.substring(relativeUrl.indexOf('/') + 1)); final Long clientId = Long.parseLong(relativeUrl.substring(relativeUrl.indexOf('/') + 1));
//Try-catch blocks to map exceptions to appropriate status codes // Try-catch blocks to map exceptions to appropriate status codes
try { try {
//Calls 'update' function from 'ClientsApiResource' to update a client // Calls 'update' function from 'ClientsApiResource' to update a
responseBody = clientsApiResource.update(clientId, request.getBody()); // client
responseBody = clientsApiResource.update(clientId, request.getBody());
response.setStatusCode(200); response.setStatusCode(200);
//Sets the body of the response after the successful update of client information // Sets the body of the response after the successful update of
response.setBody(responseBody); // client information
response.setBody(responseBody);
} catch (RuntimeException e) { } catch (RuntimeException e) {
//Gets an object of type ErrorInfo, containing information about raised exception // Gets an object of type ErrorInfo, containing information about
ErrorInfo ex = ErrorHandler.handler(e); // raised exception
ErrorInfo ex = ErrorHandler.handler(e);
response.setStatusCode(ex.getStatusCode()); response.setStatusCode(ex.getStatusCode());
response.setBody(ex.getMessage()); response.setBody(ex.getMessage());
} }
return response; return response;
} }
} }
@@ -3,10 +3,10 @@ package org.mifosplatform.batch.domain;
import java.util.Set; import java.util.Set;
/** /**
* Provides an object for separate HTTP requests in the Batch Request for Batch API. * Provides an object for separate HTTP requests in the Batch Request for Batch
* A requestId is also included as data field which takes care of dependency issues * API. A requestId is also included as data field which takes care of
* among various requests. This class also provides getter and setter functions to * dependency issues among various requests. This class also provides getter and
* access Batch Request data fields. * setter functions to access Batch Request data fields.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -15,151 +15,158 @@ import java.util.Set;
*/ */
public class BatchRequest { public class BatchRequest {
private Long requestId; private Long requestId;
private String relativeUrl; private String relativeUrl;
private String method; private String method;
private Set<Header> headers; private Set<Header> headers;
private Long reference; private Long reference;
private String body; private String body;
/** /**
* Constructs a 'BatchRequest' with requestId, relativeUrl, method, headers, reference and * Constructs a 'BatchRequest' with requestId, relativeUrl, method, headers,
* body of the incoming request. * reference and body of the incoming request.
* *
* @param requestId of HTTP request. * @param requestId
* @param relativeUrl of HTTP request. * of HTTP request.
* @param method of HTTP request. * @param relativeUrl
* @param headers of HTTP request. * of HTTP request.
* @param reference of HTTP request. * @param method
* @param body of HTTP request. * of HTTP request.
* * @param headers
* @see Header * of HTTP request.
*/ * @param reference
public BatchRequest(Long requestId, String relativeUrl, String method, Set<Header> headers, * of HTTP request.
Long reference, String body) { * @param body
* of HTTP request.
*
* @see Header
*/
public BatchRequest(Long requestId, String relativeUrl, String method, Set<Header> headers, Long reference, String body) {
this.requestId = requestId; this.requestId = requestId;
this.relativeUrl = relativeUrl; this.relativeUrl = relativeUrl;
this.method = method; this.method = method;
this.headers = headers; this.headers = headers;
this.reference = reference; this.reference = reference;
this.body = body; this.body = body;
} }
/** /**
* Constructs a default constructor of 'BatchRequest' * Constructs a default constructor of 'BatchRequest'
*/ */
public BatchRequest() { public BatchRequest() {
} }
/** /**
* Returns the value of 'requestId' of an object of this class. * Returns the value of 'requestId' of an object of this class.
* *
* @return requestId of the HTTP request. * @return requestId of the HTTP request.
*/ */
public Long getRequestId() { public Long getRequestId() {
return this.requestId; return this.requestId;
} }
/** /**
* Sets the value of 'requestId' of an object of this class. * Sets the value of 'requestId' of an object of this class.
* *
* @param requestId * @param requestId
*/ */
public void setRequestId(Long requestId) { public void setRequestId(Long requestId) {
this.requestId = requestId; this.requestId = requestId;
} }
/** /**
* Returns the value of 'relativeUrl' of an object of this class. * Returns the value of 'relativeUrl' of an object of this class.
* *
* @return relativeUrl of the HTTP request. * @return relativeUrl of the HTTP request.
*/ */
public String getRelativeUrl() { public String getRelativeUrl() {
return this.relativeUrl; return this.relativeUrl;
} }
/** /**
* Sets the value of 'relativeUrl' of an object of this class. * Sets the value of 'relativeUrl' of an object of this class.
* *
* @param relativeUrl * @param relativeUrl
*/ */
public void setRelativeUrl(String relativeUrl) { public void setRelativeUrl(String relativeUrl) {
this.relativeUrl = relativeUrl; this.relativeUrl = relativeUrl;
} }
/** /**
* Returns the value of 'method' of an object of this class. * Returns the value of 'method' of an object of this class.
* *
* @return method of the HTTP request. * @return method of the HTTP request.
*/ */
public String getMethod() { public String getMethod() {
return this.method; return this.method;
} }
/** /**
* Sets the value of 'method' of the object of this class. * Sets the value of 'method' of the object of this class.
* *
* @param method * @param method
*/ */
public void setMethod(String method) { public void setMethod(String method) {
this.method = method; this.method = method;
} }
/** /**
* Returns the values of 'headers' of {@link Header} type of an object of this class. * Returns the values of 'headers' of {@link Header} type of an object of
* * this class.
* @return headers of the HTTP request. *
* @see Header * @return headers of the HTTP request.
*/ * @see Header
public Set<Header> getHeaders() { */
return this.headers; public Set<Header> getHeaders() {
} return this.headers;
}
/** /**
* Sets the values of 'headers' of {@link Header} type of an object of this class. * Sets the values of 'headers' of {@link Header} type of an object of this
* * class.
* @param headers *
* @see Header * @param headers
*/ * @see Header
public void setHeaders(Set<Header> headers) { */
this.headers = headers; public void setHeaders(Set<Header> headers) {
} this.headers = headers;
}
/** /**
* Returns the value of 'reference' of an object of this class * Returns the value of 'reference' of an object of this class
* *
* @return reference of the HTTP request * @return reference of the HTTP request
*/ */
public Long getReference() { public Long getReference() {
return this.reference; return this.reference;
} }
/** /**
* Sets the value of 'reference' of an object of this class. * Sets the value of 'reference' of an object of this class.
* *
* @param reference * @param reference
*/ */
public void setReference(Long reference) { public void setReference(Long reference) {
this.reference = reference; this.reference = reference;
} }
/** /**
* Returns the value of 'body' of an object of this class. * Returns the value of 'body' of an object of this class.
* *
* @return body of the HTTP request. * @return body of the HTTP request.
*/ */
public String getBody() { public String getBody() {
return this.body; return this.body;
} }
/** /**
* Sets the value of 'body' of an object of this class. * Sets the value of 'body' of an object of this class.
* *
* @param body * @param body
*/ */
public void setBody(String body) { public void setBody(String body) {
this.body = body; this.body = body;
} }
} }
@@ -3,9 +3,10 @@ package org.mifosplatform.batch.domain;
import java.util.Set; import java.util.Set;
/** /**
* Provides an object for separate HTTP responses in the Batch Response for Batch API. * Provides an object for separate HTTP responses in the Batch Response for
* It contains all the information about a particular HTTP response in the Batch Response. * Batch API. It contains all the information about a particular HTTP response
* Getter and Setter functions are also included to access response data fields. * in the Batch Response. Getter and Setter functions are also included to
* access response data fields.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -15,106 +16,108 @@ import java.util.Set;
*/ */
public class BatchResponse { public class BatchResponse {
private Long requestId; private Long requestId;
private Integer statusCode; private Integer statusCode;
private Set<Header> headers; private Set<Header> headers;
private String body; private String body;
/** /**
* Constructs a 'BatchResponse' with requestId, statusCode, headers * Constructs a 'BatchResponse' with requestId, statusCode, headers and body
* and body of the HTTP requests. * of the HTTP requests.
* *
* @param requestId * @param requestId
* @param statusCode * @param statusCode
* @param headers * @param headers
* @param body * @param body
* @see Header * @see Header
*/ */
public BatchResponse(Long requestId, Integer statusCode, Set<Header> headers, String body) { public BatchResponse(Long requestId, Integer statusCode, Set<Header> headers, String body) {
this.requestId = requestId; this.requestId = requestId;
this.statusCode = statusCode; this.statusCode = statusCode;
this.headers = headers; this.headers = headers;
this.body = body; this.body = body;
} }
/** /**
* Constructs a default constructor of 'BatchResponse' * Constructs a default constructor of 'BatchResponse'
*/ */
public BatchResponse() { public BatchResponse() {
} }
/** /**
* Returns the 'requestId' of an object of this class. * Returns the 'requestId' of an object of this class.
* *
* @return requestId of the HTTP request. * @return requestId of the HTTP request.
*/ */
public Long getRequestId() { public Long getRequestId() {
return this.requestId; return this.requestId;
} }
/** /**
* Sets the value of 'requestId' of an object of this class. * Sets the value of 'requestId' of an object of this class.
* *
* @param requestId * @param requestId
*/ */
public void setRequestId(Long requestId) { public void setRequestId(Long requestId) {
this.requestId = requestId; this.requestId = requestId;
} }
/** /**
* Returns the 'statusCode' of an object of this class. * Returns the 'statusCode' of an object of this class.
* *
* @return statusCode of the HTTP request. * @return statusCode of the HTTP request.
*/ */
public Integer getStatusCode() { public Integer getStatusCode() {
return this.statusCode; return this.statusCode;
} }
/** /**
* Sets the value of 'statusCode' of an object of this class. * Sets the value of 'statusCode' of an object of this class.
* *
* @param statusCode * @param statusCode
*/ */
public void setStatusCode(Integer statusCode) { public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode; this.statusCode = statusCode;
} }
/** /**
* Returns the 'headers' of {@link Header} type of an object of this class. * Returns the 'headers' of {@link Header} type of an object of this class.
* *
* @return headers of the HTTP request. * @return headers of the HTTP request.
* @see Header * @see Header
*/ */
public Set<Header> getHeaders() { public Set<Header> getHeaders() {
return this.headers; return this.headers;
} }
/** /**
* Sets the value of 'headers' of {@link Header} type of an object of this class. * Sets the value of 'headers' of {@link Header} type of an object of this
* * class.
* @param headers of {@link Header} Type *
* @see Header * @param headers
*/ * of {@link Header} Type
public void setHeaders(Set<Header> headers) { * @see Header
this.headers = headers; */
} public void setHeaders(Set<Header> headers) {
this.headers = headers;
}
/** /**
* Returns the 'body' of an object of this class. * Returns the 'body' of an object of this class.
* *
* @return body of the HTTP request. * @return body of the HTTP request.
*/ */
public String getBody() { public String getBody() {
return this.body; return this.body;
} }
/** /**
* Sets the value of 'body' of an object of this class. * Sets the value of 'body' of an object of this class.
* *
* @param body * @param body
*/ */
public void setBody(String body) { public void setBody(String body) {
this.body = body; this.body = body;
} }
} }
@@ -1,9 +1,9 @@
package org.mifosplatform.batch.domain; package org.mifosplatform.batch.domain;
/** /**
* Provides an object to handle HTTP headers as name and value pairs for Batch API. * Provides an object to handle HTTP headers as name and value pairs for Batch
* It is used in {@link BatchRequest} and {@link BatchResponse} to store the * API. It is used in {@link BatchRequest} and {@link BatchResponse} to store
* information regarding the headers in incoming and outgoing JSON Strings. * the information regarding the headers in incoming and outgoing JSON Strings.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -12,61 +12,63 @@ package org.mifosplatform.batch.domain;
*/ */
public class Header { public class Header {
private String name; private String name;
private String value; private String value;
/** /**
* Constructs a 'Header' with the name and value of HTTP headers. * Constructs a 'Header' with the name and value of HTTP headers.
* *
* @param name of the HTTP header. * @param name
* @param value of the HTTP header. * of the HTTP header.
*/ * @param value
public Header(String name, String value) { * of the HTTP header.
this.name = name; */
this.value = value; public Header(String name, String value) {
} this.name = name;
this.value = value;
}
/** /**
* Constructs a default constructor of 'Header' * Constructs a default constructor of 'Header'
*/ */
public Header() { public Header() {
} }
/** /**
* Returns the 'name' data field of the object of this class. * Returns the 'name' data field of the object of this class.
* *
* @return name data field of this class * @return name data field of this class
*/ */
public String getName() { public String getName() {
return this.name; return this.name;
} }
/** /**
* Sets 'name' data field of this class. * Sets 'name' data field of this class.
* *
* @param name * @param name
*/ */
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
/** /**
* Returns the 'value' data field of the object of this class. * Returns the 'value' data field of the object of this class.
* *
* @return value data field of this class * @return value data field of this class
*/ */
public String getValue() { public String getValue() {
return this.value; return this.value;
} }
/** /**
* Sets 'value' data field of this class. * Sets 'value' data field of this class.
* *
* @param value * @param value
*/ */
public void setValue(String value) { public void setValue(String value) {
this.value = value; this.value = value;
} }
} }
@@ -3,52 +3,90 @@ package org.mifosplatform.batch.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.exception.PlatformInternalServerException;
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException; import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
import org.mifosplatform.infrastructure.core.exceptionmapper.PlatformApiDataValidationExceptionMapper;
import org.mifosplatform.infrastructure.core.exceptionmapper.PlatformDataIntegrityExceptionMapper;
import org.mifosplatform.infrastructure.core.exceptionmapper.PlatformInternalServerExceptionMapper;
import org.mifosplatform.infrastructure.core.exceptionmapper.PlatformResourceNotFoundExceptionMapper;
import org.mifosplatform.infrastructure.core.exceptionmapper.UnsupportedParameterExceptionMapper;
import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/** /**
* Provides an Error Handler method that returns an object of type {@link ErrorInfo} * Provides an Error Handler method that returns an object of type
* to the CommandStrategy which raised the exception. This class uses various subclasses * {@link ErrorInfo} to the CommandStrategy which raised the exception. This
* of RuntimeException to check the kind of exception raised and provide appropriate * class uses various subclasses of RuntimeException to check the kind of
* status and error codes for each one of the raised exception. * exception raised and provide appropriate status and error codes for each one
* of the raised exception.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
* @see org.mifosplatform.batch.command.CommandStrategy * @see org.mifosplatform.batch.command.CommandStrategy
* @see org.mifosplatform.batch.command.internal.CreateClientCommandStrategy * @see org.mifosplatform.batch.command.internal.CreateClientCommandStrategy
*/ */
public class ErrorHandler extends RuntimeException{ public class ErrorHandler extends RuntimeException {
/** private static Gson jsonHelper = new GsonBuilder().setPrettyPrinting().create();
* Sole Constructor
*/ /**
ErrorHandler() { * Sole Constructor
*/
ErrorHandler() {
super(); super();
} }
/** /**
* Returns an object of ErrorInfo type containing the information regarding * Returns an object of ErrorInfo type containing the information regarding
* the raised error. * the raised error.
* *
* @param exception * @param exception
* @return ErrorInfo * @return ErrorInfo
*/ */
public static ErrorInfo handler(RuntimeException exception) { public static ErrorInfo handler(final RuntimeException exception) {
if(exception instanceof AbstractPlatformResourceNotFoundException) { if (exception instanceof AbstractPlatformResourceNotFoundException) {
return new ErrorInfo(404, 1001, "Requested Resource was not Found: Check for any unavailable Parameters");
} else if(exception instanceof UnsupportedParameterException) {
return new ErrorInfo(400, 2001, "UnsupportedParameterException: Check for any unsupported Parameters");
} else if(exception instanceof PlatformApiDataValidationException) {
return new ErrorInfo(400, 2002, "ApiDataValidationException: Re-Check the input data");
} else if(exception instanceof PlatformDataIntegrityException) {
return new ErrorInfo(403, 3001, "DataIntegrityException: Check for any duplicate data");
} else if(exception instanceof TransactionException) {
return new ErrorInfo(400, 4001, "TransactionException: One or more of your requests were unsuccessful," +
" reverting back all the transactions in this Batch Request");
}
return new ErrorInfo(500, 9999, "Internal Server Error"); final PlatformResourceNotFoundExceptionMapper mapper = new PlatformResourceNotFoundExceptionMapper();
final String errorBody = jsonHelper
.toJson(mapper.toResponse((AbstractPlatformResourceNotFoundException) exception).getEntity());
} return new ErrorInfo(404, 1001, errorBody);
} else if (exception instanceof UnsupportedParameterException) {
final UnsupportedParameterExceptionMapper mapper = new UnsupportedParameterExceptionMapper();
final String errorBody = jsonHelper.toJson(mapper.toResponse((UnsupportedParameterException) exception).getEntity());
return new ErrorInfo(400, 2001, errorBody);
} else if (exception instanceof PlatformApiDataValidationException) {
final PlatformApiDataValidationExceptionMapper mapper = new PlatformApiDataValidationExceptionMapper();
final String errorBody = jsonHelper.toJson(mapper.toResponse((PlatformApiDataValidationException) exception).getEntity());
return new ErrorInfo(400, 2002, errorBody);
} else if (exception instanceof PlatformDataIntegrityException) {
final PlatformDataIntegrityExceptionMapper mapper = new PlatformDataIntegrityExceptionMapper();
final String errorBody = jsonHelper.toJson(mapper.toResponse((PlatformDataIntegrityException) exception).getEntity());
return new ErrorInfo(403, 3001, errorBody);
} else if (exception instanceof TransactionException) {
return new ErrorInfo(400, 4001, "Exception " + exception.getCause() + " " + exception.getMessage());
} else if (exception instanceof PlatformInternalServerException) {
final PlatformInternalServerExceptionMapper mapper = new PlatformInternalServerExceptionMapper();
final String errorBody = jsonHelper.toJson(mapper.toResponse((PlatformInternalServerException) exception).getEntity());
return new ErrorInfo(500, 5001, errorBody);
}
return new ErrorInfo(500, 9999, exception.toString() + " " + exception.getStackTrace());
}
} }
@@ -72,8 +72,8 @@ public final class ErrorInfo {
} }
/** /**
* Getter method to provide the message of the error for an object * Getter method to provide the message of the error for an object of this
* of this type. * type.
* *
* @return String * @return String
*/ */
@@ -82,12 +82,11 @@ public final class ErrorInfo {
} }
/** /**
* Setter method to set the message of the error for an object * Setter method to set the message of the error for an object of this type.
* of this type.
* *
* @param message * @param message
*/ */
public void setMessage(final String message) { public void setMessage(final String message) {
this.message = message; this.message = message;
} }
} }
@@ -10,7 +10,8 @@ import org.springframework.stereotype.Component;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
/** /**
* Extends {@link org.mifosplatform.infrastructure.core.serialization.FromJsonHelper} to * Extends
* {@link org.mifosplatform.infrastructure.core.serialization.FromJsonHelper} to
* de-serialize the incoming String into a JSON List of type * de-serialize the incoming String into a JSON List of type
* {@link org.mifosplatform.batch.domain.BatchRequest} * {@link org.mifosplatform.batch.domain.BatchRequest}
* *
@@ -22,16 +23,16 @@ import com.google.gson.reflect.TypeToken;
@Component @Component
public class BatchRequestJsonHelper extends FromJsonHelper { public class BatchRequestJsonHelper extends FromJsonHelper {
/** /**
* Returns a list of batchRequests after de-serializing it from the input * Returns a list of batchRequests after de-serializing it from the input
* JSON string. * JSON string.
* *
* @param json * @param json
* @return List<BatchRequest> * @return List<BatchRequest>
*/ */
public List<BatchRequest> extractList(final String json) { public List<BatchRequest> extractList(final String json) {
final Type listType = new TypeToken<List<BatchRequest>>(){}.getType(); final Type listType = new TypeToken<List<BatchRequest>>() {}.getType();
final List<BatchRequest> requests = super.getGsonConverter().fromJson(json, listType); final List<BatchRequest> requests = super.getGsonConverter().fromJson(json, listType);
return requests; return requests;
} }
} }
@@ -2,12 +2,14 @@ package org.mifosplatform.batch.service;
import java.util.List; import java.util.List;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
/** /**
* Provides an interface for service class, that implements the method to * Provides an interface for service class, that implements the method to handle
* handle separate Batch Requests. * separate Batch Requests.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -17,25 +19,29 @@ import org.mifosplatform.batch.domain.BatchResponse;
*/ */
public interface BatchApiService { public interface BatchApiService {
/** /**
* Returns a list of {@link org.mifosplatform.batch.domain.BatchResponse}s by getting * Returns a list of {@link org.mifosplatform.batch.domain.BatchResponse}s
* the appropriate CommandStrategy for every {@link org.mifosplatform.batch.domain.BatchRequest}. * by getting the appropriate CommandStrategy for every
* It will be used when the Query Parameter "enclosingTransaction "is set to 'false'. * {@link org.mifosplatform.batch.domain.BatchRequest}. It will be used when
* * the Query Parameter "enclosingTransaction "is set to 'false'.
* @param requestList *
* @return List<BatchResponse> * @param requestList
*/ * @param uriInfo
List<BatchResponse> handleBatchRequestsWithoutEnclosingTransaction(List<BatchRequest> requestList); * @return List<BatchResponse>
*/
List<BatchResponse> handleBatchRequestsWithoutEnclosingTransaction(List<BatchRequest> requestList, UriInfo uriInfo);
/** /**
* returns a list of {@link org.mifosplatform.batch.domain.BatchResponse}s by getting * returns a list of {@link org.mifosplatform.batch.domain.BatchResponse}s
* the appropriate CommandStrategy for every {@link org.mifosplatform.batch.domain.BatchRequest}. * by getting the appropriate CommandStrategy for every
* It will be used when the Query Parameter "enclosingTransaction "is set to 'true'. If one or * {@link org.mifosplatform.batch.domain.BatchRequest}. It will be used when
* more of the requests are not completed properly then whole of the transaction will be rolled back * the Query Parameter "enclosingTransaction "is set to 'true'. If one or
* properly. * more of the requests are not completed properly then whole of the
* * transaction will be rolled back properly.
* @param requestList *
* @return List<BatchResponse> * @param requestList
*/ * @param uriInfo
List<BatchResponse> handleBatchRequestsWithEnclosingTransaction(List<BatchRequest> requestList); * @return List<BatchResponse>
*/
List<BatchResponse> handleBatchRequestsWithEnclosingTransaction(List<BatchRequest> requestList, UriInfo uriInfo);
} }
@@ -1,26 +1,36 @@
package org.mifosplatform.batch.service; package org.mifosplatform.batch.service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.batch.command.CommandContext; import org.mifosplatform.batch.command.CommandContext;
import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategy;
import org.mifosplatform.batch.command.CommandStrategyProvider; import org.mifosplatform.batch.command.CommandStrategyProvider;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
import org.mifosplatform.batch.exception.ErrorHandler;
import org.mifosplatform.batch.exception.ErrorInfo; import org.mifosplatform.batch.exception.ErrorInfo;
import org.mifosplatform.batch.service.ResolutionHelper.BatchRequestNode;
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; import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.gson.Gson; import com.google.gson.Gson;
import edu.emory.mathcs.backport.java.util.Collections;
/** /**
* Implementation for {@link BatchApiService} to iterate through all the incoming * Implementation for {@link BatchApiService} to iterate through all the
* requests and obtain the appropriate CommandStrategy from CommandStrategyProvider. * incoming requests and obtain the appropriate CommandStrategy from
* CommandStrategyProvider.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* *
@@ -29,101 +39,167 @@ import com.google.gson.Gson;
* @see org.mifosplatform.batch.command.CommandStrategyProvider * @see org.mifosplatform.batch.command.CommandStrategyProvider
*/ */
@Service @Service
public class BatchApiServiceImpl implements BatchApiService{ public class BatchApiServiceImpl implements BatchApiService {
private final CommandStrategyProvider strategyProvider; private final CommandStrategyProvider strategyProvider;
private final ResolutionHelper resolutionHelper; private final ResolutionHelper resolutionHelper;
private final TransactionTemplate transactionTemplate;
private List<BatchResponse> checkList = new ArrayList<>();
/** /**
* Constructs a 'BatchApiServiceImpl' with an argument of * Constructs a 'BatchApiServiceImpl' with an argument of
* {@link org.mifosplatform.batch.command.CommandStrategyProvider} type. * {@link org.mifosplatform.batch.command.CommandStrategyProvider} type.
* *
* @param strategyProvider * @param strategyProvider
*/ * @param resolutionHelper
@Autowired * @param transactionTemplate
public BatchApiServiceImpl(final CommandStrategyProvider strategyProvider, */
final ResolutionHelper resolutionHelper) { @Autowired
this.strategyProvider = strategyProvider; public BatchApiServiceImpl(final CommandStrategyProvider strategyProvider, final ResolutionHelper resolutionHelper,
this.resolutionHelper = resolutionHelper; final TransactionTemplate transactionTemplate) {
} this.strategyProvider = strategyProvider;
this.resolutionHelper = resolutionHelper;
this.transactionTemplate = transactionTemplate;
}
/** /**
* Returns the response list by getting a proper {@link org.mifosplatform.batch.command.CommandStrategy}. * Returns the response list by getting a proper
* execute() method of acquired commandStrategy is then provided with the separate Request. * {@link org.mifosplatform.batch.command.CommandStrategy}. execute() method
* * of acquired commandStrategy is then provided with the separate Request.
* @param requestList *
* @return List<BatchResponse> * @param requestList
*/ * @param uriInfo
private List<BatchResponse> handleBatchRequests(final List<BatchRequest> requestList) { * @return List<BatchResponse>
*/
private List<BatchResponse> handleBatchRequests(final List<BatchRequest> requestList, final UriInfo uriInfo) {
final List<BatchResponse> responseList = new ArrayList<>(); final List<BatchResponse> responseList = new ArrayList<>(requestList.size());
final ConcurrentHashMap<Long, ArrayList<BatchRequest>> requestMap = this.resolutionHelper.getDependencyMap(requestList); final List<BatchRequestNode> batchRequestNodes = this.resolutionHelper.getDependingRequests(requestList);
checkList.clear();
for(ConcurrentHashMap.Entry<Long, ArrayList<BatchRequest>> entry : requestMap.entrySet()) { for (BatchRequestNode rootNode : batchRequestNodes) {
final BatchRequest rootRequest = rootNode.getRequest();
final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy(CommandContext
.resource(rootRequest.getRelativeUrl()).method(rootRequest.getMethod()).build());
final BatchResponse rootResponse = commandStrategy.execute(rootRequest, uriInfo);
final ArrayList<BatchRequest> mappedList = entry.getValue(); responseList.add(rootResponse);
responseList.addAll(this.processChildRequests(rootNode, rootResponse, uriInfo));
}
for(BatchRequest br: mappedList) { Collections.sort(responseList, new Comparator<BatchResponse>() {
//If this request is dependent on any other request in the list @Override
if(br.getReference() != null) { public int compare(BatchResponse source, BatchResponse testee) {
return source.getRequestId().compareTo(testee.getRequestId());
}
});
BatchResponse parentResponse = new BatchResponse(); checkList = responseList;
return responseList;
//Fetch the response of the request on which it is dependent from responseList }
for(final BatchResponse res: responseList) {
if(res.getRequestId().equals(br.getReference())) { private List<BatchResponse> processChildRequests(final BatchRequestNode rootRequest, BatchResponse rootResponse, UriInfo uriInfo) {
parentResponse = res;
}
}
//If parentResponse has returned a successful request final List<BatchResponse> childResponses = new ArrayList<>();
if(parentResponse.getStatusCode() == 200) { if (rootRequest.getChildRequests().size() > 0) {
br = this.resolutionHelper.resoluteRequest(br, parentResponse);
} else {
//Something went wrong with the parent request, create a response with status code 409
final BatchResponse response = new BatchResponse();
response.setRequestId(br.getRequestId());
response.setStatusCode(Status.CONFLICT.getStatusCode());
//Some detail information about the error for (BatchRequestNode childNode : rootRequest.getChildRequests()) {
final ErrorInfo conflictError = new ErrorInfo(Status.CONFLICT.getStatusCode(), 8001,
"Parent request with id " + parentResponse.getRequestId() + " was erroneous!");
response.setBody(conflictError.getMessage());
responseList.add(response);
//Proceed with next list entry BatchRequest childRequest = childNode.getRequest();
continue; BatchResponse childResponse;
}
}
final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy(CommandContext. try {
resource(br.getRelativeUrl()).method(br.getMethod()).build());
final BatchResponse response = commandStrategy.execute(br); if (rootResponse.getStatusCode().equals(200)) {
childRequest = this.resolutionHelper.resoluteRequest(childRequest, rootResponse);
final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy(CommandContext
.resource(childRequest.getRelativeUrl()).method(childRequest.getMethod()).build());
responseList.add(response); childResponse = commandStrategy.execute(childRequest, uriInfo);
}
} } else {
// Something went wrong with the parent request, create
// a response with status code 409
childResponse = new BatchResponse();
childResponse.setRequestId(childRequest.getRequestId());
childResponse.setStatusCode(Status.CONFLICT.getStatusCode());
return responseList; // Some detail information about the error
} final ErrorInfo conflictError = new ErrorInfo(Status.CONFLICT.getStatusCode(), 8001, "Parent request with id "
+ rootResponse.getRequestId() + " was erroneous!");
childResponse.setBody(conflictError.getMessage());
}
childResponses.addAll(this.processChildRequests(childNode, childResponse, uriInfo));
@Override } catch (Throwable ex) {
public List<BatchResponse> handleBatchRequestsWithoutEnclosingTransaction(final
List<BatchRequest> requestList) {
return handleBatchRequests(requestList); childResponse = new BatchResponse();
} childResponse.setRequestId(childRequest.getRequestId());
childResponse.setStatusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
childResponse.setBody(ex.getMessage());
@Transactional }
@Override
public List<BatchResponse> handleBatchRequestsWithEnclosingTransaction( final
List<BatchRequest> requestList) {
return handleBatchRequests(requestList); childResponses.add(childResponse);
} }
}
return childResponses;
}
@Override
public List<BatchResponse> handleBatchRequestsWithoutEnclosingTransaction(final List<BatchRequest> requestList, UriInfo uriInfo) {
return handleBatchRequests(requestList, uriInfo);
}
@Override
public List<BatchResponse> handleBatchRequestsWithEnclosingTransaction(final List<BatchRequest> requestList, final UriInfo uriInfo) {
try {
return this.transactionTemplate.execute(new TransactionCallback<List<BatchResponse>>() {
@Override
public List<BatchResponse> doInTransaction(TransactionStatus status) {
try {
return handleBatchRequests(requestList, uriInfo);
} catch (RuntimeException ex) {
ErrorInfo e = ErrorHandler.handler(ex);
BatchResponse errResponse = new BatchResponse();
errResponse.setStatusCode(e.getStatusCode());
errResponse.setBody(e.getMessage());
List<BatchResponse> errResponseList = new ArrayList<>();
errResponseList.add(errResponse);
status.setRollbackOnly();
return errResponseList;
}
}
});
} catch (TransactionException ex) {
ErrorInfo e = ErrorHandler.handler(ex);
BatchResponse errResponse = new BatchResponse();
errResponse.setStatusCode(e.getStatusCode());
for (BatchResponse res : checkList) {
if (!res.getStatusCode().equals(200)) {
errResponse.setBody("Transaction is being rolled back. First erroneous request: \n" + new Gson().toJson(res));
break;
}
}
checkList.clear();
List<BatchResponse> errResponseList = new ArrayList<>();
errResponseList.add(errResponse);
return errResponseList;
}
}
} }
@@ -3,7 +3,6 @@ package org.mifosplatform.batch.service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.domain.BatchResponse;
@@ -11,14 +10,15 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.jayway.jsonpath.JsonModel; import com.jayway.jsonpath.JsonModel;
/** /**
* Provides methods to create dependency map among the various batchRequests. * Provides methods to create dependency map among the various batchRequests. It
* It also provides method that takes care of dependency resolution among * also provides method that takes care of dependency resolution among related
* related requests. * requests.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
* @see BatchApiServiceImpl * @see BatchApiServiceImpl
@@ -26,151 +26,185 @@ import com.jayway.jsonpath.JsonModel;
@Component @Component
public class ResolutionHelper { public class ResolutionHelper {
private FromJsonHelper fromJsonHelper; /**
* Provides a Node like object for the request tree.
*
* @author Rishabh shukla
*
*/
public class BatchRequestNode {
@Autowired private BatchRequest request;
public ResolutionHelper(final FromJsonHelper fromJsonHelper) { private final List<BatchRequestNode> childRequests = new ArrayList<>();
this.fromJsonHelper = fromJsonHelper;
}
/** public BatchRequestNode() {
* Returns a map containing requests that are divided in accordance of dependency relations super();
* among them. Each different list is identified with a "Key" which is the "requestId" of }
* the request at topmost level in dependency hierarchy of that particular list.
*
* @param requestList
* @return ConcurrentHashMap<Long, List<BatchRequest>>
*/
public ConcurrentHashMap<Long, ArrayList<BatchRequest>> getDependencyMap(final List<BatchRequest> requestList) {
//Create a map with a '-1L' which will contain a list of independent requests public BatchRequest getRequest() {
final ConcurrentHashMap<Long, ArrayList<BatchRequest>> dependencyMap = new ConcurrentHashMap<>(); return this.request;
}
dependencyMap.put(-1L, new ArrayList<BatchRequest>()); public void setRequest(BatchRequest request) {
this.request = request;
}
//For every request in the requestList public List<BatchRequestNode> getChildRequests() {
for(final BatchRequest br : requestList) { return this.childRequests;
}
final Long reference = br.getReference(); public void addChildRequest(final BatchRequestNode batchRequest) {
this.childRequests.add(batchRequest);
}
//If current request is dependent on some other request }
if(reference != null) {
//Then find the request on which br is dependent on private FromJsonHelper fromJsonHelper;
for(final BatchRequest parentRequest : requestList) {
//If request on which br is dependent is found @Autowired
if(parentRequest.getRequestId().equals(reference)) { public ResolutionHelper(final FromJsonHelper fromJsonHelper) {
this.fromJsonHelper = fromJsonHelper;
}
//Then start the process of adding it to one of the key in the dependencyMap /**
* Returns a map containing requests that are divided in accordance of
* dependency relations among them. Each different list is identified with a
* "Key" which is the "requestId" of the request at topmost level in
* dependency hierarchy of that particular list.
*
* @param batchRequests
* @return List<ArrayList<BatchRequestNode>>
*/
public List<BatchRequestNode> getDependingRequests(final List<BatchRequest> batchRequests) {
final List<BatchRequestNode> rootRequests = new ArrayList<>();
//If referenced request is in the list with key "-1L" for (BatchRequest batchRequest : batchRequests) {
if(dependencyMap.get(-1L).contains(parentRequest)) { if (batchRequest.getReference() == null) {
final BatchRequestNode node = new BatchRequestNode();
node.setRequest(batchRequest);
rootRequests.add(node);
} else {
this.addDependingRequest(batchRequest, rootRequests);
}
}
//Then create a new list with key = reference return rootRequests;
final ArrayList<BatchRequest> newList = new ArrayList<>(); }
newList.add(parentRequest);
newList.add(br);
//And add this list to the map private void addDependingRequest(final BatchRequest batchRequest, final List<BatchRequestNode> parentRequests) {
dependencyMap.put(reference, newList); for (BatchRequestNode batchRequestNode : parentRequests) {
if (batchRequestNode.getRequest().getRequestId().equals(batchRequest.getReference())) {
final BatchRequestNode dependingRequest = new BatchRequestNode();
dependingRequest.setRequest(batchRequest);
batchRequestNode.addChildRequest(dependingRequest);
} else {
addDependingRequest(batchRequest, batchRequestNode.getChildRequests());
}
}
}
//Also remove parentRequest from the list at '-1L' /**
final ArrayList<BatchRequest> listCopy = dependencyMap.get(-1L); * Returns a BatchRequest after dependency resolution. It takes a request
* and the response of the request it is dependent upon as its arguments and
* change the body or relativeUrl of the request according to parent
* Request.
*
* @param request
* @param lastResponse
* @return BatchRequest
*/
public BatchRequest resoluteRequest(final BatchRequest request, final BatchResponse parentResponse) {
listCopy.remove(parentRequest); // Create a duplicate request
final BatchRequest br = request;
dependencyMap.replace(-1L, listCopy); final JsonModel responseJsonModel = JsonModel.model(parentResponse.getBody());
}
//If referenced request is not in key '-1L'
else {
//Find the list in which it is present
for(final ConcurrentHashMap.Entry<Long, ArrayList<BatchRequest>> entry: dependencyMap.entrySet()) {
if(entry.getValue().contains(parentRequest)) {
final ArrayList<BatchRequest> listCopy = entry.getValue();
listCopy.add(br);
//Add this request to this list // Gets the body from current Request as a JsonObject
dependencyMap.replace(entry.getKey(), listCopy); final JsonObject jsonRequestBody = this.fromJsonHelper.parse(request.getBody()).getAsJsonObject();
}
}
}
}
} JsonObject jsonResultBody = new JsonObject();
}
//If request is independent then add it to '-1L'
else {
final ArrayList<BatchRequest> listCopy = dependencyMap.get(-1L);
listCopy.add(br);
dependencyMap.replace(-1L, listCopy); // Iterate through each element in the requestBody to find dependent
} // parameter
for (Entry<String, JsonElement> element : jsonRequestBody.entrySet()) {
final String key = element.getKey();
final JsonElement value = resolveDependentVariables(element, responseJsonModel);
jsonResultBody.add(key, value);
}
} // Set the body after dependency resolution
br.setBody(jsonResultBody.toString());
return dependencyMap; // Also check the relativeUrl for any dependency resolution
} String relativeUrl = request.getRelativeUrl();
/** if (relativeUrl.contains("$.")) {
* Returns a BatchRequest after dependency resolution. It takes a request and the
* response of the request it is dependent upon as its arguments and change the body
* or relativeUrl of the request according to parent Request.
*
* @param request
* @param lastResponse
* @return BatchRequest
*/
public BatchRequest resoluteRequest(final BatchRequest request,final BatchResponse parentResponse) {
//Create a duplicate request final String[] parameters = relativeUrl.split("/");
final BatchRequest br = request;
final JsonModel responseJsonModel = JsonModel.model(parentResponse.getBody()); for (String parameter : parameters) {
if (parameter.contains("$.")) {
final String resParamValue = responseJsonModel.get(parameter).toString();
relativeUrl = relativeUrl.replace(parameter, resParamValue);
br.setRelativeUrl(relativeUrl);
}
}
}
//Gets the body from current Request as a JsonObject return br;
final JsonObject jsonRequestBody = this.fromJsonHelper.parse(request.getBody()).getAsJsonObject(); }
JsonObject jsonResultBody = new JsonObject(); private JsonElement resolveDependentVariables(final Entry<String, JsonElement> entryElement, final JsonModel responseJsonModel) {
JsonElement value = null;
//Iterate through each element in the requestBody to find dependent parameter final JsonElement element = entryElement.getValue();
for(Entry<String, JsonElement> element: jsonRequestBody.entrySet()) {
String paramVal = element.getValue().getAsString(); if (element.isJsonObject()) {
final JsonObject jsObject = element.getAsJsonObject();
value = processJsonObject(jsObject, responseJsonModel);
} else if (element.isJsonArray()) {
final JsonArray jsElementArray = element.getAsJsonArray();
value = processJsonArray(jsElementArray, responseJsonModel);
} else {
value = resolveDependentVariable(element, responseJsonModel);
}
return value;
}
//If a dependent parameter is found private JsonElement processJsonObject(final JsonObject jsObject, final JsonModel responseJsonModel) {
if(paramVal.contains("$.")) { JsonObject valueObj = new JsonObject();
for (Entry<String, JsonElement> element : jsObject.entrySet()) {
final String key = element.getKey();
final JsonElement value = resolveDependentVariable(element.getValue(), responseJsonModel);
valueObj.add(key, value);
}
return valueObj;
}
//Get the value of the parameter from parent response private JsonArray processJsonArray(final JsonArray elementArray, final JsonModel responseJsonModel) {
final String resParamValue = responseJsonModel.get(paramVal).toString();
//Add the value after dependency resolution JsonArray valueArr = new JsonArray();
jsonResultBody.add(element.getKey(), this.fromJsonHelper.parse(resParamValue));
}
else {
jsonResultBody.add(element.getKey(), element.getValue());
}
}
//Set the body after dependency resolution for (JsonElement element : elementArray) {
br.setBody(jsonResultBody.toString()); if (element.isJsonObject()) {
final JsonObject jsObject = element.getAsJsonObject();
valueArr.add(processJsonObject(jsObject, responseJsonModel));
}
}
//Also check the relativeUrl for any dependency resolution return valueArr;
String relativeUrl = request.getRelativeUrl(); }
if(relativeUrl.contains("$.")) { private JsonElement resolveDependentVariable(final JsonElement element, final JsonModel responseJsonModel) {
JsonElement value = element;
final String parameter = relativeUrl.substring(relativeUrl.indexOf('/') + 1, relativeUrl.length()); String paramVal = element.getAsString();
if (paramVal.contains("$.")) {
//Get the value of the parameter from last response // Get the value of the parameter from parent response
final String resParamValue = responseJsonModel.get(parameter).toString(); final String resParamValue = responseJsonModel.get(paramVal).toString();
value = this.fromJsonHelper.parse(resParamValue);
relativeUrl = relativeUrl.replace(parameter, resParamValue); }
return value;
br.setRelativeUrl(relativeUrl); }
}
return br;
}
} }
@@ -31,7 +31,7 @@ public class PlatformInternalServerExceptionMapper implements ExceptionMapper<Pl
@Override @Override
public Response toResponse(final PlatformInternalServerException exception) { public Response toResponse(final PlatformInternalServerException exception) {
final ApiGlobalErrorResponse notFoundErrorResponse = ApiGlobalErrorResponse.notFound(exception.getGlobalisationMessageCode(), final ApiGlobalErrorResponse notFoundErrorResponse = ApiGlobalErrorResponse.serverSideError(exception.getGlobalisationMessageCode(),
exception.getDefaultUserMessage(), exception.getDefaultUserMessageArgs()); exception.getDefaultUserMessage(), exception.getDefaultUserMessageArgs());
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(notFoundErrorResponse).type(MediaType.APPLICATION_JSON).build(); return Response.status(Status.INTERNAL_SERVER_ERROR).entity(notFoundErrorResponse).type(MediaType.APPLICATION_JSON).build();
} }
@@ -494,6 +494,9 @@ public class Loan extends AbstractPersistable<Long> {
// NOTE: must add new loan charge to set of loan charges before // NOTE: must add new loan charge to set of loan charges before
// reporcessing the repayment schedule. // reporcessing the repayment schedule.
if(this.charges == null) {
this.charges = new HashSet<>();
}
this.charges.add(loanCharge); this.charges.add(loanCharge);
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
@@ -28,6 +28,11 @@
<property name="entityManagerFactory" ref="entityManagerFactory" /> <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean> </bean>
<!-- Create instance of transaction template for programmatic transaction manipulation -->
<bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<jee:jndi-lookup jndi-name="java:comp/env/jdbc/mifosplatform-tenants" <jee:jndi-lookup jndi-name="java:comp/env/jdbc/mifosplatform-tenants"
id="tenantDataSourceJndi" /> id="tenantDataSourceJndi" />
</beans> </beans>
@@ -3,6 +3,8 @@ package org.mifosplatform.batch;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.ws.rs.core.UriInfo;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchRequest;
@@ -11,33 +13,37 @@ import org.mifosplatform.batch.service.BatchApiService;
import org.mockito.Mockito; import org.mockito.Mockito;
/** /**
* Runs a unit test for BatchApiResource by mocking the * Runs a unit test for BatchApiResource by mocking the BatchRequest and
* BatchRequest and BatchApiService objects. * BatchApiService objects.
* *
* @author Rishabh Shukla * @author Rishabh Shukla
*/ */
public class BatchBuilder { public class BatchBuilder {
//Verify a non-empty response by BatchApiResource. // Verify a non-empty response by BatchApiResource.
@Test @Test
public void batchApiTest() { public void batchApiTest() {
//Mock a BatchRequest objects // Mock a BatchRequest objects
final BatchRequest batchTest = Mockito.mock(BatchRequest.class); final BatchRequest batchTest = Mockito.mock(BatchRequest.class);
//Mock a BatchApiService object // Mock a BatchApiService object
final BatchApiService serviceTest = Mockito.mock(BatchApiService.class); final BatchApiService serviceTest = Mockito.mock(BatchApiService.class);
final List<BatchRequest> requestList = new ArrayList<>(); // Mock a UriInfo object
requestList.add(batchTest); final UriInfo uriInfo = Mockito.mock(UriInfo.class);
//Call the BatchApiService using mocked objects final List<BatchRequest> requestList = new ArrayList<>();
final List<BatchResponse> result = serviceTest.handleBatchRequestsWithoutEnclosingTransaction(requestList); requestList.add(batchTest);
//Verifies whether handleBatchRequests() function of BatchApiService was called // Call the BatchApiService using mocked objects
Mockito.verify(serviceTest).handleBatchRequestsWithoutEnclosingTransaction(requestList); final List<BatchResponse> result = serviceTest.handleBatchRequestsWithoutEnclosingTransaction(requestList, uriInfo);
//Verifies a non-empty response by the BatchApiResource // Verifies whether handleBatchRequests() function of BatchApiService
Assert.assertNotNull(result); // was called
} Mockito.verify(serviceTest).handleBatchRequestsWithoutEnclosingTransaction(requestList, uriInfo);
// Verifies a non-empty response by the BatchApiResource
Assert.assertNotNull(result);
}
} }