This is an automated email from the ASF dual-hosted git repository.

awasum pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-reporting.git

commit bf26ed8722e81f105fee93d24d9b80f16460d8f6
Author: mgeiss <[email protected]>
AuthorDate: Fri Jun 30 22:51:43 2017 +0200

    added first report defition
---
 .../AbstractReportingSpecificationTest.java        | 117 ++++++++++++++++++++
 .../TestCustomerListReportSpecification.java       |  27 ++---
 component-test/src/main/resources/logback-test.xml |  37 +------
 .../reporting/service/ReportingConfiguration.java  |   5 +-
 .../provider/ReportSpecificationProvider.java      |  95 ++++++++++++++++
 .../CustomerListReportSpecification.java           | 120 +++++++++++++++++++++
 .../service/rest/ReportingRestController.java      |  92 ++++++++++++++--
 .../service/spi/DisplayableFieldBuilder.java       |  49 +++++++++
 .../service/spi/QueryParameterBuilder.java         |  71 ++++++++++++
 .../ReportingService.java => spi/Report.java}      |  29 +++--
 .../ReportSpecification.java}                      |  23 ++--
 11 files changed, 580 insertions(+), 85 deletions(-)

diff --git 
a/component-test/src/main/java/io/mifos/reporting/AbstractReportingSpecificationTest.java
 
b/component-test/src/main/java/io/mifos/reporting/AbstractReportingSpecificationTest.java
new file mode 100644
index 0000000..47fc8a7
--- /dev/null
+++ 
b/component-test/src/main/java/io/mifos/reporting/AbstractReportingSpecificationTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.reporting;
+
+import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
+import io.mifos.core.api.context.AutoUserContext;
+import io.mifos.core.lang.ApplicationName;
+import io.mifos.core.test.env.TestEnvironment;
+import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
+import io.mifos.core.test.fixture.cassandra.CassandraInitializer;
+import io.mifos.core.test.fixture.mariadb.MariaDBInitializer;
+import io.mifos.reporting.api.v1.client.ReportManager;
+import io.mifos.reporting.service.ReportingConfiguration;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.cloud.netflix.ribbon.RibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.security.interfaces.RSAPrivateKey;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+    webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
+    classes = {AbstractReportingSpecificationTest.TestConfiguration.class}
+)
+public class AbstractReportingSpecificationTest {
+  private static final String APP_NAME = "reporting-v1";
+  public static final String LOGGER_NAME = "test-logger";
+
+  @Configuration
+  @EnableFeignClients(basePackages = {"io.mifos.reporting.api.v1.client"})
+  @RibbonClient(name = APP_NAME)
+  @Import({ReportingConfiguration.class})
+  public static class TestConfiguration {
+    public TestConfiguration() {
+      super();
+    }
+
+    @Bean(name = LOGGER_NAME)
+    public Logger logger() {
+      return LoggerFactory.getLogger(LOGGER_NAME);
+    }
+  }
+
+  static final String TEST_USER = "homer";
+
+  private final static TestEnvironment testEnvironment = new 
TestEnvironment(APP_NAME);
+  private final static CassandraInitializer cassandraInitializer = new 
CassandraInitializer();
+  private final static MariaDBInitializer mariaDBInitializer = new 
MariaDBInitializer();
+  final static TenantDataStoreContextTestRule tenantDataStoreContext = 
TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, 
mariaDBInitializer);
+
+  @ClassRule
+  public static TestRule orderClassRules = RuleChain
+      .outerRule(testEnvironment)
+      .around(cassandraInitializer)
+      .around(mariaDBInitializer)
+      .around(tenantDataStoreContext);
+
+  @Rule
+  public final TenantApplicationSecurityEnvironmentTestRule 
tenantApplicationSecurityEnvironment
+      = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, 
this::waitForInitialize);
+
+  private AutoUserContext userContext;
+
+  @Autowired
+  ReportManager testSubject;
+
+  @Autowired
+  private ApplicationName applicationName;
+
+  @Autowired
+  @Qualifier(LOGGER_NAME)
+  Logger logger;
+
+  @Before
+  public void prepTest() {
+    userContext = 
tenantApplicationSecurityEnvironment.createAutoUserContext(TEST_USER);
+    final RSAPrivateKey tenantPrivateKey = 
tenantApplicationSecurityEnvironment.getSystemSecurityEnvironment().tenantPrivateKey();
+    logger.info("tenantPrivateKey = {}", tenantPrivateKey);
+  }
+
+  @After
+  public void cleanTest() {
+    userContext.close();
+  }
+
+  boolean waitForInitialize() {
+    return true;
+  }
+}
diff --git 
a/service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
 
b/component-test/src/main/java/io/mifos/reporting/TestCustomerListReportSpecification.java
similarity index 50%
copy from 
service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
copy to 
component-test/src/main/java/io/mifos/reporting/TestCustomerListReportSpecification.java
index 620dff3..dfabeaf 100644
--- 
a/service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
+++ 
b/component-test/src/main/java/io/mifos/reporting/TestCustomerListReportSpecification.java
@@ -13,22 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.reporting.service.internal.service;
+package io.mifos.reporting;
 
-import io.mifos.reporting.service.ServiceConstants;
-import org.slf4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Service;
+import io.mifos.reporting.api.v1.domain.ReportDefinition;
+import org.junit.Assert;
+import org.junit.Test;
 
-@Service
-public class ReportingService {
+import java.util.List;
 
-  private final Logger logger;
+public class TestCustomerListReportSpecification extends 
AbstractReportingSpecificationTest {
 
-  @Autowired
-  public ReportingService(@Qualifier(ServiceConstants.LOGGER_NAME) final 
Logger logger) {
+  public TestCustomerListReportSpecification() {
     super();
-    this.logger = logger;
+  }
+
+  @Test
+  public void shouldReturnReportDefinition() {
+    final List<ReportDefinition> reportDefinitions = 
super.testSubject.fetchReportDefinitions("Customer");
+    Assert.assertTrue(
+        reportDefinitions.stream().anyMatch(reportDefinition -> 
reportDefinition.getIdentifier().equals("Listing"))
+    );
   }
 }
diff --git a/component-test/src/main/resources/logback-test.xml 
b/component-test/src/main/resources/logback-test.xml
index 2473b99..735b8e5 100644
--- a/component-test/src/main/resources/logback-test.xml
+++ b/component-test/src/main/resources/logback-test.xml
@@ -16,44 +16,17 @@
 
 -->
 <configuration>
-    <appender name="FILE" 
class="ch.qos.logback.core.rolling.RollingFileAppender">
-        <file>logs/reporting.log</file>
-        <rollingPolicy 
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            
<fileNamePattern>logs/archive/reporting.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <maxHistory>7</maxHistory>
-            <totalSizeCap>2GB</totalSizeCap>
-        </rollingPolicy>
-        <encoder>
-            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - 
%msg%n</pattern>
-        </encoder>
-    </appender>
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
             <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - 
%msg%n</pattern>
         </encoder>
     </appender>
 
-    <logger name="com" level="INFO">
-        <appender-ref ref="STDOUT" />
-    </logger>
-
-    <logger name="ch" level="INFO">
-        <appender-ref ref="STDOUT" />
-    </logger>
-
-    <logger name="org" level="INFO">
-        <appender-ref ref="STDOUT" />
-    </logger>
-
-    <logger name="io" level="DEBUG">
-        <appender-ref ref="STDOUT" />
-    </logger>
-
-    <logger name="net" level="INFO">
-        <appender-ref ref="STDOUT" />
-    </logger>
+    <logger name="org" level="ERROR"/>
+    <logger name="com" level="OFF"/>
+    <logger name="ch" level="OFF"/>
 
-    <root level="WARN">
-        <appender-ref ref="FILE"/>
+    <root level="DEBUG">
+        <appender-ref ref="STDOUT"/>
     </root>
 </configuration>
\ No newline at end of file
diff --git 
a/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java 
b/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
index 58dd263..f0ab979 100644
--- 
a/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
+++ 
b/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
@@ -42,9 +42,8 @@ import 
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
 @EnableServiceException
 @ComponentScan({
     "io.mifos.reporting.service.rest",
-    "io.mifos.reporting.service.internal.service",
-    "io.mifos.reporting.service.repository",
-    "io.mifos.reporting.service.internal.provider"
+    "io.mifos.reporting.service.internal",
+    "io.mifos.reporting.service.repository"
 })
 @EnableJpaRepositories({
     "io.mifos.reporting.service.repository"
diff --git 
a/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
 
b/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
new file mode 100644
index 0000000..7ef4871
--- /dev/null
+++ 
b/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.reporting.service.internal.provider;
+
+import io.mifos.reporting.api.v1.domain.ReportDefinition;
+import io.mifos.reporting.service.ServiceConstants;
+import io.mifos.reporting.service.spi.Report;
+import io.mifos.reporting.service.spi.ReportSpecification;
+import org.slf4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+public class ReportSpecificationProvider implements ApplicationContextAware {
+
+  private final Logger logger;
+  private final HashMap<String, ReportSpecification> reportSpecificationCache 
= new HashMap<>();
+  private final HashMap<String, List<ReportDefinition>> reportCategoryCache = 
new HashMap<>();
+
+  private ApplicationContext applicationContext;
+
+  @Autowired
+  public ReportSpecificationProvider(@Qualifier(ServiceConstants.LOGGER_NAME) 
final Logger logger) {
+    super();
+    this.logger = logger;
+  }
+
+  @Override
+  public void setApplicationContext(final ApplicationContext 
applicationContext) throws BeansException {
+    this.applicationContext = applicationContext;
+    this.initialize();
+  }
+
+  public List<String> getAvailableCategories() {
+    return new ArrayList<>(this.reportCategoryCache.keySet());
+  }
+
+  public List<ReportDefinition> getAvailableReports(final String category) {
+    this.logger.debug("Looking up report definitions for category {}.", 
category);
+    return this.reportCategoryCache.getOrDefault(category, 
Collections.emptyList());
+  }
+
+  public Optional<ReportSpecification> getReportSpecification(final String 
category, final String identifier) {
+    final String keyForReportSpecificationCache = 
this.buildKeyForSpecificationCache(category, identifier);
+    this.logger.debug("Looking up report specification for {}.", 
keyForReportSpecificationCache);
+    return 
Optional.ofNullable(this.reportSpecificationCache.get(keyForReportSpecificationCache));
+  }
+
+  private void initialize() {
+    final Map<String, Object> beansWithAnnotation = 
this.applicationContext.getBeansWithAnnotation(Report.class);
+
+    beansWithAnnotation.values().forEach(bean -> {
+      final ReportSpecification reportSpecification = 
ReportSpecification.class.cast(bean);
+      final Report report = 
reportSpecification.getClass().getAnnotation(Report.class);
+      final String keyForReportSpecificationCache =
+          this.buildKeyForSpecificationCache(report.category(), 
report.identifier());
+      this.logger.debug("Adding report specification for {}", 
keyForReportSpecificationCache);
+
+      this.reportCategoryCache.computeIfAbsent(report.category(), (key) -> new 
ArrayList<>());
+      
this.reportCategoryCache.get(report.category()).add(reportSpecification.getReportDefinition());
+      this.reportSpecificationCache.put(keyForReportSpecificationCache, 
reportSpecification);
+    });
+  }
+
+  private String buildKeyForSpecificationCache(final String category, final 
String identifier) {
+    return category + "~" + identifier;
+  }
+}
diff --git 
a/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
 
b/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
new file mode 100644
index 0000000..b166b53
--- /dev/null
+++ 
b/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.reporting.service.internal.specification;
+
+import io.mifos.reporting.api.v1.domain.DisplayableField;
+import io.mifos.reporting.api.v1.domain.QueryParameter;
+import io.mifos.reporting.api.v1.domain.ReportDefinition;
+import io.mifos.reporting.api.v1.domain.ReportPage;
+import io.mifos.reporting.api.v1.domain.ReportRequest;
+import io.mifos.reporting.api.v1.domain.Type;
+import io.mifos.reporting.service.ServiceConstants;
+import io.mifos.reporting.service.spi.DisplayableFieldBuilder;
+import io.mifos.reporting.service.spi.QueryParameterBuilder;
+import io.mifos.reporting.service.spi.Report;
+import io.mifos.reporting.service.spi.ReportSpecification;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Report(category = "Customer", identifier = "Listing")
+public class CustomerListReportSpecification implements ReportSpecification {
+
+  private final Logger logger;
+  private final HashMap<String, String> columnMapping = new HashMap<>();
+
+  @Autowired
+  public 
CustomerListReportSpecification(@Qualifier(ServiceConstants.LOGGER_NAME) final 
Logger logger) {
+    super();
+    this.logger = logger;
+    this.initializeMapping();
+  }
+
+  @Override
+  public ReportDefinition getReportDefinition() {
+    final ReportDefinition reportDefinition = new ReportDefinition();
+    reportDefinition.setIdentifier("Listing");
+    reportDefinition.setName("Customer Listing");
+    reportDefinition.setDescription("List of all customers.");
+    reportDefinition.setQueryParameters(this.buildQueryParameters());
+    reportDefinition.setDisplayableFields(this.buildDisplayableFields());
+    return reportDefinition;
+  }
+
+  @Override
+  public ReportPage generateReport(final ReportRequest reportRequest) {
+    return null;
+  }
+
+  @Override
+  public void validate(final ReportRequest reportRequest) throws 
IllegalArgumentException {
+    final ArrayList<String> unknownFields =  new ArrayList<>();
+    reportRequest.getQueryParameters().forEach(queryParameter -> {
+      if (!this.columnMapping.keySet().contains(queryParameter.getName())) {
+        unknownFields.add(queryParameter.getName());
+      }
+    });
+
+    reportRequest.getDisplayableFields().forEach(displayableField -> {
+      if (!this.columnMapping.keySet().contains(displayableField.getName())) {
+        unknownFields.add(displayableField.getName());
+      }
+    });
+
+    if (!unknownFields.isEmpty()) {
+      throw new IllegalArgumentException(
+          "Unspecified fields requested: " + 
unknownFields.stream().collect(Collectors.joining(", "))
+      );
+    }
+  }
+
+  private void initializeMapping() {
+    this.columnMapping.put("Date Range", "createdBy");
+    this.columnMapping.put("State", "currentState");
+    this.columnMapping.put("Customer", "identifier");
+    this.columnMapping.put("Account number", "identifier");
+    this.columnMapping.put("First name", "givenName");
+    this.columnMapping.put("Middle Name", "middleName");
+    this.columnMapping.put("Last Name", "surname");
+    this.columnMapping.put("Balance", "balance");
+    this.columnMapping.put("Address", "address");
+  }
+
+  private List<QueryParameter> buildQueryParameters() {
+    return Arrays.asList(
+        QueryParameterBuilder.create("Date range", 
Type.DATE).operator(QueryParameter.Operator.BETWEEN).build(),
+        QueryParameterBuilder.create("State", 
Type.TEXT).operator(QueryParameter.Operator.IN).build()
+    );
+  }
+
+  private List<DisplayableField> buildDisplayableFields() {
+    return Arrays.asList(
+        DisplayableFieldBuilder.create("Customer", 
Type.TEXT).mandatory().build(),
+        DisplayableFieldBuilder.create("First name", Type.TEXT).build(),
+        DisplayableFieldBuilder.create("Middle name", Type.TEXT).build(),
+        DisplayableFieldBuilder.create("Last name", Type.TEXT).build(),
+        DisplayableFieldBuilder.create("Account number", 
Type.TEXT).mandatory().build(),
+        DisplayableFieldBuilder.create("Balance", Type.NUMBER).build(),
+        DisplayableFieldBuilder.create("Address", Type.TEXT).build()
+    );
+  }
+}
diff --git 
a/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
 
b/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
index d639ae7..16307c5 100644
--- 
a/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
+++ 
b/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
@@ -17,15 +17,28 @@ package io.mifos.reporting.service.rest;
 
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
-import io.mifos.core.command.gateway.CommandGateway;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.reporting.api.v1.domain.ReportDefinition;
+import io.mifos.reporting.api.v1.domain.ReportPage;
+import io.mifos.reporting.api.v1.domain.ReportRequest;
 import io.mifos.reporting.service.ServiceConstants;
-import io.mifos.reporting.service.internal.service.ReportingService;
+import 
io.mifos.reporting.service.internal.provider.ReportSpecificationProvider;
+import io.mifos.reporting.service.spi.ReportSpecification;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Optional;
 
 @SuppressWarnings("unused")
 @RestController
@@ -33,16 +46,77 @@ import org.springframework.web.bind.annotation.*;
 public class ReportingRestController {
 
   private final Logger logger;
-  private final CommandGateway commandGateway;
-  private final ReportingService reportingService;
+  private final ReportSpecificationProvider reportSpecificationProvider;
 
   @Autowired
   public ReportingRestController(@Qualifier(ServiceConstants.LOGGER_NAME) 
final Logger logger,
-                                 final CommandGateway commandGateway,
-                                 final ReportingService reportingService) {
+                                 final ReportSpecificationProvider 
reportSpecificationProvider) {
     super();
     this.logger = logger;
-    this.commandGateway = commandGateway;
-    this.reportingService = reportingService;
+    this.reportSpecificationProvider = reportSpecificationProvider;
+  }
+
+  @Permittable(value = AcceptedTokenType.SYSTEM)
+  @RequestMapping(
+      value = "/initialize",
+      method = RequestMethod.POST,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  public
+  @ResponseBody
+  ResponseEntity<Void> initialize() {
+    return ResponseEntity.accepted().build();
+  }
+
+  @RequestMapping(
+      value = "/categories",
+      method = RequestMethod.GET,
+      produces = MediaType.APPLICATION_JSON_VALUE,
+      consumes = MediaType.ALL_VALUE
+  )
+  public
+  ResponseEntity<List<String>> fetchCategories() {
+    return 
ResponseEntity.ok(this.reportSpecificationProvider.getAvailableCategories());
+  }
+
+  @RequestMapping(
+      value = "categories/{category}",
+      method = RequestMethod.GET,
+      produces = MediaType.APPLICATION_JSON_VALUE,
+      consumes = MediaType.ALL_VALUE)
+  public
+  ResponseEntity<List<ReportDefinition>> 
fetchReportDefinitions(@PathVariable("category") final String category) {
+    return 
ResponseEntity.ok(this.reportSpecificationProvider.getAvailableReports(category));
+  }
+
+  @RequestMapping(
+      value = "/categories/{category}/reports/{identifier}",
+      method = RequestMethod.POST,
+      produces = MediaType.APPLICATION_JSON_VALUE,
+      consumes = MediaType.APPLICATION_JSON_VALUE
+  )
+  public
+  ResponseEntity<ReportPage> generateReport(@PathVariable("category") final 
String category,
+                                           @PathVariable("identifier") final 
String identifier,
+                                           @RequestBody final ReportRequest 
reportRequest,
+                                           @RequestParam(value = "pageIndex", 
required = false) final Integer pageIndex,
+                                           @RequestParam(value = "size", 
required = false) final Integer size) {
+
+    final Optional<ReportSpecification> optionalReportSpecification =
+        this.reportSpecificationProvider.getReportSpecification(category, 
identifier);
+    if (optionalReportSpecification.isPresent()) {
+      final ReportSpecification reportSpecification = 
optionalReportSpecification.get();
+
+      try {
+        reportSpecification.validate(reportRequest);
+      } catch (final IllegalArgumentException iaex) {
+        throw ServiceException.badRequest(iaex.getMessage());
+      }
+
+      return 
ResponseEntity.ok(reportSpecification.generateReport(reportRequest));
+    } else {
+      throw ServiceException.notFound("Report {0} not found.", identifier);
+    }
   }
 }
diff --git 
a/service/src/main/java/io/mifos/reporting/service/spi/DisplayableFieldBuilder.java
 
b/service/src/main/java/io/mifos/reporting/service/spi/DisplayableFieldBuilder.java
new file mode 100644
index 0000000..987ad9b
--- /dev/null
+++ 
b/service/src/main/java/io/mifos/reporting/service/spi/DisplayableFieldBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.reporting.service.spi;
+
+import io.mifos.reporting.api.v1.domain.DisplayableField;
+import io.mifos.reporting.api.v1.domain.Type;
+
+public class DisplayableFieldBuilder {
+
+  private String name;
+  private Type type;
+  private Boolean mandatory;
+
+  private DisplayableFieldBuilder(final String name, final Type type) {
+    super();
+    this.name = name;
+    this.type = type;
+  }
+
+  public static DisplayableFieldBuilder create(final String name, final Type 
type) {
+    return new DisplayableFieldBuilder(name, type);
+  }
+
+  public DisplayableFieldBuilder mandatory() {
+    this.mandatory = Boolean.TRUE;
+    return this;
+  }
+
+  public DisplayableField build() {
+    final DisplayableField displayableField = new DisplayableField();
+    displayableField.setName(this.name);
+    displayableField.setType(this.type);
+    displayableField.setMandatory(this.mandatory != null ? this.mandatory : 
Boolean.FALSE);
+    return displayableField;
+  }
+}
diff --git 
a/service/src/main/java/io/mifos/reporting/service/spi/QueryParameterBuilder.java
 
b/service/src/main/java/io/mifos/reporting/service/spi/QueryParameterBuilder.java
new file mode 100644
index 0000000..00251f3
--- /dev/null
+++ 
b/service/src/main/java/io/mifos/reporting/service/spi/QueryParameterBuilder.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.reporting.service.spi;
+
+import io.mifos.reporting.api.v1.domain.AutoCompleteResource;
+import io.mifos.reporting.api.v1.domain.QueryParameter;
+import io.mifos.reporting.api.v1.domain.Type;
+
+import java.util.Arrays;
+
+public class QueryParameterBuilder {
+
+  private String name;
+  private Type type;
+  private QueryParameter.Operator operator;
+  private Boolean mandatory;
+  private AutoCompleteResource autoCompleteResource;
+
+  private QueryParameter queryParameter;
+
+  private QueryParameterBuilder(final String name, final Type type) {
+    super();
+    this.name = name;
+    this.type = type;
+  }
+
+  public static QueryParameterBuilder create(final String name, final Type 
type) {
+    return new QueryParameterBuilder(name, type);
+  }
+
+  public QueryParameterBuilder operator(final QueryParameter.Operator 
operator) {
+    this.operator = operator;
+    return this;
+  }
+
+  public QueryParameterBuilder mandatory() {
+    this.mandatory = Boolean.TRUE;
+    return this;
+  }
+
+  public QueryParameterBuilder autoComplete(final String path, final String... 
terms) {
+    final AutoCompleteResource autoCompleteResource = new 
AutoCompleteResource();
+    autoCompleteResource.setPath(path);
+    autoCompleteResource.setTerms(Arrays.asList(terms));
+    this.autoCompleteResource = autoCompleteResource;
+    return this;
+  }
+
+  public QueryParameter build() {
+    final QueryParameter queryParameter = new QueryParameter();
+    queryParameter.setName(this.name);
+    queryParameter.setType(this.type);
+    queryParameter.setOperator(this.operator != null ? this.operator : 
QueryParameter.Operator.EQUALS);
+    queryParameter.setMandatory(this.mandatory != null ? this.mandatory : 
Boolean.FALSE);
+    queryParameter.setAutoCompleteResource(this.autoCompleteResource);
+    return queryParameter;
+  }
+}
diff --git 
a/service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
 b/service/src/main/java/io/mifos/reporting/service/spi/Report.java
similarity index 53%
copy from 
service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
copy to service/src/main/java/io/mifos/reporting/service/spi/Report.java
index 620dff3..ec40ec8 100644
--- 
a/service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
+++ b/service/src/main/java/io/mifos/reporting/service/spi/Report.java
@@ -13,22 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.reporting.service.internal.service;
+package io.mifos.reporting.service.spi;
 
-import io.mifos.reporting.service.ServiceConstants;
-import org.slf4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Service;
+import org.springframework.stereotype.Component;
 
-@Service
-public class ReportingService {
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-  private final Logger logger;
-
-  @Autowired
-  public ReportingService(@Qualifier(ServiceConstants.LOGGER_NAME) final 
Logger logger) {
-    super();
-    this.logger = logger;
-  }
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Component
+public @interface Report {
+  String category();
+  String identifier();
 }
diff --git 
a/service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
 b/service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
similarity index 53%
rename from 
service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
rename to 
service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
index 620dff3..3502b94 100644
--- 
a/service/src/main/java/io/mifos/reporting/service/internal/service/ReportingService.java
+++ 
b/service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
@@ -13,22 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.reporting.service.internal.service;
+package io.mifos.reporting.service.spi;
 
-import io.mifos.reporting.service.ServiceConstants;
-import org.slf4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Service;
+import io.mifos.reporting.api.v1.domain.ReportDefinition;
+import io.mifos.reporting.api.v1.domain.ReportPage;
+import io.mifos.reporting.api.v1.domain.ReportRequest;
 
-@Service
-public class ReportingService {
+public interface ReportSpecification {
 
-  private final Logger logger;
+  ReportDefinition getReportDefinition();
 
-  @Autowired
-  public ReportingService(@Qualifier(ServiceConstants.LOGGER_NAME) final 
Logger logger) {
-    super();
-    this.logger = logger;
-  }
+  ReportPage generateReport(final ReportRequest reportRequest);
+
+  void validate(final ReportRequest reportRequest) throws 
IllegalArgumentException;
 }

Reply via email to