From 2e259b15833c08ba83c0b418ec4fc5ca8e04d254 Mon Sep 17 00:00:00 2001
From: Kasem
Date: Sat, 29 Jul 2017 18:05:46 +0600
Subject: [PATCH 01/73] Adhoc Query
---
.../adhocquery/api/AdHocApiResource.java | 170 +++++++++++++++
.../adhocquery/api/AdHocJsonInputParams.java | 55 +++++
.../fineract/adhocquery/data/AdHocData.java | 122 +++++++++++
.../fineract/adhocquery/domain/AdHoc.java | 153 ++++++++++++++
.../adhocquery/domain/AdHocRepository.java | 26 +++
.../exception/AdHocNotFoundException.java | 32 +++
.../handler/CreateAdHocCommandHandler.java | 47 +++++
.../handler/DeleteAdHocCommandHandler.java | 47 +++++
.../handler/UpdateAdHocCommandHandler.java | 48 +++++
.../service/AdHocDataValidator.java | 127 ++++++++++++
.../service/AdHocReadPlatformService.java | 34 +++
.../service/AdHocReadPlatformServiceImpl.java | 109 ++++++++++
.../AdHocScheduledJobRunnerService.java | 23 +++
.../AdHocScheduledJobRunnerServiceImpl.java | 77 +++++++
.../service/AdHocWritePlatformService.java | 35 ++++
...WritePlatformServiceJpaRepositoryImpl.java | 193 ++++++++++++++++++
.../service/CommandWrapperBuilder.java | 39 ++++
.../infrastructure/jobs/service/JobName.java | 4 +-
.../security/utils/SQLInjectionValidator.java | 77 +++++++
.../resources/META-INF/spring/appContext.xml | 4 +-
.../migrations/core_db/V333__adhocquery.sql | 49 +++++
21 files changed, 1468 insertions(+), 3 deletions(-)
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocApiResource.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHocRepository.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/exception/AdHocNotFoundException.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/CreateAdHocCommandHandler.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/DeleteAdHocCommandHandler.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/UpdateAdHocCommandHandler.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformService.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerService.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformService.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformServiceJpaRepositoryImpl.java
create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V333__adhocquery.sql
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocApiResource.java
new file mode 100644
index 00000000000..75380f14d8b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocApiResource.java
@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.api;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.adhocquery.data.AdHocData;
+import org.apache.fineract.adhocquery.service.AdHocReadPlatformService;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/adhocquery")
+@Component
+@Scope("singleton")
+public class AdHocApiResource {
+
+ /**
+ * The set of parameters that are supported in response for {@link AdhocData}
+ */
+ private final Set RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "query", "tableName","tableField","isActive","createdBy","createdOn","createdById","updatedById","updatedOn","email"));
+
+ private final PlatformSecurityContext context;
+ private final AdHocReadPlatformService adHocReadPlatformService;
+ private final DefaultToApiJsonSerializer toApiJsonSerializer;
+ private final ApiRequestParameterHelper apiRequestParameterHelper;
+ private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
+
+ @Autowired
+ public AdHocApiResource(final PlatformSecurityContext context, final AdHocReadPlatformService readPlatformService,
+ final DefaultToApiJsonSerializer toApiJsonSerializer,
+ final ApiRequestParameterHelper apiRequestParameterHelper,
+ final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
+ this.context = context;
+ this.adHocReadPlatformService = readPlatformService;
+ this.toApiJsonSerializer = toApiJsonSerializer;
+ this.apiRequestParameterHelper = apiRequestParameterHelper;
+ this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
+ }
+
+ @GET
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String retrieveAll(@Context final UriInfo uriInfo) {
+
+ this.context.authenticatedUser();
+ final Collection adhocs = this.adHocReadPlatformService.retrieveAllAdHocQuery();
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, adhocs, this.RESPONSE_DATA_PARAMETERS);
+ }
+ @GET
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Path("template")
+ public String template(@Context final UriInfo uriInfo) {
+ this.context.authenticatedUser();
+ final AdHocData user = this.adHocReadPlatformService.retrieveNewAdHocDetails();
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, user, this.RESPONSE_DATA_PARAMETERS);
+ }
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String createAdHocQuery(final String apiRequestBodyAsJson) {
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .createAdHoc() //
+ .withJson(apiRequestBodyAsJson) //
+ .build();
+
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
+ @GET
+ @Path("{adHocId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String retrieveAdHocQuery(@PathParam("adHocId") final Long adHocId, @Context final UriInfo uriInfo) {
+
+ this.context.authenticatedUser();
+
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+
+ final AdHocData adhoc = this.adHocReadPlatformService.retrieveOne(adHocId);
+
+ return this.toApiJsonSerializer.serialize(settings, adhoc, this.RESPONSE_DATA_PARAMETERS);
+ }
+ @PUT
+ @Path("{adHocId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String update(@PathParam("adHocId") final Long adHocId, final String apiRequestBodyAsJson) {
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateAdHoc(adHocId) //
+ .withJson(apiRequestBodyAsJson) //
+ .build();
+
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+ /**
+ * Delete AdHocQuery
+ *
+ * @param adHocId
+ * @return
+ */
+ @DELETE
+ @Path("{adHocId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String deleteAdHocQuery(@PathParam("adHocId") final Long adHocId) {
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteAdHoc(adHocId) //
+ .build();
+
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
+ private boolean is(final String commandParam, final String commandValue) {
+ return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
+ }
+
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java
new file mode 100644
index 00000000000..0cb33844ec1
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.api;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/***
+ * Enum of all parameters passed in while creating/updating a AdHocQuery
+ ***/
+public enum AdHocJsonInputParams {
+ ID("id"), NAME("name"),QUERY("query"),TABLENAME("tableName"),TABLEFIELD("tableFields"), ISACTIVE("isActive"),EMAIL("email");
+
+ private final String value;
+
+ private AdHocJsonInputParams(final String value) {
+ this.value = value;
+ }
+
+ private static final Set values = new HashSet<>();
+ static {
+ for (final AdHocJsonInputParams type : AdHocJsonInputParams.values()) {
+ values.add(type.value);
+ }
+ }
+
+ public static Set getAllValues() {
+ return values;
+ }
+
+ @Override
+ public String toString() {
+ return name().toString().replaceAll("_", " ");
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java
new file mode 100644
index 00000000000..f0fd7a85832
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.data;
+
+import java.util.Collection;
+
+import org.apache.fineract.organisation.office.data.OfficeData;
+import org.apache.fineract.useradministration.data.AppUserData;
+import org.apache.fineract.useradministration.data.RoleData;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+/**
+ * Immutable data object represent note or case information AdHocData
+ *
+ */
+public class AdHocData {
+
+
+
+ @SuppressWarnings("unused")
+ private final Long id;
+ @SuppressWarnings("unused")
+ private final String name;
+ @SuppressWarnings("unused")
+ private final String query;
+ @SuppressWarnings("unused")
+ private final String tableName;
+ @SuppressWarnings("unused")
+ private final String tableFields;
+ @SuppressWarnings("unused")
+ private final String email;
+ @SuppressWarnings("unused")
+ private final boolean isActive;
+ @SuppressWarnings("unused")
+ private final DateTime createdOn;
+ @SuppressWarnings("unused")
+ private final Long createdById;
+ @SuppressWarnings("unused")
+ private final Long updatedById;
+ @SuppressWarnings("unused")
+ private final DateTime updatedOn;
+ @SuppressWarnings("unused")
+ private final String createdBy;
+
+
+
+
+ public AdHocData(final Long id, final String name,final String query, final String tableName,final String tableFields,
+ final boolean isActive, final DateTime createdOn, final Long createdById,final Long updatedById,
+ final DateTime updatedOn,final String createdBy,final String email
+ ) {
+ this.id = id;
+ this.name=name;
+ this.query=query;
+ this.tableName = tableName;
+ this.tableFields = tableFields;
+ this.isActive = isActive;
+ this.createdOn = createdOn;
+ this.createdById = createdById;
+ this.updatedById=updatedById;
+ this.updatedOn=updatedOn;
+ this.createdBy=createdBy;
+ this.email=email;
+ }
+ public static AdHocData template() {
+ AdHocData adHocData = new AdHocData(null,null,null,null,null,false,null,null,null,null,null,null);
+ return adHocData;
+ }
+ public Long getId() {
+ return this.id;
+ }
+ public String getName() {
+ return this.name;
+ }
+ public String getQuery() {
+ return this.query;
+ }
+ public String getTableName() {
+ return this.tableName;
+ }
+ public String getTableFields() {
+ return this.tableFields;
+ }
+ public String getEmail() {
+ return this.email;
+ }
+ public boolean isActive() {
+ return this.isActive;
+ }
+ public DateTime getCreatedOn() {
+ return this.createdOn;
+ }
+ public Long getCreatedById() {
+ return this.createdById;
+ }
+ public Long getUpdatedById() {
+ return this.updatedById;
+ }
+ public DateTime getUpdatedOn() {
+ return this.updatedOn;
+ }
+ public String getCreatedBy() {
+ return this.createdBy;
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java
new file mode 100644
index 00000000000..be315ba9972
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.domain;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.adhocquery.api.AdHocJsonInputParams;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
+import org.apache.fineract.useradministration.domain.AppUser;
+
+
+@Entity
+@Table(name = "m_adhoc")
+public class AdHoc extends AbstractAuditableCustom {
+
+ @Column(name = "name", length = 100)
+ private String name;
+
+ @Column(name = "query", length = 2000)
+ private String query;
+
+ @Column(name = "table_name", length = 100)
+ private String tableName;
+
+
+ @Column(name = "table_fields", length = 2000)
+ private String tableFields;
+
+ @Column(name = "email", length = 500)
+ private String email;
+
+
+ @Column(name = "IsActive", nullable = false)
+ private boolean isActive = false;
+
+ private AdHoc(final String name, final String query,final String tableName,final String tableFields ,final String email,final boolean isActive) {
+ this.name = StringUtils.defaultIfEmpty(name, null);
+ this.query=StringUtils.defaultIfEmpty(query,null);
+ this.tableName=StringUtils.defaultIfEmpty(tableName,null);
+ this.tableFields=StringUtils.defaultIfEmpty(tableFields,null);
+ this.email=StringUtils.defaultIfEmpty(email,null);
+ this.isActive = BooleanUtils.toBooleanDefaultIfNull(isActive, false);
+
+ }
+ public static AdHoc fromJson(final JsonCommand command) {
+ final String name = command.stringValueOfParameterNamed(AdHocJsonInputParams.NAME.getValue());
+
+ String commandQuery=command.stringValueOfParameterNamed(AdHocJsonInputParams.QUERY.getValue());
+
+ SQLInjectionValidator.validateAdhocQuery(commandQuery);
+ final String query = commandQuery;
+ final String tableName = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLENAME.getValue());
+ final String tableFields = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLEFIELD.getValue());
+ final String email = command.stringValueOfParameterNamed(AdHocJsonInputParams.EMAIL.getValue());
+ final boolean isActive = command.booleanPrimitiveValueOfParameterNamed(AdHocJsonInputParams.ISACTIVE.getValue());
+ return new AdHoc(name,query,tableName,tableFields ,email,isActive);
+ }
+
+ public Map update(final JsonCommand command) {
+
+ final Map actualChanges = new LinkedHashMap<>(7);
+
+ final String nameParamName = "name";
+ if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
+ final String newValue = command.stringValueOfParameterNamed(nameParamName);
+ actualChanges.put(nameParamName, newValue);
+ this.name = newValue;
+ }
+
+ final String descriptionParamName = "query";
+ if (command.isChangeInStringParameterNamed(descriptionParamName, this.query)) {
+ final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
+ actualChanges.put(descriptionParamName, newValue);
+ this.query = newValue;
+ }
+ final String tableName = "tableName";
+ if (command.isChangeInStringParameterNamed(tableName, this.tableName)) {
+ final String newValue = command.stringValueOfParameterNamed(tableName);
+ actualChanges.put(tableName, newValue);
+ this.tableName = newValue;
+ }
+ final String tableField = "tableField";
+ if (command.isChangeInStringParameterNamed(tableField, this.tableFields)) {
+ final String newValue = command.stringValueOfParameterNamed(tableField);
+ actualChanges.put(tableField, newValue);
+ this.tableFields = newValue;
+ }
+ final String email = "email";
+ if (command.isChangeInStringParameterNamed(email, this.email)) {
+ final String newValue = command.stringValueOfParameterNamed(email);
+ actualChanges.put(email, newValue);
+ this.email = newValue;
+ }
+ final String paramisActive = "isActive";
+ if (command.isChangeInBooleanParameterNamed(paramisActive, this.isActive)) {
+ final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramisActive);
+ actualChanges.put(paramisActive, newValue);
+ this.isActive = newValue;
+ }
+ return actualChanges;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public String getQuery() {
+ return query;
+ }
+ public String getTableName() {
+ return tableName;
+ }
+ public String getTableFields() {
+ return tableFields;
+ }
+ public boolean isActive() {
+ return this.isActive;
+ }
+ public String getEmail() {
+ return email;
+ }
+ public void disableActive() {
+ this.isActive = true;
+ }
+ public void enableActive() {
+ this.isActive = false;
+ }
+
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHocRepository.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHocRepository.java
new file mode 100644
index 00000000000..fc31eb7e3ac
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHocRepository.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface AdHocRepository extends JpaRepository, JpaSpecificationExecutor {
+ // no added behaviour
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/exception/AdHocNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/exception/AdHocNotFoundException.java
new file mode 100644
index 00000000000..7de67f5c466
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/exception/AdHocNotFoundException.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+
+/**
+ * A {@link RuntimeException} thrown when AdHoc resources are not
+ * found.
+ */
+public class AdHocNotFoundException extends AbstractPlatformResourceNotFoundException {
+
+ public AdHocNotFoundException(final Long id) {
+ super("error.msg.adhocquery.adhoc.id.invalid", "Adhoc Record with identifier " + id + " does not exist", id);
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/CreateAdHocCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/CreateAdHocCommandHandler.java
new file mode 100644
index 00000000000..73f09dccc7f
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/CreateAdHocCommandHandler.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.handler;
+
+import org.apache.fineract.adhocquery.service.AdHocWritePlatformService;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "ADHOC", action = "CREATE")
+public class CreateAdHocCommandHandler implements NewCommandSourceHandler {
+
+ private final AdHocWritePlatformService writePlatformService;
+
+ @Autowired
+ public CreateAdHocCommandHandler(final AdHocWritePlatformService writePlatformService) {
+ this.writePlatformService = writePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+
+ return this.writePlatformService.createAdHocQuery(command);
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/DeleteAdHocCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/DeleteAdHocCommandHandler.java
new file mode 100644
index 00000000000..eb8e9f69669
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/DeleteAdHocCommandHandler.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.adhocquery.service.AdHocWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "ADHOC", action = "DELETE")
+public class DeleteAdHocCommandHandler implements NewCommandSourceHandler {
+
+ private final AdHocWritePlatformService writePlatformService;
+
+ @Autowired
+ public DeleteAdHocCommandHandler(final AdHocWritePlatformService writePlatformService) {
+ this.writePlatformService = writePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+
+ return this.writePlatformService.deleteAdHocQuery(command.entityId());
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/UpdateAdHocCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/UpdateAdHocCommandHandler.java
new file mode 100644
index 00000000000..db9d4ac92f5
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/handler/UpdateAdHocCommandHandler.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.adhocquery.service.AdHocWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "ADHOC", action = "UPDATE")
+public class UpdateAdHocCommandHandler implements NewCommandSourceHandler {
+
+ private final AdHocWritePlatformService writePlatformService;
+
+ @Autowired
+ public UpdateAdHocCommandHandler(final AdHocWritePlatformService writePlatformService) {
+ this.writePlatformService = writePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+
+ final Long adHocId = command.entityId();
+ return this.writePlatformService.updateAdHocQuery(adHocId, command);
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java
new file mode 100644
index 00000000000..2cdd294a954
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java
@@ -0,0 +1,127 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.fineract.adhocquery.service;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+@Component
+public final class AdHocDataValidator {
+
+ /**
+ * The parameters supported for this command.
+ */
+ private final Set supportedParameters = new HashSet<>(Arrays.asList("name","query","tableName","tableFields","email","isActive"));
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ @Autowired
+ public AdHocDataValidator(final FromJsonHelper fromApiJsonHelper) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ public void validateForCreate(final String json) {
+ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+ final Type typeOfMap = new TypeToken