diff --git a/mifosng-provider/build.gradle b/mifosng-provider/build.gradle index f4f2aaaf1..dd206629a 100644 --- a/mifosng-provider/build.gradle +++ b/mifosng-provider/build.gradle @@ -125,7 +125,7 @@ war { license { header rootProject.file('../LICENSE.md') - excludes(["**/*.mustache", "**/*.sql", "**/package-info.java", "**/keystore.jks"]) + excludes(["**/*.html", "**/*.mustache", "**/*.sql", "**/package-info.java", "**/keystore.jks"]) strictCheck true } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/template/domain/Template.java b/mifosng-provider/src/main/java/org/mifosplatform/template/domain/Template.java index 5fe32480c..98b08cdbb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/template/domain/Template.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/template/domain/Template.java @@ -60,7 +60,6 @@ public class Template extends AbstractPersistable { } protected Template() { - // } public static Template fromJson(final JsonCommand command) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java b/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java index 0ef0454dd..41876d605 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java @@ -21,10 +21,13 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import org.codehaus.jackson.map.ObjectMapper; import org.mifosplatform.template.domain.Template; import org.mifosplatform.template.domain.TemplateFunctions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; @@ -34,6 +37,7 @@ import com.github.mustachejava.MustacheFactory; @Service public class TemplateMergeService { + private final static Logger logger = LoggerFactory.getLogger(TemplateMergeService.class); // private final FromJsonHelper fromApiJsonHelper; private Map scopes; @@ -49,7 +53,6 @@ public class TemplateMergeService { } public String compile(final Template template, final Map scopes) throws MalformedURLException, IOException { - this.scopes = scopes; this.scopes.put("static", new TemplateFunctions()); @@ -59,19 +62,19 @@ public class TemplateMergeService { final Map mappers = getCompiledMapFromMappers(template.getMappersAsMap()); this.scopes.putAll(mappers); + expandMapArrays(scopes); + final StringWriter stringWriter = new StringWriter(); mustache.execute(stringWriter, this.scopes); return stringWriter.toString(); } - private Map getCompiledMapFromMappers(final Map data) { - + private Map getCompiledMapFromMappers(final Map data) { final MustacheFactory mf = new DefaultMustacheFactory(); if (data != null) { for (final Map.Entry entry : data.entrySet()) { - final Mustache mappersMustache = mf.compile(new StringReader(entry.getValue()), ""); final StringWriter stringWriter = new StringWriter(); @@ -82,10 +85,8 @@ public class TemplateMergeService { } try { this.scopes.put(entry.getKey(), getMapFromUrl(url)); - } catch (final MalformedURLException e) { - e.printStackTrace(); } catch (final IOException e) { - e.printStackTrace(); + logger.error("getCompiledMapFromMappers() failed", e); } } } @@ -94,7 +95,6 @@ public class TemplateMergeService { @SuppressWarnings("unchecked") private Map getMapFromUrl(final String url) throws MalformedURLException, IOException { - final HttpURLConnection connection = getConnection(url); final String response = getStringFromInputStream(connection.getInputStream()); @@ -108,13 +108,11 @@ public class TemplateMergeService { } private HttpURLConnection getConnection(final String url) { - if (this.authToken == null) { final String name = SecurityContextHolder.getContext().getAuthentication().getName(); final String password = SecurityContextHolder.getContext().getAuthentication().getCredentials().toString(); Authenticator.setDefault(new Authenticator() { - @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(name, password.toCharArray()); @@ -132,23 +130,15 @@ public class TemplateMergeService { connection.setDoInput(true); - } catch (final MalformedURLException e) { - e.printStackTrace(); - } catch (final IOException e) { - e.printStackTrace(); - } catch (final KeyManagementException e) { - e.printStackTrace(); - } catch (final NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (final KeyStoreException e) { - e.printStackTrace(); + } catch (IOException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { + logger.error("getConnection() failed, return null", e); } return connection; } + // TODO Replace this with appropriate alternative available in Guava private static String getStringFromInputStream(final InputStream is) { - BufferedReader br = null; final StringBuilder sb = new StringBuilder(); @@ -161,7 +151,7 @@ public class TemplateMergeService { } } catch (final IOException e) { - // FIXME - HANDLE THIS CASE + logger.error("getStringFromInputStream() failed", e); } finally { if (br != null) { try { @@ -174,4 +164,27 @@ public class TemplateMergeService { return sb.toString(); } + + @SuppressWarnings("unchecked") + private void expandMapArrays(Object value) { + if (value instanceof Map) { + Map valueAsMap = (Map) value; + for (Entry valueAsMapEntry : valueAsMap.entrySet()) { + Object valueAsMapEntryValue = valueAsMapEntry.getValue(); + if (valueAsMapEntryValue instanceof Map) { // JSON Object + expandMapArrays(valueAsMapEntryValue); + } else if (valueAsMapEntryValue instanceof Iterable) { // JSON Array + Iterable valueAsMapEntryValueIterable = (Iterable) valueAsMapEntryValue; + String valueAsMapEntryKey = valueAsMapEntry.getKey(); + int i = 0; + for (Object object : valueAsMapEntryValueIterable) { + valueAsMap.put(valueAsMapEntryKey + "#" + i, object); + ++i; + expandMapArrays(object); + } + } + } + } + } + } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/template/TemplateMergeServiceTest.java b/mifosng-provider/src/test/java/org/mifosplatform/template/TemplateMergeServiceTest.java index cf783236c..d73791b08 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/template/TemplateMergeServiceTest.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/template/TemplateMergeServiceTest.java @@ -7,78 +7,101 @@ package org.mifosplatform.template; import static org.junit.Assert.assertEquals; -import java.io.DataInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; +import java.lang.reflect.Type; +import java.net.MalformedURLException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import org.joda.time.LocalDate; -import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.portfolio.loanaccount.LoanScheduleTestDataHelper; import org.mifosplatform.portfolio.loanaccount.MonetaryCurrencyBuilder; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.template.domain.Template; +import org.mifosplatform.template.domain.TemplateMapper; import org.mifosplatform.template.service.TemplateMergeService; +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + public class TemplateMergeServiceTest { - private Template template; - private final static String TEST_FILE = "src/test/resources/template.mustache"; - private static TemplateMergeService tms; + private TemplateMergeService tms = new TemplateMergeService(); - @BeforeClass - public static void init() { - tms = new TemplateMergeService( -// new FromJsonHelper() - ); - } - - @Ignore @Test public void compileHelloTemplate() throws Exception { - final String name = "TemplateName"; - final String text = "Hello Test for Template {{template.name}}!"; + String templateText = "Hello Test for Template {{file.name}}!"; - this.template = new Template(name, text, null, null, null); + File file = new File("hello"); + Map scopes = new HashMap<>(); + scopes.put("file", file); - final HashMap scopes = new HashMap<>(); - scopes.put("template", this.template); - - String output = ""; - output = tms.compile(this.template, scopes); - assertEquals("Hello Test for Template TemplateName!", output); + String output = compileTemplateText(templateText, scopes); + assertEquals("Hello Test for Template hello!", output); } - @Ignore @Test public void compileLoanSummary() throws IOException { + LocalDate july2nd = new LocalDate(2012, 7, 2); + MonetaryCurrency usDollars = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build(); + List installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars); - final LocalDate july2nd = new LocalDate(2012, 7, 2); - final MonetaryCurrency usDollars = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build(); - final List installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars); - - final File file = new File(TEST_FILE); - final DataInputStream dis = new DataInputStream(new FileInputStream(file)); - final byte[] bytes = new byte[(int) file.length()]; - dis.readFully(bytes); - final String content = new String(bytes, "UTF-8"); - - this.template = new Template("TemplateName", content, null, null, null); - - final HashMap scopes = new HashMap<>(); + Map scopes = new HashMap<>(); scopes.put("installments", installments); - final String output = tms.compile(this.template, scopes); + String templateText = Resources.toString(Resources.getResource("template.mustache"), Charsets.UTF_8); + String expectedOutput = Resources.toString(Resources.getResource("template-expected.html"), Charsets.UTF_8); - dis.close(); - - System.out.println(output); - dis.close(); + String output = compileTemplateText(templateText, scopes); + // System.out.println(output); + assertEquals(expectedOutput, output); } + @Test + public void arrayUsingLoop() throws Exception { + String templateText = "Hello Test for Template{{#data.name}} {{.}}{{/data.name}}!"; + String jsonData = "{\"name\": [ \"Michael\", \"Terence\" ] }"; + String expectedOutput = "Hello Test for Template Michael Terence!"; + + Map scopes = new HashMap<>(); + scopes.put("data", createMapFromJSON(jsonData)); + + String output = compileTemplateText(templateText, scopes); + assertEquals(expectedOutput, output); + } + + @Test + public void arrayUsingIndex() throws Exception { + String templateText = "Hello Test for Template {{data.name#1}} & {{data.name#0}}!"; + String jsonData = "{\"name\": [ \"Michael\", \"Terence\" ] }"; + String expectedOutput = "Hello Test for Template Terence & Michael!"; + + Map scopes = new HashMap<>(); + scopes.put("data", createMapFromJSON(jsonData)); + + String output = compileTemplateText(templateText, scopes); + assertEquals(expectedOutput, output); + } + + protected String compileTemplateText(String templateText, Map scope) throws MalformedURLException, IOException { + List mappers = new ArrayList<>(); + Template template = new Template("TemplateName", templateText, null, null, mappers); + return tms.compile(template, scope); + } + + protected Map createMapFromJSON(String jsonText) { + Gson gson = new Gson(); + Type ssMap = new TypeToken>(){}.getType(); + JsonElement json = new JsonParser().parse(jsonText); + return gson.fromJson(json, ssMap); + } } diff --git a/mifosng-provider/src/test/resources/template-expected.html b/mifosng-provider/src/test/resources/template-expected.html new file mode 100644 index 000000000..a008dae34 --- /dev/null +++ b/mifosng-provider/src/test/resources/template-expected.html @@ -0,0 +1,152 @@ +

Sample Loan Summary & Repayment Schedule 

+ +

TABLE 1. LOAN SUMMARY 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Loan size and currency                    Total no. of installments                     
Interest rate  Installment frequency 
Commission fee Disbursement date  
Other fees Mandatory savings 
Purpose of loan Maturity date  
Grace period (if applies) Type of guarantee 
Penalty for late payment Maturity of the loan (in months)  
Penalty for pre-payment Total cost of loan (principle + int.+ fees)  
+ +

 

+ +

 

+ +

 

+ +

  

+ +

 

+ +

 

+ +

 

+ +

 

+ +

TABLE 2. REPAYMENT SCHEDULE 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateDisbursement Amount +

Principle Repayment

+
 Interest RepaymentCommission PaymentFeesTax PaymentTotal RepaymentTotal Balance Outstanding
2012-07-021NO fromDate AS IT CHANGES    
2012-08-022NO fromDate AS IT CHANGES    
2012-09-023NO fromDate AS IT CHANGES    
Total        
+ +

 

+ +

Client Signature ___________________________________________________________________ 

+ +

Credit Officer Signature _____________________________________________________________ 

+ +

Branch Director Signature ___________________________________________________________ 

+ +

 

+ +

 

+ +

Please contact the MacroDreams Customer Service Line if you have questions or complaints about our products, 

+ +

services, or staff: 555-555-5555. Open Monday to Saturday 7:00-20:00. All calls are free. 

diff --git a/mifosng-provider/src/test/resources/template.mustache b/mifosng-provider/src/test/resources/template.mustache index 9547a3e17..959b61f05 100644 --- a/mifosng-provider/src/test/resources/template.mustache +++ b/mifosng-provider/src/test/resources/template.mustache @@ -3,56 +3,56 @@

TABLE 1. LOAN SUMMARY 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Loan size and currency                    Total no. of installments                     
Interest rate  Installment frequency 
Commission fee Disbursement date  
Other fees Mandatory savings 
Purpose of loan Maturity date  
Grace period (if applies) Type of guarantee 
Penalty for late payment Maturity of the loan (in months)  
Penalty for pre-payment Total cost of loan (principle + int.+ fees)  
Loan size and currency                    Total no. of installments                     
Interest rate  Installment frequency 
Commission fee Disbursement date  
Other fees Mandatory savings 
Purpose of loan Maturity date  
Grace period (if applies) Type of guarantee 
Penalty for late payment Maturity of the loan (in months)  
Penalty for pre-payment Total cost of loan (principle + int.+ fees)  

 

@@ -74,45 +74,45 @@

TABLE 2. REPAYMENT SCHEDULE 

- - - - - - - - - - - - - {{#installments}} - - - - - - - - - - - - {{/installments}} - - - - - - - - - - - - + + + + + + + + + + + + + {{#installments}} + + + + + + + + + + + + {{/installments}} + + + + + + + + + + + +
DateDisbursement Amount -

Principle Repayment

-
 Interest RepaymentCommission PaymentFeesTax PaymentTotal RepaymentTotal Balance Outstanding
{{dueDate}}{{installmentNumber}}{{fromDate}}{{principal}} {{feeChargesCharged}}   
Total        
DateDisbursement Amount +

Principle Repayment

+
 Interest RepaymentCommission PaymentFeesTax PaymentTotal RepaymentTotal Balance Outstanding
{{dueDate}}{{installmentNumber}}NO fromDate AS IT CHANGES{{principal}} {{feeChargesCharged}}   
Total