Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* 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.infrastructure.core.api.jersey;

import com.google.common.base.Splitter;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;

public class PageableParamProvider implements ValueParamProvider {

@Override
public Function<ContainerRequest, ?> getValueProvider(Parameter parameter) {

if (parameter.getRawType() == Pageable.class && parameter.isAnnotationPresent(Pagination.class)) {
return new PageableFunction(parameter);
}
return null;
}

@Override
public PriorityType getPriority() {
// Use HIGH otherwise it might not be used
return Priority.HIGH;
}

private record PageableFunction(Parameter param) implements Function<ContainerRequest, Pageable> {

@Override
public Pageable apply(ContainerRequest request) {
Pagination pagination = param.getAnnotation(Pagination.class);
MultivaluedMap<String, String> queryParameters = request.getUriInfo().getQueryParameters();
List<Sort.Order> sortingOrders = new ArrayList<>();
AtomicInteger page = new AtomicInteger(0);
int pageSize = pagination.size() > pagination.maximumSize() ? pagination.maximumSize() : pagination.size();
AtomicInteger size = new AtomicInteger(pageSize);
AtomicReference<List<String>> sort = new AtomicReference<>();
queryParameters.forEach((key, list) -> {
switch (key) {
case "page" -> page.set(Integer.parseInt(list.get(0)));
case "size" -> size.set(Integer.parseInt(list.get(0)));
case "sort" -> sort.set(list);
default -> {
}
}
});
if (sort.get() != null) {
for (String propOrder : sort.get()) {
List<String> propOrderSplit = Splitter.on(',').splitToList(propOrder);
String property = propOrderSplit.get(0);
if (propOrderSplit.size() == 1) {
sortingOrders.add(Sort.Order.by(property));
} else {
Sort.Direction direction = Sort.Direction.fromString(propOrderSplit.get(1));
sortingOrders.add(new Sort.Order(direction, property));
}
}
}

if (sortingOrders.isEmpty()) {
if (param.isAnnotationPresent(SortDefault.class)) {
SortDefault sortDefault = param.getAnnotation(SortDefault.class);

for (String sortStr : sortDefault.sort()) {
sortingOrders.add(new Sort.Order(sortDefault.direction(), sortStr));
}
} else if (param.isAnnotationPresent(SortDefault.SortDefaults.class)) {
SortDefault.SortDefaults sortDefaults = param.getAnnotation(SortDefault.SortDefaults.class);

for (SortDefault sortDefault : sortDefaults.value()) {
for (String sortStr : sortDefault.sort()) {
sortingOrders.add(new Sort.Order(sortDefault.direction(), sortStr));
}
}
}
}

return PageRequest.of(page.get(), size.get(), Sort.by(sortingOrders));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 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.infrastructure.core.api.jersey;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pagination {

@AliasFor("size")
int value() default 50;

@AliasFor("value")
int size() default 50;

int maximumSize() default 10000;
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void checkOldestCOBProcessed() throws IOException {

@Then("There are no locked loan accounts")
public void listOfLockedLoansEmpty() throws IOException {
Response<GetLoanAccountLockResponse> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute();
Response<GetLoanAccountLockResponse> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);

int size = response.body().getContent().size();
Expand All @@ -82,7 +82,7 @@ public void loanIsNotInListOfLockedLoans() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long targetLoanId = loanResponse.body().getLoanId();

Response<GetLoanAccountLockResponse> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute();
Response<GetLoanAccountLockResponse> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);

List<LoanAccountLock> content = response.body().getContent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.util.List;
import java.util.Set;

/**
Expand Down Expand Up @@ -275,6 +276,31 @@ private PaymentType() {}
}
}

@Schema(description = "PostLoansLoanIdTransactionsQueryRequest")
public static final class PostLoansLoanIdTransactionsQueryRequest {

private PostLoansLoanIdTransactionsQueryRequest() {}

@Schema(example = "[\"accrual\",\"repayment\"]") // TODO enum list
public List<String> excludedTypes;
}

@Schema(description = "GetLoansLoanIdTransactionsResponse")
public static final class GetLoansLoanIdTransactionsResponse {

private GetLoansLoanIdTransactionsResponse() {}

@Schema(example = "0")
public int page;
@Schema(example = "50")
public int size;
@Schema(example = "2")
public int totalPages;
@Schema(example = "70")
public long totalElements;
public List<GetLoansLoanIdTransactionsTransactionIdResponse> content;
}

@Schema(description = "PostLoansLoanIdTransactionsRequest")
public static final class PostLoansLoanIdTransactionsRequest {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.fineract.cob.api;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand All @@ -35,15 +36,13 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.data.LoanAccountLockResponseDTO;
import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.service.LoanAccountLockService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.api.jersey.Pagination;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

@Path("/v1/loans")
Expand All @@ -52,11 +51,9 @@
@RequiredArgsConstructor
public class LoanAccountLockApiResource {

private static final Set<String> LOAN_ACCOUNT_LOCK_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("page", "limit", "content"));
private static final Set<String> LOAN_ACCOUNT_LOCK_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("page", "size", "content"));

private final LoanAccountLockService loanAccountLockService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<LoanAccountLockResponseDTO> businessStepConfigSerializeService;

@GET
@Path("locked")
Expand All @@ -65,19 +62,17 @@ public class LoanAccountLockApiResource {
@Operation(summary = "List locked loan accounts", description = "Returns the locked loan IDs")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAccountLockApiResourceSwagger.GetLoanAccountLockResponse.class))) })
public String retrieveLockedAccounts(@Context final UriInfo uriInfo, @QueryParam("page") Integer pageParam,
@QueryParam("limit") Integer limitParam) {
int page = Objects.requireNonNullElse(pageParam, 0);
int limit = Objects.requireNonNullElse(limitParam, 50);

List<LoanAccountLock> lockedLoanAccounts = loanAccountLockService.getLockedLoanAccountByPage(page, limit);
public LoanAccountLockResponseDTO retrieveLockedAccounts(@Context final UriInfo uriInfo,
@QueryParam("page") @Parameter(description = "page") final Integer page,
@QueryParam("size") @Parameter(description = "size") final Integer size,
@QueryParam("sort") @Parameter(description = "sort") final String sort,
@Parameter(hidden = true) @Pagination Pageable pageable) {
List<LoanAccountLock> lockedLoanAccounts = loanAccountLockService.getLockedLoanAccountByPage(pageable);
LoanAccountLockResponseDTO response = new LoanAccountLockResponseDTO();
response.setPage(page);
response.setLimit(limit);
response.setPage(pageable.getPageNumber());
response.setSize(pageable.getPageSize());
response.setContent(lockedLoanAccounts);

final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());

return businessStepConfigSerializeService.serialize(settings, response, LOAN_ACCOUNT_LOCK_RESPONSE_DATA_PARAMETERS);
return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static final class GetLoanAccountLockResponse {
private GetLoanAccountLockResponse() {}

public int page;
public int limit;
public int size;
public List<LoanAccountLock> content;

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
public class LoanAccountLockResponseDTO {

private int page;
private int size;
@Deprecated
private int limit;
private List<LoanAccountLock> content;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@

import java.util.List;
import org.apache.fineract.cob.domain.LoanAccountLock;
import org.springframework.data.domain.Pageable;

public interface LoanAccountLockService {

List<LoanAccountLock> getLockedLoanAccountByPage(int page, int limit);
List<LoanAccountLock> getLockedLoanAccountByPage(Pageable pageable);

boolean isLoanHardLocked(Long loanId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
import org.apache.fineract.cob.domain.LockOwner;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
Expand All @@ -37,9 +36,8 @@ public class LoanAccountLockServiceImpl implements LoanAccountLockService {
private final LoanAccountLockRepository loanAccountLockRepository;

@Override
public List<LoanAccountLock> getLockedLoanAccountByPage(int page, int limit) {
Pageable loanAccountLockPage = PageRequest.of(page, limit);
Page<LoanAccountLock> loanAccountLocks = loanAccountLockRepository.findAll(loanAccountLockPage);
public List<LoanAccountLock> getLockedLoanAccountByPage(Pageable pageable) {
Page<LoanAccountLock> loanAccountLocks = loanAccountLockRepository.findAll(pageable);
return loanAccountLocks.getContent();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
package org.apache.fineract.infrastructure.core.jersey;

import jakarta.annotation.PostConstruct;
import jakarta.inject.Singleton;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.ext.Provider;
import org.apache.fineract.infrastructure.core.api.jersey.PageableParamProvider;
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
Expand All @@ -35,6 +39,13 @@ public class JerseyConfig extends ResourceConfig {

JerseyConfig() {
register(org.glassfish.jersey.media.multipart.MultiPartFeature.class);
register(new AbstractBinder() {

@Override
protected void configure() {
bind(PageableParamProvider.class).to(ValueParamProvider.class).in(Singleton.class);
}
});
property(ServerProperties.WADL_FEATURE_DISABLE, true);
}

Expand Down
Loading