Adhoc Query
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 0000000..75380f1
--- /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<String> 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<AdHocData> toApiJsonSerializer;
+ private final ApiRequestParameterHelper apiRequestParameterHelper;
+ private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
+
+ @Autowired
+ public AdHocApiResource(final PlatformSecurityContext context, final AdHocReadPlatformService readPlatformService,
+ final DefaultToApiJsonSerializer<AdHocData> 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<AdHocData> 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 0000000..0cb3384
--- /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<String> values = new HashSet<>();
+ static {
+ for (final AdHocJsonInputParams type : AdHocJsonInputParams.values()) {
+ values.add(type.value);
+ }
+ }
+
+ public static Set<String> 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 0000000..f0fd7a8
--- /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 0000000..be315ba
--- /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<AppUser, Long> {
+
+ @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<String, Object> update(final JsonCommand command) {
+
+ final Map<String, Object> 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 0000000..fc31eb7
--- /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<AdHoc, Long>, JpaSpecificationExecutor<AdHoc> {
+ // 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 0000000..7de67f5
--- /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 0000000..73f09dc
--- /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 0000000..eb8e9f6
--- /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 0000000..db9d4ac
--- /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 0000000..2cdd294
--- /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<String> 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<Map<String, Object>>() {}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("adhoc");
+
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+ final String name = this.fromApiJsonHelper.extractStringNamed("name", element);
+ baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
+
+ final String description = this.fromApiJsonHelper.extractStringNamed("query", element);
+ baseDataValidator.reset().parameter("query").value(description).notBlank().notExceedingLengthOf(2000);
+
+ final String tableName = this.fromApiJsonHelper.extractStringNamed("tableName", element);
+ baseDataValidator.reset().parameter("tableName").value(tableName).notBlank().notExceedingLengthOf(100);
+
+ final String tableFields = this.fromApiJsonHelper.extractStringNamed("tableFields", element);
+ baseDataValidator.reset().parameter("tableFields").value(tableFields).notBlank().notExceedingLengthOf(1000);
+
+ final String email = this.fromApiJsonHelper.extractStringNamed("email", element);
+ baseDataValidator.reset().parameter("email").value(email).notBlank().notExceedingLengthOf(500);
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ public void validateForUpdate(final String json) {
+ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("adhoc");
+
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+ if (this.fromApiJsonHelper.parameterExists("name", element)) {
+ final String name = this.fromApiJsonHelper.extractStringNamed("name", element);
+ baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
+ }
+
+ if (this.fromApiJsonHelper.parameterExists("query", element)) {
+ final String query = this.fromApiJsonHelper.extractStringNamed("query", element);
+ baseDataValidator.reset().parameter("query").value(query).notBlank().notExceedingLengthOf(2000);
+ }
+ if (this.fromApiJsonHelper.parameterExists("tableName", element)) {
+ final String tableName = this.fromApiJsonHelper.extractStringNamed("tableName", element);
+ baseDataValidator.reset().parameter("tableName").value(tableName).notBlank().notExceedingLengthOf(100);
+ }
+ if (this.fromApiJsonHelper.parameterExists("tableFields", element)) {
+ final String tableField = this.fromApiJsonHelper.extractStringNamed("tableFields", element);
+ baseDataValidator.reset().parameter("tableFields").value(tableField).notBlank().notExceedingLengthOf(2000);
+ }
+ if (this.fromApiJsonHelper.parameterExists("email", element)) {
+ final String email = this.fromApiJsonHelper.extractStringNamed("email", element);
+ baseDataValidator.reset().parameter("email").value(email).notBlank().notExceedingLengthOf(500);
+ }
+ /*if (this.fromApiJsonHelper.parameterExists("isActive", element)) {
+ final Integer isActive = this.fromApiJsonHelper.extractIntegerNamed("isActive", element, Locale.getDefault());
+ baseDataValidator.reset().parameter("isActive").value(isActive).notNull().inMinMaxRange(1, 2);
+ }*/
+
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformService.java
new file mode 100644
index 0000000..414a2b2
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformService.java
@@ -0,0 +1,34 @@
+/**
+ * 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.util.Collection;
+
+import org.apache.fineract.adhocquery.data.AdHocData;
+
+public interface AdHocReadPlatformService {
+
+ Collection<AdHocData> retrieveAllAdHocQuery();
+
+ Collection<AdHocData> retrieveAllActiveAdHocQuery();
+
+ AdHocData retrieveOne(Long adHocId);
+ AdHocData retrieveNewAdHocDetails();
+
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java
new file mode 100644
index 0000000..eb1f633
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java
@@ -0,0 +1,109 @@
+/**
+ * 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.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+
+import org.apache.fineract.adhocquery.data.AdHocData;
+import org.apache.fineract.adhocquery.exception.AdHocNotFoundException;
+import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final AdHocMapper adHocRowMapper;
+
+ @Autowired
+ public AdHocReadPlatformServiceImpl(final RoutingDataSource dataSource) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ this.adHocRowMapper = new AdHocMapper();
+ }
+
+ @Override
+ public Collection<AdHocData> retrieveAllAdHocQuery() {
+ final String sql = "select " + this.adHocRowMapper.schema() + " order by r.id";
+
+ return this.jdbcTemplate.query(sql, this.adHocRowMapper);
+ }
+
+ @Override
+ public Collection<AdHocData> retrieveAllActiveAdHocQuery() {
+ final String sql = "select " + this.adHocRowMapper.schema() + " where r.IsActive = 1 order by r.id";
+
+ return this.jdbcTemplate.query(sql, this.adHocRowMapper);
+ }
+
+ @Override
+ public AdHocData retrieveOne(final Long id) {
+
+ try {
+ final String sql = "select " + this.adHocRowMapper.schema() + " where r.id=?";
+
+ return this.jdbcTemplate.queryForObject(sql, this.adHocRowMapper, new Object[] { id });
+ } catch (final EmptyResultDataAccessException e) {
+ throw new AdHocNotFoundException(id);
+ }
+ }
+
+ protected static final class AdHocMapper implements RowMapper<AdHocData> {
+
+ @Override
+ public AdHocData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
+
+ final Long id = JdbcSupport.getLong(rs, "id");
+ final String name = rs.getString("name");
+ final String query = rs.getString("query");
+ final String tableName=rs.getString("tableName");
+ final String tableFields=rs.getString("tableField");
+ final Boolean isActive = rs.getBoolean("isActive");
+ final DateTime createdDate = JdbcSupport.getDateTime(rs, "createdDate");
+ final Long createdById = JdbcSupport.getLong(rs, "createdById");
+ final Long updatedById=JdbcSupport.getLong(rs, "updatedById");
+ final DateTime updatedOn=JdbcSupport.getDateTime(rs, "updatedOn");
+ final String createdByUsername=rs.getString("createdBy");
+ final String email=rs.getString("email");
+
+ return new AdHocData(id,name,query, tableName,tableFields,isActive,createdDate,createdById,updatedById,updatedOn,createdByUsername,email);
+ }
+
+ public String schema() {
+ return " r.id as id, r.name as name, r.query as query, r.table_name as tableName,r.table_fields as tableField ,r.IsActive as isActive ,r.email as email ,"
+ +" r.created_date as createdDate, r.createdby_id as createdById,cb.username as createdBy,r.lastmodifiedby_id as updatedById ,r.lastmodified_date as updatedOn "
+ + " from m_adhoc r left join m_appuser cb on cb.id=r.createdby_id left join m_appuser mb on mb.id=r.lastmodifiedby_id";
+
+ }
+ }
+
+ @Override
+ public AdHocData retrieveNewAdHocDetails() {
+ return AdHocData.template();
+ }
+
+
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerService.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerService.java
new file mode 100644
index 0000000..88e24a2
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerService.java
@@ -0,0 +1,23 @@
+/**
+ * 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;
+
+public interface AdHocScheduledJobRunnerService {
+ void generateClientSchedule();
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java
new file mode 100644
index 0000000..d10cddd
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java
@@ -0,0 +1,77 @@
+/**
+ * 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.util.Collection;
+
+import org.apache.fineract.adhocquery.data.AdHocData;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service(value = "adHocScheduledJobRunnerService")
+public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunnerService {
+
+ private final static Logger logger = LoggerFactory.getLogger(AdHocScheduledJobRunnerServiceImpl.class);
+ private final AdHocReadPlatformService adHocReadPlatformService;
+ private final JdbcTemplate jdbcTemplate;
+
+ @Autowired
+ public AdHocScheduledJobRunnerServiceImpl(final RoutingDataSource dataSource,
+ final AdHocReadPlatformService adHocReadPlatformService
+ ) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ this.adHocReadPlatformService = adHocReadPlatformService;
+
+ }
+ @Transactional
+ @Override
+ @CronTarget(jobName = JobName.GENERATE_ADHOCCLIENT_SCEHDULE)
+ public void generateClientSchedule() {
+ final Collection<AdHocData> adhocs = this.adHocReadPlatformService.retrieveAllActiveAdHocQuery();
+ if(adhocs.size()>0){
+ adhocs.forEach(adhoc->{
+ //jdbcTemplate.execute("truncate table "+adhoc.getTableName());
+ final StringBuilder insertSqlBuilder = new StringBuilder(900);
+ insertSqlBuilder
+ .append("INSERT INTO ")
+ .append(adhoc.getTableName()+"(")
+ .append(adhoc.getTableFields()+") ")
+ .append(adhoc.getQuery());
+ if (insertSqlBuilder.length() > 0) {
+ final int result = this.jdbcTemplate.update(insertSqlBuilder.toString());
+ logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by inserted: " + result);
+ }
+ });
+ }else{
+ logger.info(ThreadLocalContextUtil.getTenant().getName() + "Nothing to update ");
+ }
+
+
+
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformService.java
new file mode 100644
index 0000000..23427fc
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformService.java
@@ -0,0 +1,35 @@
+/**
+ * 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 org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+
+public interface AdHocWritePlatformService {
+
+ CommandProcessingResult createAdHocQuery(JsonCommand command);
+
+ CommandProcessingResult updateAdHocQuery(Long adHocId, JsonCommand command);
+
+ CommandProcessingResult deleteAdHocQuery(Long adHocId);
+
+ CommandProcessingResult disableAdHocQuery(Long adHocId);
+
+ CommandProcessingResult enableAdHocQuery(Long adHocId);
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformServiceJpaRepositoryImpl.java
new file mode 100644
index 0000000..1cec38d
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocWritePlatformServiceJpaRepositoryImpl.java
@@ -0,0 +1,193 @@
+/**
+ * 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.util.Map;
+
+import org.apache.fineract.adhocquery.domain.AdHoc;
+import org.apache.fineract.adhocquery.domain.AdHocRepository;
+import org.apache.fineract.adhocquery.exception.AdHocNotFoundException;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class AdHocWritePlatformServiceJpaRepositoryImpl implements AdHocWritePlatformService {
+
+ private final static Logger logger = LoggerFactory.getLogger(AdHocWritePlatformServiceJpaRepositoryImpl.class);
+ private final PlatformSecurityContext context;
+ private final AdHocRepository adHocRepository;
+ private final AdHocDataValidator adHocCommandFromApiJsonDeserializer;
+
+
+ @Autowired
+ public AdHocWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final AdHocRepository adHocRepository,
+ final AdHocDataValidator adHocCommandFromApiJsonDeserializer) {
+ this.context = context;
+ this.adHocRepository = adHocRepository;
+ this.adHocCommandFromApiJsonDeserializer = adHocCommandFromApiJsonDeserializer;
+
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult createAdHocQuery(final JsonCommand command) {
+
+ try {
+ this.context.authenticatedUser();
+
+ this.adHocCommandFromApiJsonDeserializer.validateForCreate(command.json());
+
+ final AdHoc entity = AdHoc.fromJson(command);
+ this.adHocRepository.save(entity);
+
+ return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(entity.getId()).build();
+ } catch (final DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(command, dve);
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .build();
+ }
+ }
+
+ /*
+ * Guaranteed to throw an exception no matter what the data integrity issue
+ * is.
+ */
+ private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
+
+ final Throwable realCause = dve.getMostSpecificCause();
+ if (realCause.getMessage().contains("unq_name")) {
+
+ final String name = command.stringValueOfParameterNamed("name");
+ throw new PlatformDataIntegrityException("error.msg.adhocquery.duplicate.name", "AdHocQuery with name `" + name + "` already exists",
+ "name", name);
+ }
+
+ logAsErrorUnexpectedDataIntegrityException(dve);
+ throw new PlatformDataIntegrityException("error.msg.adhocquery.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource.");
+ }
+
+ private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
+ logger.error(dve.getMessage(), dve);
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult updateAdHocQuery(final Long adHocId, final JsonCommand command) {
+ try {
+ this.context.authenticatedUser();
+
+ this.adHocCommandFromApiJsonDeserializer.validateForUpdate(command.json());
+
+ final AdHoc adHoc = this.adHocRepository.findOne(adHocId);
+ if (adHoc == null) { throw new AdHocNotFoundException(adHocId); }
+
+ final Map<String, Object> changes = adHoc.update(command);
+ if (!changes.isEmpty()) {
+ this.adHocRepository.saveAndFlush(adHoc);
+ }
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(adHocId) //
+ .with(changes) //
+ .build();
+ } catch (final DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(command, dve);
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .build();
+ }
+ }
+ /**
+ * Method for Delete adhoc
+ */
+ @Transactional
+ @Override
+ public CommandProcessingResult deleteAdHocQuery(Long adHocId) {
+
+ try {
+ /**
+ * Checking the adhocQuery present in DB or not using adHocId
+ */
+ final AdHoc adHoc = this.adHocRepository.findOne(adHocId);
+ if (adHoc == null) { throw new AdHocNotFoundException(adHocId); }
+
+ this.adHocRepository.delete(adHoc);
+ return new CommandProcessingResultBuilder().withEntityId(adHocId).build();
+ } catch (final DataIntegrityViolationException e) {
+ throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource: " + e.getMostSpecificCause());
+ }
+ }
+
+ /**
+ * Method for disabling the adhocquery
+ */
+ @Transactional
+ @Override
+ public CommandProcessingResult disableAdHocQuery(Long adHocId) {
+ try {
+ /**
+ * Checking the adhocquery present in DB or not using adHocId
+ */
+ final AdHoc adHoc = this.adHocRepository.findOne(adHocId);
+ if (adHoc == null) { throw new AdHocNotFoundException(adHocId); }
+ adHoc.disableActive();
+ this.adHocRepository.save(adHoc);
+ return new CommandProcessingResultBuilder().withEntityId(adHocId).build();
+
+ } catch (final DataIntegrityViolationException e) {
+ throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource: " + e.getMostSpecificCause());
+ }
+ }
+
+ /**
+ * Method for Enabling the Active
+ */
+ @Transactional
+ @Override
+ public CommandProcessingResult enableAdHocQuery(Long adHocId) {
+ try {
+ /**
+ * Checking the adHoc present in DB or not using id
+ */
+ final AdHoc adHoc = this.adHocRepository.findOne(adHocId);
+ if (adHoc == null) { throw new AdHocNotFoundException(adHocId); }
+ adHoc.enableActive();
+ this.adHocRepository.save(adHoc);
+ return new CommandProcessingResultBuilder().withEntityId(adHocId).build();
+
+ } catch (final DataIntegrityViolationException e) {
+ throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource: " + e.getMostSpecificCause());
+ }
+ }
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 46c585b..de85e43 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -2977,4 +2977,43 @@
this.href = "/savingsaccounts/" + accountId + "?command=unblock";
return this;
}
+ public CommandWrapperBuilder disableAdHoc(Long adHocId) {
+ this.actionName = "DISABLE";
+ this.entityName = "ADHOC";
+ this.entityId = adHocId;
+ this.href = "/adhoc/" + adHocId + "/disbale";
+ this.json = "{}";
+ return this;
+ }
+
+ public CommandWrapperBuilder enableAdHoc(Long adHocId) {
+ this.actionName = "ENABLE";
+ this.entityName = "ADHOC";
+ this.entityId = adHocId;
+ this.href = "/adhoc/" + adHocId + "/enable";
+ this.json = "{}";
+ return this;
+ }
+ public CommandWrapperBuilder createAdHoc() {
+ this.actionName = "CREATE";
+ this.entityName = "ADHOC";
+ this.href = "/adhocquery/template";
+ return this;
+ }
+ public CommandWrapperBuilder updateAdHoc(final Long adHocId) {
+ this.actionName = "UPDATE";
+ this.entityName = "ADHOC";
+ this.entityId = adHocId;
+ this.href = "/adhocquery/" + adHocId;
+ return this;
+ }
+
+ public CommandWrapperBuilder deleteAdHoc(Long adHocId) {
+ this.actionName = "DELETE";
+ this.entityName = "ADHOC";
+ this.entityId = adHocId;
+ this.href = "/adhocquery/" + adHocId;
+ this.json = "{}";
+ return this;
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java
index b0b75b1..f3a160f 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java
@@ -45,8 +45,8 @@
EXECUTE_REPORT_MAILING_JOBS("Execute Report Mailing Jobs"),
UPDATE_SMS_OUTBOUND_WITH_CAMPAIGN_MESSAGE("Update SMS Outbound with Campaign Message"),
SEND_MESSAGES_TO_SMS_GATEWAY("Send Messages to SMS Gateway"),
- GET_DELIVERY_REPORTS_FROM_SMS_GATEWAY("Get Delivery Reports from SMS Gateway");
-
+ GET_DELIVERY_REPORTS_FROM_SMS_GATEWAY("Get Delivery Reports from SMS Gateway"),
+ GENERATE_ADHOCCLIENT_SCEHDULE("Generate AdhocClient Schedule");
private final String name;
private JobName(final String name) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java
index ef28243..60c2070 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java
@@ -114,4 +114,81 @@
throw new SQLInjectionException();
}
}
+ public final static void validateAdhocQuery(final String sqlSearch) {
+ String lowerCaseSQL = sqlSearch.toLowerCase();
+ for (String ddl : DDL_COMMANDS) {
+ if (lowerCaseSQL.contains(ddl)) {
+ throw new SQLInjectionException();
+ }
+ }
+
+
+ for (String comments : COMMENTS) {
+ if (lowerCaseSQL.contains(comments)) {
+ throw new SQLInjectionException();
+ }
+ }
+
+ //Removing the space before and after '=' operator
+ //String s = " \" OR 1 = 1"; For the cases like this
+ boolean injectionFound = false;
+ String inputSqlString = lowerCaseSQL;
+ while (inputSqlString.indexOf(" =") > 0) { //Don't remove space before = operator
+ inputSqlString = inputSqlString.replaceAll(" =", "=");
+ }
+
+ while (inputSqlString.indexOf("= ") > 0) { //Don't remove space after = operator
+ inputSqlString = inputSqlString.replaceAll("= ", "=");
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(inputSqlString, " ");
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken().trim();
+ if (token.equals("'")) {
+ if (tokenizer.hasMoreElements()) {
+ String nextToken = tokenizer.nextToken().trim();
+ if (!nextToken.equals("'")) {
+ injectionFound = true;
+ break;
+ }
+ } else {
+ injectionFound = true;
+ break ;
+ }
+ }
+ if (token.equals("\"")) {
+ if (tokenizer.hasMoreElements()) {
+ String nextToken = tokenizer.nextToken().trim();
+ if (!nextToken.equals("\"")) {
+ injectionFound = true;
+ break;
+ }
+ } else {
+ injectionFound = true;
+ break ;
+ }
+ } else if (token.indexOf('=') > 0) {
+ StringTokenizer operatorToken = new StringTokenizer(token, "=");
+ String operand = operatorToken.nextToken().trim();
+ if (!operatorToken.hasMoreTokens()) {
+ injectionFound = true;
+ break;
+ }
+ String value = operatorToken.nextToken().trim();
+ if (operand.equals(value)) {
+ injectionFound = true;
+ break;
+ }
+ }
+ }
+ if (injectionFound) {
+ throw new SQLInjectionException();
+ }
+
+ Pattern pattern = Pattern.compile(SQL_PATTERN);
+ Matcher matcher = pattern.matcher(sqlSearch);
+ if (!matcher.matches()) {
+ throw new SQLInjectionException();
+ }
+ }
}
diff --git a/fineract-provider/src/main/resources/META-INF/spring/appContext.xml b/fineract-provider/src/main/resources/META-INF/spring/appContext.xml
index 9fe95d4..dffd1b2 100644
--- a/fineract-provider/src/main/resources/META-INF/spring/appContext.xml
+++ b/fineract-provider/src/main/resources/META-INF/spring/appContext.xml
@@ -55,7 +55,8 @@
org.apache.fineract.template.*,
org.apache.fineract.template.service.*,
org.apache.fineract.useradministration.*,
- org.apache.fineract.batch">
+ org.apache.fineract.batch,
+ org.apache.fineract.adhocquery.*">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
@@ -80,6 +81,7 @@
<jpa:repositories base-package="org.apache.fineract.scheduledjobs.domain" />
<jpa:repositories base-package="org.apache.fineract.template.domain" />
<jpa:repositories base-package="org.apache.fineract.infrastructure.campaigns.sms.domain" />
+ <jpa:repositories base-package="org.apache.fineract.adhocquery.domain" />
<import resource="infrastructure.xml" />
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V333__adhocquery.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V333__adhocquery.sql
new file mode 100644
index 0000000..1da45cf
--- /dev/null
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V333__adhocquery.sql
@@ -0,0 +1,49 @@
+--
+-- 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.
+--
+
+CREATE TABLE `m_adhoc` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `name` VARCHAR(100) NULL DEFAULT NULL,
+ `query` VARCHAR(2000) NULL DEFAULT NULL,
+ `table_name` VARCHAR(100) NULL DEFAULT NULL,
+ `table_fields` VARCHAR(1000) NULL DEFAULT NULL,
+ `email` VARCHAR(500) NOT NULL,
+ `IsActive` TINYINT(1) NOT NULL DEFAULT '0',
+ `created_date` DATETIME NULL DEFAULT NULL,
+ `createdby_id` BIGINT NOT NULL,
+ `lastmodifiedby_id` BIGINT(20) NOT NULL,
+ `lastmodified_date` DATETIME NULL DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ CONSTRAINT `createdby_id` FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`),
+ CONSTRAINT `lastmodifiedby_id` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `m_appuser` (`id`)
+)
+COLLATE='latin1_swedish_ci'
+ENGINE=InnoDB
+;
+
+INSERT INTO `m_permission`
+(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`) VALUES
+('authorisation','UPDATE_ADHOC','ADHOC','UPDATE',1),
+('authorisation','UPDATE_ADHOC_CHECKER','ADHOC','UPDATE',0),
+('authorisation','DELETE_ADHOC','ADHOC','DELETE',1),
+('authorisation','DELETE_ADHOC_CHECKER','ADHOC','DELETE',0),
+('authorisation','CREATE_ADHOC','ADHOC','CREATE',1),
+('authorisation','CREATE_ADHOC_CHECKER','ADHOC','CREATE',0);
+
+INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`) VALUES ('Generate AdhocClient Schedule', 'Generate AdhocClient Schedule', '0 0 12 1/1 * ? *', now());