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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + + final List 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + + final List 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 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 00000000000..414a2b2af6b --- /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 retrieveAllAdHocQuery(); + + Collection 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 00000000000..eb1f6335e3a --- /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 retrieveAllAdHocQuery() { + final String sql = "select " + this.adHocRowMapper.schema() + " order by r.id"; + + return this.jdbcTemplate.query(sql, this.adHocRowMapper); + } + + @Override + public Collection 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 { + + @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 00000000000..88e24a25533 --- /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 00000000000..d10cdddecae --- /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 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 00000000000..23427fcffe7 --- /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 00000000000..1cec38df54e --- /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 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 46c585bd518..de85e435d01 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 @@ public CommandWrapperBuilder unblockSavingsAccount(final Long accountId) { 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 b0b75b19557..f3a160f96da 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 @@ public enum JobName { 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 ef2824391d2..60c2070095d 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 @@ -46,6 +46,83 @@ public final static void validateSQLInput(final String sqlSearch) { } } + 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(); + } + } + 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(); 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 9fe95d447e1..dffd1b26bdf 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.*"> @@ -80,6 +81,7 @@ + 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 00000000000..1da45cf4229 --- /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());