Skip to content

Commit

Permalink
#235 | Institution Manager Application Management | Institution Summa…
Browse files Browse the repository at this point in the history
…ry Service (#251)

Co-authored-by: AhmetAksunger <[email protected]>
Co-authored-by: Emre Yılmaz <[email protected]>
  • Loading branch information
3 people authored Nov 21, 2023
1 parent c4dd8d8 commit 9746e7a
Show file tree
Hide file tree
Showing 16 changed files with 542 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.ays.institution.controller;

import com.ays.common.model.dto.response.AysResponse;
import com.ays.institution.model.Institution;
import com.ays.institution.model.dto.response.InstitutionResponse;
import com.ays.institution.model.dto.response.InstitutionsSummaryResponse;
import com.ays.institution.model.mapper.InstitutionToInstitutionsSummaryResponseMapper;
import com.ays.institution.service.InstitutionService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* REST controller class for managing institution-related operations via HTTP requests.
* This controller handles the business operations for institutions in the system.
* The mapping path for this controller is "/api/v1/institutions".
*/
@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
@Validated
class InstitutionController {

private final InstitutionService institutionService;

private final InstitutionToInstitutionsSummaryResponseMapper institutionToInstitutionsSummaryResponseMapper = InstitutionToInstitutionsSummaryResponseMapper.initialize();

/**
* Retrieves a summary of all institutions.
* Requires the user to have the 'SUPER_ADMIN' authority.
*
* @return An {@link AysResponse} containing a list of {@link InstitutionResponse} representing the summary of institutions.
*/
@GetMapping("/institutions/summary")
@PreAuthorize("hasAnyAuthority('SUPER_ADMIN')")
public AysResponse<List<InstitutionsSummaryResponse>> getSummaryOfActiveInstitutions() {

final List<Institution> institutionsSummary = institutionService.getSummaryOfActiveInstitutions();
final List<InstitutionsSummaryResponse> institutionsSummaryRespons = institutionToInstitutionsSummaryResponseMapper.map(institutionsSummary);
return AysResponse.successOf(institutionsSummaryRespons);
}

}
3 changes: 2 additions & 1 deletion src/main/java/com/ays/institution/model/Institution.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ays.institution.model;

import com.ays.common.model.BaseDomainModel;
import com.ays.institution.model.enums.InstitutionStatus;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
Expand All @@ -17,5 +18,5 @@ public class Institution extends BaseDomainModel {

private String id;
private String name;

private InstitutionStatus status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ays.institution.model.dto.response;

import lombok.Getter;
import lombok.Setter;

/**
* This class represents the response for a single institution.
* It includes information such as the institution's id and name.
*/
@Getter
@Setter
public class InstitutionsSummaryResponse {

private String id;
private String name;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ays.institution.model.mapper;

import com.ays.common.model.mapper.BaseMapper;
import com.ays.institution.model.Institution;
import com.ays.institution.model.entity.InstitutionEntity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

/**
* InstitutionEntityToInstitutionMapper is an interface that defines the mapping between an {@link InstitutionEntity} and an {@link Institution}.
* This interface uses the MapStruct annotation @Mapper to generate an implementation of this interface at compile-time.
* <p>The class provides a static method {@code initialize()} that returns an instance of the generated mapper implementation.
* <p>The interface extends the MapStruct interface {@link BaseMapper}, which defines basic mapping methods.
* The interface adds no additional mapping methods, but simply defines the types to be used in the mapping process.
*/
@Mapper
public interface InstitutionEntityToInstitutionMapper extends BaseMapper<InstitutionEntity, Institution> {

/**
* Initializes the mapper.
*
* @return the initialized mapper object.
*/
static InstitutionEntityToInstitutionMapper initialize() {
return Mappers.getMapper(InstitutionEntityToInstitutionMapper.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ays.institution.model.mapper;

import com.ays.common.model.mapper.BaseMapper;
import com.ays.institution.model.Institution;
import com.ays.institution.model.dto.response.InstitutionsSummaryResponse;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

/**
* InstitutionToInstitutionSummaryResponseMapper is an interface that defines the mapping between an {@link Institution} and an {@link InstitutionsSummaryResponse}.
* This interface uses the MapStruct annotation @Mapper to generate an implementation of this interface at compile-time.
* <p>The class provides a static method {@code initialize()} that returns an instance of the generated mapper implementation.
* <p>The interface extends the MapStruct interface {@link BaseMapper}, which defines basic mapping methods.
* The interface adds no additional mapping methods, but simply defines the types to be used in the mapping process.
*/
@Mapper
public interface InstitutionToInstitutionsSummaryResponseMapper extends BaseMapper<Institution, InstitutionsSummaryResponse> {

/**
* Initializes the mapper.
*
* @return the initialized mapper object.
*/
static InstitutionToInstitutionsSummaryResponseMapper initialize() {
return Mappers.getMapper(InstitutionToInstitutionsSummaryResponseMapper.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
package com.ays.institution.repository;

import com.ays.institution.model.entity.InstitutionEntity;
import com.ays.institution.model.enums.InstitutionStatus;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
* An interface for accessing and managing institutions in a data source with CRUD operations.
* It extends the JpaRepository interface with InstitutionEntity as the entity type and String as the ID type.
* The default behavior of the repository can be extended by adding custom methods to this interface.
*/
public interface InstitutionRepository extends JpaRepository<InstitutionEntity, String> {

/**
* Find all institutions by status
*
* @param status the status of the institutions to retrieve
* @return the list of institutions
*/
List<InstitutionEntity> findAllByStatusOrderByNameAsc(InstitutionStatus status);

}
19 changes: 19 additions & 0 deletions src/main/java/com/ays/institution/service/InstitutionService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ays.institution.service;

import com.ays.institution.model.Institution;

import java.util.List;

/**
* Institution Service to perform institution related business operations.
*/
public interface InstitutionService {

/**
* Get all active institutions summary
*
* @return list of active institutions
*/
List<Institution> getSummaryOfActiveInstitutions();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.ays.institution.service.impl;

import com.ays.institution.model.Institution;
import com.ays.institution.model.entity.InstitutionEntity;
import com.ays.institution.model.enums.InstitutionStatus;
import com.ays.institution.model.mapper.InstitutionEntityToInstitutionMapper;
import com.ays.institution.repository.InstitutionRepository;
import com.ays.institution.service.InstitutionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* Implementation of the {@link InstitutionService} interface for performing institution-related business operations.
*/
@Service
@RequiredArgsConstructor
class InstitutionServiceImpl implements InstitutionService {

private final InstitutionRepository institutionRepository;

private final InstitutionEntityToInstitutionMapper institutionEntityToInstitutionMapper = InstitutionEntityToInstitutionMapper.initialize();

/**
* Retrieves a summary of all active institutions.
*
* @return a list of {@link Institution} representing the summary of institutions
*/
@Override
public List<Institution> getSummaryOfActiveInstitutions() {
final List<InstitutionEntity> activeInstitutions = institutionRepository.findAllByStatusOrderByNameAsc(InstitutionStatus.ACTIVE);
return institutionEntityToInstitutionMapper.map(activeInstitutions);
}

}
4 changes: 4 additions & 0 deletions src/test/java/com/ays/AbstractRestControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.ays.parameter.model.AysParameter;
import com.ays.parameter.model.AysParameterBuilder;
import com.ays.parameter.service.AysParameterService;
import com.ays.super_admin.entity.SuperAdminEntityBuilder;
import com.ays.user.model.entity.UserEntityBuilder;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
Expand Down Expand Up @@ -35,6 +36,7 @@ public abstract class AbstractRestControllerTest extends AbstractTestContainerCo

@Autowired
protected MockMvc mockMvc;
protected AysToken mockSuperAdminToken;
protected AysToken mockAdminUserToken;
protected AysToken mockUserToken;

Expand All @@ -49,7 +51,9 @@ public void initializeAuth() {
Set<AysParameter> parameters = AysParameterBuilder.getParameters();
Mockito.when(parameterService.getParameters(Mockito.anyString()))
.thenReturn(parameters);

this.tokenConfiguration = new AysTokenConfigurationParameter(parameterService);
this.mockSuperAdminToken = this.generate(new SuperAdminEntityBuilder().withValidFields().build().getClaims());
this.mockAdminUserToken = this.generate(new AdminUserEntityBuilder().build().getClaims());
this.mockUserToken = this.generate(new UserEntityBuilder().build().getClaims());
}
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/com/ays/AbstractSystemTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.ays.parameter.model.AysParameter;
import com.ays.parameter.model.AysParameterBuilder;
import com.ays.parameter.service.AysParameterService;
import com.ays.super_admin.entity.SuperAdminEntityBuilder;
import com.ays.user.model.entity.UserEntityBuilder;
import com.ays.util.AysTestData;
import io.jsonwebtoken.JwtBuilder;
Expand Down Expand Up @@ -37,6 +38,7 @@ public abstract class AbstractSystemTest extends AbstractTestContainerConfigurat
@Autowired
protected MockMvc mockMvc;

protected AysToken superAdminToken;
protected AysToken adminUserTokenOne;
protected AysToken adminUserTokenTwo;
protected AysToken userTokenOne;
Expand All @@ -58,6 +60,14 @@ public void initializeAuth() {
.thenReturn(parameters);
this.tokenConfiguration = new AysTokenConfigurationParameter(parameterService);

final Map<String, Object> claimsOfSuperAdminOne = new SuperAdminEntityBuilder()
.withId(AysTestData.SuperAdminUser.VALID_ID_ONE)
.withUsername(AysTestData.SuperAdminUser.VALID_USERNAME_ONE)
.withInstitutionId(null)
.build()
.getClaims();
this.superAdminToken = this.generate(claimsOfSuperAdminOne);

final Map<String, Object> claimsOfAdminUserOne = new AdminUserEntityBuilder()
.withId(AysTestData.AdminUser.VALID_ID_ONE)
.withUsername(AysTestData.AdminUser.VALID_USERNAME_ONE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.ays.institution.controller;

import com.ays.AbstractRestControllerTest;
import com.ays.common.model.dto.response.AysResponse;
import com.ays.common.model.dto.response.AysResponseBuilder;
import com.ays.common.util.exception.model.AysError;
import com.ays.institution.model.Institution;
import com.ays.institution.model.dto.response.InstitutionsSummaryResponse;
import com.ays.institution.model.entity.InstitutionBuilder;
import com.ays.institution.model.mapper.InstitutionToInstitutionsSummaryResponseMapper;
import com.ays.institution.service.InstitutionService;
import com.ays.util.AysMockMvcRequestBuilders;
import com.ays.util.AysMockResultMatchersBuilders;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;

import java.util.List;

class InstitutionControllerTest extends AbstractRestControllerTest {

@MockBean
private InstitutionService institutionService;


private final InstitutionToInstitutionsSummaryResponseMapper institutionToInstitutionsSummaryResponseMapper = InstitutionToInstitutionsSummaryResponseMapper.initialize();


private static final String BASE_PATH = "/api/v1/institutions";

@Test
void whenInstitutionStatusActive_thenReturnListInstitutionResponse() throws Exception {

// When
List<Institution> mockActiveInstitutions = List.of(
new InstitutionBuilder().withValidFields().build(),
new InstitutionBuilder().withValidFields().build()
);

Mockito.when(institutionService.getSummaryOfActiveInstitutions()).thenReturn(mockActiveInstitutions);

// Then
List<InstitutionsSummaryResponse> mockActiveInstitutionsSummaryResponses = institutionToInstitutionsSummaryResponseMapper.map(mockActiveInstitutions);
AysResponse<List<InstitutionsSummaryResponse>> mockAysResponse = AysResponse.successOf(mockActiveInstitutionsSummaryResponses);

mockMvc.perform(AysMockMvcRequestBuilders
.get(BASE_PATH.concat("/summary"), mockSuperAdminToken.getAccessToken()))
.andDo(MockMvcResultHandlers.print())
.andExpect(AysMockResultMatchersBuilders.status().isOk())
.andExpect(AysMockResultMatchersBuilders.time()
.isNotEmpty())
.andExpect(AysMockResultMatchersBuilders.httpStatus()
.value(mockAysResponse.getHttpStatus().getReasonPhrase()))
.andExpect(AysMockResultMatchersBuilders.isSuccess()
.value(mockAysResponse.getIsSuccess()))
.andExpect(AysMockResultMatchersBuilders.response()
.isNotEmpty());

}

@Test
void whenUserUnauthorizedForSummary_thenReturnAccessDeniedException() throws Exception {

// When
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.get(BASE_PATH.concat("/summary"), mockUserToken.getAccessToken());

// Then
AysResponse<AysError> mockResponse = AysResponseBuilder.FORBIDDEN;
mockMvc.perform(mockHttpServletRequestBuilder)
.andDo(MockMvcResultHandlers.print())
.andExpect(AysMockResultMatchersBuilders.status().isForbidden())
.andExpect(AysMockResultMatchersBuilders.time().isNotEmpty())
.andExpect(AysMockResultMatchersBuilders.httpStatus().value(mockResponse.getHttpStatus().name()))
.andExpect(AysMockResultMatchersBuilders.isSuccess().value(mockResponse.getIsSuccess()))
.andExpect(AysMockResultMatchersBuilders.response().doesNotExist());
}

}
Loading

0 comments on commit 9746e7a

Please sign in to comment.