FINERACT-2169: Refactor staff api controller;

This commit is contained in:
viktorpavlenko
2025-04-24 14:02:27 +02:00
committed by Adam Saghy
parent e5a23406ca
commit c069a6bf3c
9 changed files with 101 additions and 123 deletions
@@ -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<RetrieveOneResponse> staff = Calls.ok(fineract.staff.retrieveAll16(1L, true, false, "ACTIVE"));
List<StaffData> staff = Calls.ok(fineract.staff.retrieveAll16(1L, true, false, "ACTIVE"));
String name = staff.get(0).getDisplayName();
log.info("Display name: {}", name);
// end::documentation[]
@@ -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;
@@ -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<String> 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<StaffData> 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<StaffData> 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<OfficeData> 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);
}
}
@@ -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 {
@@ -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;
}
@@ -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<StaffData> retrieveAllStaffForDropdown(Long officeId);
List<StaffData> retrieveAllStaffForDropdown(Long officeId);
Collection<StaffData> retrieveAllLoanOfficersInOfficeById(Long officeId);
List<StaffData> retrieveAllLoanOfficersInOfficeById(Long officeId);
/**
* returns all staff in offices that are above the provided <code>officeId</code>.
*/
Collection<StaffData> retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(Long officeId, boolean loanOfficersOnly);
List<StaffData> retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(Long officeId, boolean loanOfficersOnly);
Collection<StaffData> retrieveAllStaff(Long officeId, boolean loanOfficersOnly, String status);
List<StaffData> retrieveAllStaff(Long officeId, boolean loanOfficersOnly, String status);
Object[] hasAssociatedItems(Long staffId);
}
@@ -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<StaffData> retrieveAllLoanOfficersInOfficeById(final Long officeId) {
public List<StaffData> 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<StaffData> retrieveAllStaffForDropdown(final Long officeId) {
public List<StaffData> 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<StaffData> retrieveAllStaff(final Long officeId, final boolean loanOfficersOnly, final String status) {
public List<StaffData> retrieveAllStaff(final Long officeId, final boolean loanOfficersOnly, final String status) {
final SQLBuilder extraCriteria = getStaffCriteria(officeId, loanOfficersOnly, status);
return retrieveAllStaff(extraCriteria);
}
private Collection<StaffData> retrieveAllStaff(final SQLBuilder extraCriteria) {
private List<StaffData> 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<StaffData> retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(final Long officeId, final boolean loanOfficersOnly) {
public List<StaffData> retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(final Long officeId, final boolean loanOfficersOnly) {
String sql = "select " + STAFF_IN_OFFICE_HIERARCHY_MAPPER.schema(loanOfficersOnly);
sql = sql + " order by s.lastname";
@@ -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
@@ -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<Long> 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();