From c069a6bf3c8ac3a8d26ea40ec58c7809e28b7118 Mon Sep 17 00:00:00 2001 From: viktorpavlenko Date: Wed, 23 Apr 2025 23:28:14 +0300 Subject: [PATCH] FINERACT-2169: Refactor staff api controller; --- .../client/test/FineractClientDemo.java | 4 +- .../organisation/staff/data/StaffData.java | 6 ++ .../staff/api/StaffApiResource.java | 61 +++++++------------ .../staff/api/StaffApiResourceSwagger.java | 61 ------------------- .../organisation/staff/data/StaffRequest.java | 58 ++++++++++++++++++ .../service/StaffReadPlatformService.java | 10 +-- .../service/StaffReadPlatformServiceImpl.java | 11 ++-- .../fineract/integrationtests/StaffTest.java | 5 -- .../integrationtests/client/StaffTest.java | 8 +-- 9 files changed, 101 insertions(+), 123 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffRequest.java diff --git a/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientDemo.java b/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientDemo.java index 0a3d7ec96..9e4950b56 100644 --- a/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientDemo.java +++ b/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientDemo.java @@ -19,7 +19,7 @@ package org.apache.fineract.client.test; import java.util.List; -import org.apache.fineract.client.models.RetrieveOneResponse; +import org.apache.fineract.client.models.StaffData; import org.apache.fineract.client.util.Calls; import org.apache.fineract.client.util.FineractClient; import org.slf4j.Logger; @@ -41,7 +41,7 @@ public class FineractClientDemo { // tag::documentation[] FineractClient fineract = FineractClient.builder().baseURL("https://demo.fineract.dev/fineract-provider/api/v1/").tenant("default") .basicAuth("mifos", "password").build(); - List staff = Calls.ok(fineract.staff.retrieveAll16(1L, true, false, "ACTIVE")); + List staff = Calls.ok(fineract.staff.retrieveAll16(1L, true, false, "ACTIVE")); String name = staff.get(0).getDisplayName(); log.info("Display name: {}", name); // end::documentation[] diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/staff/data/StaffData.java b/fineract-core/src/main/java/org/apache/fineract/organisation/staff/data/StaffData.java index 1a3ce1251..bda72e532 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/staff/data/StaffData.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/staff/data/StaffData.java @@ -18,16 +18,22 @@ */ package org.apache.fineract.organisation.staff.data; +import java.io.Serial; import java.io.Serializable; import java.time.LocalDate; import java.util.Collection; +import lombok.Getter; import org.apache.fineract.organisation.office.data.OfficeData; /** * Immutable data object representing staff data. */ +@Getter public final class StaffData implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + private final Long id; private final String externalId; private final String firstname; diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResource.java index 1411bc3f2..0e88e7a8f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResource.java @@ -20,7 +20,6 @@ package org.apache.fineract.organisation.staff.api; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -41,10 +40,8 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; import java.io.InputStream; -import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; @@ -61,6 +58,7 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte import org.apache.fineract.organisation.office.data.OfficeData; import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; import org.apache.fineract.organisation.staff.data.StaffData; +import org.apache.fineract.organisation.staff.data.StaffRequest; import org.apache.fineract.organisation.staff.service.StaffReadPlatformService; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; @@ -72,12 +70,6 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class StaffApiResource { - /** - * The set of parameters that are supported in response for {@link StaffData}. - */ - private static final Set RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "firstname", "lastname", "displayName", - "officeId", "officeName", "isLoanOfficer", "externalId", "mobileNo", "allowedOffices", "isActive", "joiningDate")); - private static final String RESOURCE_NAME_FOR_PERMISSIONS = "STAFF"; private final PlatformSecurityContext context; @@ -97,22 +89,13 @@ public class StaffApiResource { + "By default it Returns all the ACTIVE Staff.\n" + "\n" + "If status=INACTIVE, then it returns all INACTIVE Staff.\n" + "\n" + "and for status=ALL, it Returns both ACTIVE and INACTIVE Staff.\n" + "\n" + "Example Requests:\n" + "\n" + "staff?status=active") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = StaffApiResourceSwagger.RetrieveOneResponse.class)))) }) - public String retrieveAll(@Context final UriInfo uriInfo, - @QueryParam("officeId") @Parameter(description = "officeId") final Long officeId, + public List retrieveAll(@QueryParam("officeId") @Parameter(description = "officeId") final Long officeId, @DefaultValue("false") @QueryParam("staffInOfficeHierarchy") @Parameter(description = "staffInOfficeHierarchy") final boolean staffInOfficeHierarchy, @DefaultValue("false") @QueryParam("loanOfficersOnly") @Parameter(description = "loanOfficersOnly") final boolean loanOfficersOnly, @DefaultValue("active") @QueryParam("status") @Parameter(description = "status") final String status) { context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); - final Collection staff; - if (staffInOfficeHierarchy) { - staff = readPlatformService.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, loanOfficersOnly); - } else { - staff = readPlatformService.retrieveAllStaff(officeId, loanOfficersOnly, status); - } - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return toApiJsonSerializer.serialize(settings, staff, RESPONSE_DATA_PARAMETERS); + return staffInOfficeHierarchy ? readPlatformService.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, loanOfficersOnly) + : readPlatformService.retrieveAllStaff(officeId, loanOfficersOnly, status); } @POST @@ -120,13 +103,13 @@ public class StaffApiResource { @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Create a staff member", description = "Creates a staff member.\n" + "\n" + "Mandatory Fields: \n" + "officeId, firstname, lastname\n" + "\n" + "Optional Fields: \n" + "isLoanOfficer, isActive") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StaffApiResourceSwagger.PostStaffRequest.class))) + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StaffRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StaffApiResourceSwagger.CreateStaffResponse.class))) }) - public String create(@Parameter(hidden = true) final String apiRequestBodyAsJson) { - final CommandWrapper commandRequest = new CommandWrapperBuilder().createStaff().withJson(apiRequestBodyAsJson).build(); - final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest); - return toApiJsonSerializer.serialize(result); + public CommandProcessingResult create(@Parameter(hidden = true) StaffRequest staffRequest) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().createStaff() + .withJson(toApiJsonSerializer.serialize(staffRequest)).build(); + return commandsSourceWritePlatformService.logCommandSource(commandRequest); } @GET @@ -135,9 +118,7 @@ public class StaffApiResource { @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Retrieve a Staff Member", description = "Returns the details of a Staff Member.\n" + "\n" + "Example Requests:\n" + "\n" + "staff/1") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StaffApiResourceSwagger.RetrieveOneResponse.class))) }) - public String retrieveOne(@PathParam("staffId") @Parameter(description = "staffId") final Long staffId, + public StaffData retrieveOne(@PathParam("staffId") @Parameter(description = "staffId") final Long staffId, @Context final UriInfo uriInfo) { context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @@ -146,7 +127,7 @@ public class StaffApiResource { final Collection allowedOffices = officeReadPlatformService.retrieveAllOfficesForDropdown(); staff = StaffData.templateData(staff, allowedOffices); } - return toApiJsonSerializer.serialize(settings, staff, RESPONSE_DATA_PARAMETERS); + return staff; } @PUT @@ -157,11 +138,12 @@ public class StaffApiResource { @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StaffApiResourceSwagger.PutStaffRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StaffApiResourceSwagger.UpdateStaffResponse.class))) }) - public String update(@PathParam("staffId") @Parameter(description = "staffId") final Long staffId, - @Parameter(hidden = true) final String apiRequestBodyAsJson) { - final CommandWrapper commandRequest = new CommandWrapperBuilder().updateStaff(staffId).withJson(apiRequestBodyAsJson).build(); - final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest); - return toApiJsonSerializer.serialize(result); + public CommandProcessingResult update(@PathParam("staffId") @Parameter(description = "staffId") final Long staffId, + @Parameter(hidden = true) StaffRequest staffRequest) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateStaff(staffId) + .withJson(toApiJsonSerializer.serialize(staffRequest)).build(); + + return commandsSourceWritePlatformService.logCommandSource(commandRequest); } @GET @@ -176,11 +158,10 @@ public class StaffApiResource { @Consumes(MediaType.MULTIPART_FORM_DATA) @RequestBody(description = "Upload staff template", content = { @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) }) - public String postTemplate(@FormDataParam("file") InputStream uploadedInputStream, + public Long postTemplate(@FormDataParam("file") InputStream uploadedInputStream, @FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale, @FormDataParam("dateFormat") final String dateFormat) { - final Long importDocumentId = bulkImportWorkbookService.importWorkbook(GlobalEntityType.STAFF.toString(), uploadedInputStream, - fileDetail, locale, dateFormat); - return toApiJsonSerializer.serialize(importDocumentId); + return bulkImportWorkbookService.importWorkbook(GlobalEntityType.STAFF.toString(), uploadedInputStream, fileDetail, locale, + dateFormat); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResourceSwagger.java index 95e5e5553..859c5ef9f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/api/StaffApiResourceSwagger.java @@ -19,7 +19,6 @@ package org.apache.fineract.organisation.staff.api; import io.swagger.v3.oas.annotations.media.Schema; -import java.time.LocalDate; /** * Created by sanyam on 19/8/17. @@ -31,36 +30,6 @@ final class StaffApiResourceSwagger { } - @Schema(description = "PostStaffRequest") - public static final class PostStaffRequest { - - private PostStaffRequest() { - - } - - @Schema(example = "1") - public Long officeId; - @Schema(example = "John") - public String firstname; - @Schema(example = "Doe") - public String lastname; - @Schema(example = "true") - public Boolean isLoanOfficer; - @Schema(example = "17H") - public String externalId; - @Schema(example = "+353851239876") - public String mobileNo; - @Schema(example = "true") - public Boolean isActive; - @Schema(example = "01 January 2009") - public LocalDate joiningDate; - @Schema(example = "en") - public String locale; - @Schema(example = "dd MMMM yyyy") - public String dateFormat; - - } - @Schema(description = "PostStaffResponse") public static final class CreateStaffResponse { @@ -74,36 +43,6 @@ final class StaffApiResourceSwagger { public Long resourceId; } - @Schema(description = "GetStaffResponse") - public static final class RetrieveOneResponse { - - private RetrieveOneResponse() { - - } - - @Schema(example = "1") - public Long id; - @Schema(example = "John") - public String firstname; - @Schema(example = "Doe") - public String lastname; - @Schema(example = "Doe, John") - public String displayName; - @Schema(example = "1") - public Long officeId; - @Schema(example = "Head Office") - public String officeName; - @Schema(example = "true") - public Boolean isLoanOfficer; - @Schema(example = "17H") - public String externalId; - @Schema(example = "+353851239876") - public Boolean isActive; - @Schema(example = "[2009,8,1]") - public LocalDate joiningDate; - - } - @Schema(description = "PutStaffRequest") public static final class PutStaffRequest { diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffRequest.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffRequest.java new file mode 100644 index 000000000..716791579 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffRequest.java @@ -0,0 +1,58 @@ +/** + * 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.organisation.staff.data; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.Serial; +import java.io.Serializable; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldNameConstants; + +@Data +@NoArgsConstructor +@FieldNameConstants +public class StaffRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(example = "1") + private Long officeId; + @Schema(example = "John") + private String firstname; + @Schema(example = "Doe") + private String lastname; + @Schema(example = "true") + private Boolean isLoanOfficer; + @Schema(example = "17H") + private String externalId; + @Schema(example = "+353851239876") + private String mobileNo; + @Schema(example = "true") + private Boolean isActive; + @Schema(example = "01 January 2009") + private String joiningDate; + @Schema(example = "en") + private String locale; + @Schema(example = "dd MMMM yyyy") + private String dateFormat; + @Schema(example = "true") + private Boolean forceStatus; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformService.java index a06411f88..e7ce95c78 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformService.java @@ -18,23 +18,23 @@ */ package org.apache.fineract.organisation.staff.service; -import java.util.Collection; +import java.util.List; import org.apache.fineract.organisation.staff.data.StaffData; public interface StaffReadPlatformService { StaffData retrieveStaff(Long staffId); - Collection retrieveAllStaffForDropdown(Long officeId); + List retrieveAllStaffForDropdown(Long officeId); - Collection retrieveAllLoanOfficersInOfficeById(Long officeId); + List retrieveAllLoanOfficersInOfficeById(Long officeId); /** * returns all staff in offices that are above the provided officeId. */ - Collection retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(Long officeId, boolean loanOfficersOnly); + List retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(Long officeId, boolean loanOfficersOnly); - Collection retrieveAllStaff(Long officeId, boolean loanOfficersOnly, String status); + List retrieveAllStaff(Long officeId, boolean loanOfficersOnly, String status); Object[] hasAssociatedItems(Long staffId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformServiceImpl.java index 57e6ad93a..7683fd4fe 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadPlatformServiceImpl.java @@ -22,7 +22,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDate; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -147,7 +146,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { } @Override - public Collection retrieveAllLoanOfficersInOfficeById(final Long officeId) { + public List retrieveAllLoanOfficersInOfficeById(final Long officeId) { SQLBuilder extraCriteria = new SQLBuilder(); extraCriteria.addCriteria(" office_id = ", officeId); extraCriteria.addCriteria(" is_loan_officer = ", true); @@ -155,7 +154,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { } @Override - public Collection retrieveAllStaffForDropdown(final Long officeId) { + public List retrieveAllStaffForDropdown(final Long officeId) { // adding the Authorization criteria so that a user cannot see an // employee who does not belong to his office or a sub office for his @@ -196,12 +195,12 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { } @Override - public Collection retrieveAllStaff(final Long officeId, final boolean loanOfficersOnly, final String status) { + public List retrieveAllStaff(final Long officeId, final boolean loanOfficersOnly, final String status) { final SQLBuilder extraCriteria = getStaffCriteria(officeId, loanOfficersOnly, status); return retrieveAllStaff(extraCriteria); } - private Collection retrieveAllStaff(final SQLBuilder extraCriteria) { + private List retrieveAllStaff(final SQLBuilder extraCriteria) { final StaffMapper rm = new StaffMapper(); String sql = "select " + rm.schema(); @@ -245,7 +244,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { } @Override - public Collection retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(final Long officeId, final boolean loanOfficersOnly) { + public List retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(final Long officeId, final boolean loanOfficersOnly) { String sql = "select " + STAFF_IN_OFFICE_HIERARCHY_MAPPER.schema(loanOfficersOnly); sql = sql + " order by s.lastname"; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/StaffTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/StaffTest.java index cbbb19dd0..a5b9013e7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/StaffTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/StaffTest.java @@ -238,11 +238,6 @@ public class StaffTest { /** Long mobileNo test */ map.put("mobileNo", Utils.uniqueRandomStringGenerator("num_", 47)); StaffHelper.updateStaff(requestSpec, responseSpecForValidationError, 1, map); - map.remove("mobileNo"); - - /** Test unsupported parameter */ - map.put("xyz", "xyz"); - StaffHelper.updateStaff(requestSpec, responseSpecForValidationError, 1, map); } @Test diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/StaffTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/StaffTest.java index c962dbf68..6ac4a77bf 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/StaffTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/StaffTest.java @@ -21,7 +21,7 @@ package org.apache.fineract.integrationtests.client; import java.time.LocalDate; import java.time.ZoneId; import java.util.Optional; -import org.apache.fineract.client.models.PostStaffRequest; +import org.apache.fineract.client.models.StaffRequest; import org.apache.fineract.integrationtests.common.Utils; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -52,14 +52,14 @@ public class StaffTest extends IntegrationTest { } Long create() { - return ok(fineractClient().staff.create3(new PostStaffRequest().officeId(1L).firstname(Utils.randomStringGenerator("StaffTest", 6)) + return ok(fineractClient().staff.create3(new StaffRequest().officeId(1L).firstname(Utils.randomStringGenerator("StaffTest", 6)) .lastname(Utils.randomStringGenerator("Staffer_", 6)).externalId(Utils.randomStringGenerator("", 12)) - .joiningDate(LocalDate.now(ZoneId.of("UTC"))).dateFormat("yyyy-MM-dd").locale("en_US"))).getResourceId(); + .joiningDate(LocalDate.now(ZoneId.of("UTC")).toString()).dateFormat("yyyy-MM-dd").locale("en_US"))).getResourceId(); } Optional retrieveFirst() { var staff = ok(fineractClient().staff.retrieveAll16(1L, true, false, "ACTIVE")); - if (staff.size() > 0) { + if (!staff.isEmpty()) { return Optional.of((long) staff.get(0).getId()); } return Optional.empty();