init
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
package com.eactive.apim.gateway.data;
|
||||
|
||||
import com.eactive.eai.data.entity.onl.authserver.ClientEntity;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
public interface ClientEntityRepository extends BaseRepository<ClientEntity, String> {
|
||||
|
||||
Page<ClientEntity> findAllByOrgid(Pageable pageable, String orgid);
|
||||
|
||||
List<ClientEntity> findAllByOrgid(String orgid);
|
||||
|
||||
Optional<ClientEntity> findByClientidAndOrgid(String clientid, String orgid);
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.gateway.data.apiservice.repository;
|
||||
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroup;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface ApiServiceRepository extends JpaRepository<ApiGroup, String>, JpaSpecificationExecutor<ApiGroup> {
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.gateway.data.bizcode;
|
||||
|
||||
import com.eactive.eai.data.entity.onl.unifbwk.UnifBwkTp;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface UnifBwkTpRepository extends JpaRepository<UnifBwkTp, String>, JpaSpecificationExecutor<UnifBwkTp>{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.gateway.data.eaimessage;
|
||||
|
||||
import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface EAIMessageRepository extends JpaRepository<EAIMessageEntity, String>, JpaSpecificationExecutor<EAIMessageEntity> {
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.gateway.data.standardmessageinfo;
|
||||
|
||||
import com.eactive.eai.data.entity.onl.stdmessage.StandardMessageInfo;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
public interface StandardMessageInfoRepository extends JpaRepository<StandardMessageInfo, String>,JpaSpecificationExecutor<StandardMessageInfo> {
|
||||
Optional<StandardMessageInfo> findByEaisvcname(String eaiSvcName);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.eactive.apim.portal;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication(exclude = {
|
||||
UserDetailsServiceAutoConfiguration.class,
|
||||
},
|
||||
scanBasePackages = "com.eactive.apim.portal")
|
||||
@EnableScheduling
|
||||
@EnableConfigurationProperties
|
||||
@ConfigurationPropertiesScan(basePackages = {"com.eactive.apim.portal"})
|
||||
public class PortalApplication extends SpringBootServletInitializer {
|
||||
|
||||
private static final Logger portal_logger = LoggerFactory.getLogger(PortalApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
portal_logger.info("##### PortalApplication Start #####");
|
||||
|
||||
SpringApplication.run(new Class[]{PortalApplication.class}, args);
|
||||
|
||||
portal_logger.info("##### PortalApplication Ready #####");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.eactive.apim.portal.apps;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class HealthCheckController {
|
||||
@GetMapping(value = "/health", produces = "text/plain")
|
||||
public String health() {
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.eactive.apim.portal.apps.agreements.controller;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementType;
|
||||
import com.eactive.apim.portal.apps.agreements.dto.AgreementsDTO;
|
||||
import com.eactive.apim.portal.apps.agreements.service.AgreementsFacade;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/agreements")
|
||||
public class AgreementsController {
|
||||
public static final String TERMS_AGREEMENTS = "fragment/kbank/terms_agreements";
|
||||
|
||||
private final AgreementsFacade agreementsFacade;
|
||||
|
||||
@Autowired
|
||||
public AgreementsController(AgreementsFacade agreementsFacade) {
|
||||
this.agreementsFacade = agreementsFacade;
|
||||
}
|
||||
|
||||
@GetMapping("/terms")
|
||||
public String showTerms(@RequestParam(required = false) String tab,
|
||||
@RequestParam(required = false) String publishedOn,
|
||||
Model model) {
|
||||
AgreementType type;
|
||||
if ("privacy".equals(tab)) {
|
||||
type = AgreementType.PRIVACY_POLICY;
|
||||
} else {
|
||||
type = AgreementType.TERMS_OF_USE;
|
||||
}
|
||||
|
||||
List<AgreementsDTO> agreementsList = agreementsFacade.getAgreementsList(String.valueOf(type));
|
||||
model.addAttribute("agreementsList", agreementsList);
|
||||
|
||||
AgreementsDTO selectedAgreement = publishedOn != null && !publishedOn.isEmpty() ?
|
||||
findAgreementByDate(agreementsList, publishedOn) : agreementsList.get(0);
|
||||
|
||||
model.addAttribute("selectedAgreement", selectedAgreement);
|
||||
model.addAttribute("selectedDate", publishedOn);
|
||||
|
||||
model.addAttribute("isTermsOfUse", type == AgreementType.TERMS_OF_USE);
|
||||
model.addAttribute("isPrivacyPolicy", type == AgreementType.PRIVACY_POLICY);
|
||||
model.addAttribute("agreementTitle", type.getDescription());
|
||||
model.addAttribute("agreementType", type.getCode());
|
||||
|
||||
model.addAttribute("currentTab", tab != null ? tab : "terms");
|
||||
|
||||
return TERMS_AGREEMENTS;
|
||||
}
|
||||
|
||||
private AgreementsDTO findAgreementByDate(List<AgreementsDTO> agreements, String publishedOn) {
|
||||
return agreements.stream()
|
||||
.filter(a -> publishedOn.equals(
|
||||
a.getPublishedOn().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))))
|
||||
.findFirst()
|
||||
.orElse(agreements.get(0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.eactive.apim.portal.apps.agreements.dto;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementsStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class AgreementsDTO {
|
||||
/* pk */
|
||||
private String agreementsType;
|
||||
|
||||
private int revision;
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private String contents;
|
||||
|
||||
private AgreementsStatus status;
|
||||
|
||||
/* 제정일 */
|
||||
private LocalDateTime publishedOn;
|
||||
|
||||
/* 시행일 */
|
||||
private LocalDateTime scheduledOn;
|
||||
|
||||
|
||||
private String createdBy;
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private String updatedBy;
|
||||
|
||||
private LocalDateTime updatedDate;
|
||||
|
||||
// 기본 생성자 추가
|
||||
public AgreementsDTO() {
|
||||
this.contents = ""; // null 방지를 위한 기본값 설정
|
||||
this.name = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.eactive.apim.portal.apps.agreements.mapper;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.Agreements;
|
||||
import com.eactive.apim.portal.apps.agreements.dto.AgreementsDTO;
|
||||
import com.eactive.apim.portal.common.mapper.CommonMapper;
|
||||
import java.util.List;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Mapper(componentModel = "spring",
|
||||
uses = {CommonMapper.class},
|
||||
unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
@Component
|
||||
|
||||
public interface AgreementsMapper {
|
||||
|
||||
AgreementsDTO map(Agreements entity);
|
||||
|
||||
List<AgreementsDTO> map(List<Agreements> agreementsList);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eactive.apim.portal.apps.agreements.service;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementType;
|
||||
import com.eactive.apim.portal.apps.agreements.dto.AgreementsDTO;
|
||||
import java.util.List;
|
||||
|
||||
public interface AgreementsFacade {
|
||||
|
||||
AgreementsDTO getAgreement(String agreementsType);
|
||||
|
||||
List<AgreementsDTO> getAgreementsList(String agreementsType);
|
||||
|
||||
void saveUserAgreements(String userId, AgreementType privacyCollectType);
|
||||
|
||||
void deleteUserAgreements(String userId);
|
||||
|
||||
}
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package com.eactive.apim.portal.apps.agreements.service;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementType;
|
||||
import com.eactive.apim.portal.agreements.entity.Agreements;
|
||||
import com.eactive.apim.portal.agreements.service.AgreementsService;
|
||||
import com.eactive.apim.portal.apps.agreements.dto.AgreementsDTO;
|
||||
import com.eactive.apim.portal.apps.agreements.mapper.AgreementsMapper;
|
||||
import com.eactive.apim.portal.apps.user.service.PortalUserPrivacyAgreementService;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class AgreementsFacadeImpl implements AgreementsFacade {
|
||||
|
||||
private final AgreementsService agreementsService;
|
||||
private final AgreementsMapper agreementsMapper;
|
||||
private final PortalUserPrivacyAgreementService userAgreementService;
|
||||
|
||||
@Autowired
|
||||
public AgreementsFacadeImpl(AgreementsService agreementsService,
|
||||
AgreementsMapper agreementsMapper,
|
||||
PortalUserPrivacyAgreementService userAgreementService) {
|
||||
this.agreementsService = agreementsService;
|
||||
this.agreementsMapper = agreementsMapper;
|
||||
this.userAgreementService = userAgreementService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveUserAgreements(String userId, AgreementType privacyCollectType) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
// 1. 이용약관 처리
|
||||
Optional<Agreements> termsOfUse = agreementsService
|
||||
.findLastesBeforeDate(
|
||||
AgreementType.TERMS_OF_USE,
|
||||
now);
|
||||
if (termsOfUse.isPresent()) {
|
||||
userAgreementService.saveOrUpdateAgreements(
|
||||
userId, // UUID 사용
|
||||
String.valueOf(AgreementType.TERMS_OF_USE),
|
||||
termsOfUse.get().getRevision()
|
||||
);
|
||||
} else {
|
||||
throw new NotFoundException("현재 시행중인 이용약관이 없습니다.");
|
||||
}
|
||||
|
||||
// 2. 개인정보수집동의서 처리
|
||||
if (privacyCollectType != null && privacyCollectType.isPrivacyCollect()) {
|
||||
Optional<Agreements> privacyCollect = agreementsService
|
||||
.findLastesBeforeDate(
|
||||
privacyCollectType,
|
||||
now);
|
||||
if (privacyCollect.isPresent()) {
|
||||
userAgreementService.saveOrUpdateAgreements(
|
||||
userId,
|
||||
String.valueOf(privacyCollectType),
|
||||
privacyCollect.get().getRevision()
|
||||
);
|
||||
} else {
|
||||
throw new NotFoundException("현재 시행중인 개인정보수집동의서가 없습니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HTML 언이스케이프 처리
|
||||
private AgreementsDTO convertToDTO(Agreements agreement) {
|
||||
AgreementsDTO dto = agreementsMapper.map(agreement);
|
||||
if (dto.getContents() != null) {
|
||||
dto.setContents(StringEscapeUtils.unescapeHtml4(dto.getContents()));
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgreementsDTO getAgreement(String agreementsTypeCode) {
|
||||
LocalDateTime date = LocalDateTime.now();
|
||||
AgreementType agreementType = AgreementType.fromCode(agreementsTypeCode);
|
||||
|
||||
// 1. 현재 날짜 이전의 가장 최근 약관 조회
|
||||
Optional<Agreements> beforeToday = agreementsService
|
||||
.findLastesBeforeDate(agreementType, date);
|
||||
if (beforeToday.isPresent()) {
|
||||
return convertToDTO(beforeToday.get());
|
||||
}
|
||||
|
||||
// 2. 현재 날짜 이후의 가장 가까운 약관 조회
|
||||
Optional<Agreements> afterToday = agreementsService
|
||||
.findEarliesAfterDate(String.valueOf(agreementType), date);
|
||||
if (afterToday.isPresent()) {
|
||||
return convertToDTO(afterToday.get());
|
||||
}
|
||||
|
||||
// 3. 약관이 없는 경우 기본 약관
|
||||
return createDefaultAgreement(String.valueOf(agreementType));
|
||||
}
|
||||
|
||||
private AgreementsDTO createDefaultAgreement(String agreementType) {
|
||||
AgreementsDTO defaultAgreement = new AgreementsDTO();
|
||||
defaultAgreement.setAgreementsType(agreementType);
|
||||
defaultAgreement.setRevision(0);
|
||||
|
||||
switch (agreementType) {
|
||||
case "TERMS_OF_USE":
|
||||
defaultAgreement.setName("이용약관");
|
||||
defaultAgreement.setContents("현재 이용약관을 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.");
|
||||
break;
|
||||
case "PRIVACY_POLICY":
|
||||
defaultAgreement.setName("개인정보처리방침");
|
||||
defaultAgreement.setContents("현재 개인정보처리방침을 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.");
|
||||
break;
|
||||
case "PRIVACY_COLLECT_IND":
|
||||
defaultAgreement.setName("개인용 개인정보수집동의서");
|
||||
defaultAgreement.setContents("현재 개인용 개인정보수집동의서를 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.");
|
||||
break;
|
||||
case "PRIVACY_COLLECT_ORG":
|
||||
defaultAgreement.setName("법인용 개인정보수집동의서");
|
||||
defaultAgreement.setContents("현재 법인용 개인정보수집동의서를 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.");
|
||||
break;
|
||||
default:
|
||||
defaultAgreement.setName("약관");
|
||||
defaultAgreement.setContents("약관 내용을 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.");
|
||||
break;
|
||||
}
|
||||
|
||||
return defaultAgreement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AgreementsDTO> getAgreementsList(String agreementsType) {
|
||||
AgreementType agreementTypeCode = AgreementType.fromCode(agreementsType);
|
||||
List<Agreements> agreementsList = agreementsService.findAllAgreementsSortPublishedOn(agreementTypeCode);
|
||||
if (agreementsList == null || agreementsList.isEmpty()) {
|
||||
return Collections.singletonList(createDefaultAgreement(String.valueOf(agreementTypeCode)));
|
||||
}
|
||||
return agreementsList.stream()
|
||||
.map(agreement -> {
|
||||
AgreementsDTO dto = agreementsMapper.map(agreement);
|
||||
|
||||
// HTML 언이스케이프 처리
|
||||
if (dto.getContents() != null) {
|
||||
dto.setContents(StringEscapeUtils.unescapeHtml4(dto.getContents()));
|
||||
}
|
||||
return dto;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteUserAgreements(String userId) {
|
||||
userAgreementService.deleteUserAgreements(userId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.eactive.apim.portal.apps.apis.controller;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.apim.portal.apps.app.service.AppServiceFacade;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/apis")
|
||||
@RequiredArgsConstructor
|
||||
public class ApiAjaxController {
|
||||
|
||||
private final static String STG_SERVER = "STG";
|
||||
|
||||
private final ApiService apiService;
|
||||
private final ApiServiceService apiServiceService;
|
||||
private final AppServiceFacade appServiceFacade;
|
||||
|
||||
public Map<String, ApiServiceDTO> getMainIconsFromServiceDtos(List<ApiServiceDTO> apiServices) {
|
||||
Map<String, ApiServiceDTO> mainIconsMap = new HashMap<>();
|
||||
|
||||
apiServices.forEach(service -> {
|
||||
service.getApiGroupApiList().forEach(api -> {
|
||||
String apiId = api.getApiId();
|
||||
mainIconsMap.put(apiId, service);
|
||||
});
|
||||
});
|
||||
|
||||
return mainIconsMap;
|
||||
}
|
||||
|
||||
@GetMapping("/for_request")
|
||||
@Secured("ROLE_API_KEY_REQUEST")
|
||||
public List<ApiSpecInfoDto> apiRequest(@ModelAttribute ApiGroupSearch search) {
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(search);
|
||||
Map<String, ApiServiceDTO> mainIconsMap = getMainIconsFromServiceDtos(apiServices);
|
||||
List<ApiSpecInfoDto> apiSpecInfoDtos = apiService.findApis();
|
||||
|
||||
for (ApiSpecInfoDto spec : apiSpecInfoDtos) {
|
||||
ApiServiceDTO serviceDTO = mainIconsMap.get(spec.getApiId());
|
||||
if (serviceDTO != null) {
|
||||
String mainIcon = serviceDTO.getMainIcon();
|
||||
String service = serviceDTO.getGroupName();
|
||||
spec.setMainIcon(mainIcon);
|
||||
spec.setService(service);
|
||||
}
|
||||
}
|
||||
return apiSpecInfoDtos;
|
||||
}
|
||||
|
||||
@GetMapping("/for_request_prod")
|
||||
@Secured("ROLE_API_KEY_REQUEST")
|
||||
public List<ApiSpecInfoDto> apiRequestProd(@ModelAttribute ApiGroupSearch search) {
|
||||
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(search);
|
||||
Map<String, ApiServiceDTO> mainIconsMap = getMainIconsFromServiceDtos(apiServices);
|
||||
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
|
||||
List<ClientDTO> apikeys = appServiceFacade.getApiKeyList(user.getPortalOrg(), STG_SERVER);
|
||||
|
||||
List<ApiServiceApiDTO> apis = apikeys.stream()
|
||||
.flatMap(client -> client.getApiList().stream()) // flatten all apiLists into a single stream of ApiServiceApiDTO
|
||||
.distinct() // remove duplicates
|
||||
.collect(Collectors.toList()); // collect to List
|
||||
|
||||
List<ApiSpecInfoDto> apiSpecInfoDtos = new ArrayList<>();
|
||||
|
||||
apis.forEach(api -> {
|
||||
ApiSpecInfoDto specInfoDto = new ApiSpecInfoDto();
|
||||
specInfoDto.setApiId(api.getApiId());
|
||||
specInfoDto.setApiName(api.getApiDesc());
|
||||
apiSpecInfoDtos.add(specInfoDto);
|
||||
});
|
||||
|
||||
for (ApiSpecInfoDto spec : apiSpecInfoDtos) {
|
||||
ApiServiceDTO serviceDTO = mainIconsMap.get(spec.getApiId());
|
||||
if (serviceDTO != null) {
|
||||
String mainIcon = serviceDTO.getMainIcon();
|
||||
String service = serviceDTO.getGroupName();
|
||||
spec.setMainIcon(mainIcon);
|
||||
spec.setService(service);
|
||||
}
|
||||
}
|
||||
return apiSpecInfoDtos;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.eactive.apim.portal.apps.apis.controller;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiSearchFacade;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/apis")
|
||||
@RequiredArgsConstructor
|
||||
public class ApiController {
|
||||
private static final String NOT_FOUND_MESSAGE = "찾을 수 없습니다.";
|
||||
private final ApiService apiService;
|
||||
private final ApiServiceService apiServiceService;
|
||||
private final ApiSearchFacade apiSearchFacade;
|
||||
private static final String DEFAULT_TOKEN_API_ID = "default-token-api-spec";
|
||||
private static final String DEFAULT_TOKEN_API_NAME = "인증";
|
||||
|
||||
@GetMapping("/detail")
|
||||
public String apidetail(@RequestParam(value = "id", required = false) String id, ModelMap model) {
|
||||
if( id ==null) {
|
||||
return "redirect:/apis/common";
|
||||
}
|
||||
|
||||
ApiSpecInfoDto api = apiService.selectDetail(id);
|
||||
if(api==null){
|
||||
throw new NotFoundException(NOT_FOUND_MESSAGE);
|
||||
}
|
||||
model.addAttribute("apiSpecInfo", api);
|
||||
return "apps/apis/mainApiDetail";
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String apiList(@ModelAttribute ApiGroupSearch search, Model model) {
|
||||
Map<String, Object> searchResult = apiSearchFacade.searchApis(search);
|
||||
|
||||
model.addAttribute("search", search);
|
||||
model.addAttribute("services", searchResult.get("services"));
|
||||
model.addAttribute("apis", searchResult.get("apis"));
|
||||
model.addAttribute("totalApiCount", searchResult.get("totalApiCount"));
|
||||
model.addAttribute("selectedApiCount", searchResult.get("selectedApiCount"));
|
||||
model.addAttribute("selected", search.getGroupIds().size() >0 ? search.getGroupIds().get(0) : "-1");
|
||||
|
||||
return "apps/apis/mainApiList";
|
||||
}
|
||||
|
||||
@GetMapping("/testbed/api")
|
||||
public String testbedByApi(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
String selectedApiServiceName = "API 서비스 선택";
|
||||
String selectedServiceId = "";
|
||||
boolean idExists = false;
|
||||
|
||||
try {
|
||||
if (id != null && !id.isEmpty()) {
|
||||
ApiServiceDTO apiService = apiServiceService.findApiServiceByApiId(id);
|
||||
if (apiService != null) {
|
||||
selectedApiServiceName = apiService.getGroupName();
|
||||
selectedServiceId = apiService.getId();
|
||||
idExists = true;
|
||||
setupTestbedModel(model, selectedServiceId, selectedApiServiceName, idExists);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
model.addAttribute("errorMessage", "API 정보를 조회하는 중 오류가 발생했습니다.");
|
||||
}
|
||||
|
||||
setupTestbedModel(model, selectedServiceId, selectedApiServiceName, idExists);
|
||||
|
||||
return "apps/apis/mainTestbed";
|
||||
}
|
||||
|
||||
@GetMapping("/testbed")
|
||||
public String testbedByApiService(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
String selectedApiServiceName = "API 서비스 선택";
|
||||
boolean idExists = false;
|
||||
|
||||
try {
|
||||
if (id != null && !id.isEmpty()) {
|
||||
List<ApiServiceDTO> apiServiceDtoList = apiServiceService.searchApiGroupsForLnb();
|
||||
Optional<ApiServiceDTO> selectedApi = apiServiceDtoList.stream()
|
||||
.filter(api -> api.getId().equals(id))
|
||||
.findFirst();
|
||||
|
||||
if (selectedApi.isPresent()) {
|
||||
selectedApiServiceName = selectedApi.get().getGroupName();
|
||||
idExists = true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
model.addAttribute("errorMessage", "API 서비스 정보를 조회하는 중 오류가 발생했습니다.");
|
||||
}
|
||||
|
||||
setupTestbedModel(model, id, selectedApiServiceName, idExists);
|
||||
|
||||
return "apps/apis/mainTestbed";
|
||||
}
|
||||
|
||||
private void setupTestbedModel(Model model, String id, String selectedApiServiceName, boolean idExists) {
|
||||
List<ApiServiceDTO> apiServiceDtoList = apiServiceService.searchApiGroupsForLnb();
|
||||
|
||||
if (apiServiceDtoList == null) {
|
||||
apiServiceDtoList = new ArrayList<>();
|
||||
}
|
||||
|
||||
// OAuth API를 첫 번째 항목으로 추가
|
||||
ApiServiceDTO oauthService = new ApiServiceDTO();
|
||||
oauthService.setId(DEFAULT_TOKEN_API_ID);
|
||||
oauthService.setGroupName(DEFAULT_TOKEN_API_NAME);
|
||||
apiServiceDtoList.add(0, oauthService);
|
||||
|
||||
for (ApiServiceDTO apiServiceDTO : apiServiceDtoList) {
|
||||
apiServiceDTO.setContentIcon(null);
|
||||
apiServiceDTO.setMainIcon(null);
|
||||
apiServiceDTO.setApiGroupApiList(null);
|
||||
}
|
||||
|
||||
// id가 없는 경우 OAuth를 기본값으로 설정
|
||||
if (!idExists) {
|
||||
id = DEFAULT_TOKEN_API_ID;
|
||||
selectedApiServiceName = DEFAULT_TOKEN_API_NAME;
|
||||
idExists = true;
|
||||
}
|
||||
|
||||
model.addAttribute("apiServices", apiServiceDtoList);
|
||||
model.addAttribute("id", idExists ? id : "");
|
||||
model.addAttribute("selectedApiName", selectedApiServiceName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.eactive.apim.portal.apps.apis.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
import com.eactive.apim.portal.common.pagerouter.PageHandler;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.ui.Model;
|
||||
|
||||
@Component("apiHandler")
|
||||
@RequiredArgsConstructor
|
||||
public class ApiHandler implements PageHandler {
|
||||
|
||||
private final ApiServiceService apiServiceService;
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServletRequest httpRequest, Model model) {
|
||||
|
||||
ApiGroupSearch search = new ApiGroupSearch();
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(search);
|
||||
|
||||
for (ApiServiceDTO apiServiceDTO : apiServices) {
|
||||
ApiServiceDTO apiService = apiServiceService.getApiGroupById(apiServiceDTO.getId());
|
||||
apiServiceDTO.getApiGroupApiList().clear();
|
||||
apiServiceDTO.getApiGroupApiList().addAll(apiService.getApiGroupApiList());
|
||||
}
|
||||
|
||||
model.addAttribute("apiServices", apiServices);
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.eactive.apim.portal.apps.apis.controller;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ParsedRequestDTO;
|
||||
import com.eactive.apim.portal.apps.apis.service.SampleCodeGenerator;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Sungpil Hyun
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/sample_code")
|
||||
public class SampleCodeGeneratorController {
|
||||
|
||||
private final SampleCodeGenerator sampleCodeGenerator;
|
||||
|
||||
private static final List<String> allowLang = Arrays.asList("java", "csharp", "python", "ecma5");
|
||||
|
||||
public SampleCodeGeneratorController(SampleCodeGenerator sampleCodeGenerator) {
|
||||
this.sampleCodeGenerator = sampleCodeGenerator;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/{language}", consumes = "application/json", produces = "text/plain")
|
||||
public ResponseEntity<String> createRoute(@PathVariable("language") String language,
|
||||
@RequestBody ParsedRequestDTO parsedRequest) {
|
||||
if (!allowLang.contains(language)) {
|
||||
return ResponseEntity.ok().body("");
|
||||
}
|
||||
|
||||
String code = sampleCodeGenerator.generateCodeSnippet(language, parsedRequest);
|
||||
return ResponseEntity.ok().body(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.eactive.apim.portal.apps.apis.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiSpecService;
|
||||
import java.io.IOException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/apis")
|
||||
@Slf4j
|
||||
public class TestbedSpecController {
|
||||
|
||||
@Autowired
|
||||
ApiSpecService apiSpecService;
|
||||
|
||||
private static final String DEFAULT_TOKEN_API_ID = "default-token-api-spec";
|
||||
private static final String DEFAULT_SPEC_PATH = "swagger/default-token-api-spec.json";
|
||||
|
||||
@GetMapping(value = "/{id}/swagger.json", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> getSwagger(@PathVariable String id) {
|
||||
|
||||
if (DEFAULT_TOKEN_API_ID.equals(id)) {
|
||||
try {
|
||||
Resource resource = new ClassPathResource(DEFAULT_SPEC_PATH);
|
||||
String content = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
|
||||
return ResponseEntity.ok().body(content);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to read default token api spec file", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
String testbedSpec = apiSpecService.generateApiServiceTestbedSpec(id);
|
||||
|
||||
if (testbedSpec.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} else {
|
||||
return ResponseEntity.ok().body(testbedSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ApiSearchData {
|
||||
|
||||
private List<ApiServiceDTO> allServices;
|
||||
|
||||
private Map<String, ApiSpecInfoDto> apiSpecsById;
|
||||
|
||||
private Map<String, ApiServiceDTO> servicesByApiId;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiSpecInfoDto {
|
||||
|
||||
private String apiId;
|
||||
|
||||
private String apiName;
|
||||
|
||||
private String apiSimpleDescription;
|
||||
|
||||
private String apiUrl;
|
||||
|
||||
private String apiMethod;
|
||||
|
||||
private String apiContentType;
|
||||
|
||||
private String apiRequestSpec;
|
||||
|
||||
private String apiResponseSpec;
|
||||
|
||||
private String sampleRequest;
|
||||
|
||||
private String sampleResponse;
|
||||
|
||||
private String responseType; //sample, mock
|
||||
|
||||
private String mockUrl;
|
||||
|
||||
private String fileId;
|
||||
|
||||
private String description;
|
||||
|
||||
private String testbedSpec; //swagger json
|
||||
|
||||
private String mainIcon;
|
||||
|
||||
private String service;
|
||||
|
||||
private String displayYn;
|
||||
|
||||
private String displayOrg;
|
||||
|
||||
private String displayRoleCode;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Param {
|
||||
|
||||
private String key;
|
||||
private Boolean isFile;
|
||||
private String value;
|
||||
|
||||
public Param() {
|
||||
}
|
||||
|
||||
public Param(String key, Boolean isFile, String value) {
|
||||
this.key = key;
|
||||
this.isFile = isFile;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ParsedRequestDTO {
|
||||
|
||||
private String method;
|
||||
private String originalUrl;
|
||||
private String host;
|
||||
private String port;
|
||||
private String protocol;
|
||||
private Map<String, String> headers;
|
||||
private RequestBody body;
|
||||
|
||||
public String getContentType(){
|
||||
if(headers == null || headers.isEmpty()) return "application/json";
|
||||
return headers.get("content-type");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class PortalApiDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name; //gw
|
||||
|
||||
private String method; //gw
|
||||
|
||||
private String uri; //http://로 시작하는 서버 경로 //gw
|
||||
|
||||
private String path; //gateway 에 노출 되는 경로 //gw
|
||||
|
||||
private String apiPath; //backend 경로 //gw
|
||||
|
||||
private String apiSpec; //api_spec_info
|
||||
|
||||
private String description; //api_spec_info
|
||||
|
||||
private String responseType; //api_spec_info
|
||||
|
||||
private String mockUrl; //api_spec_info
|
||||
|
||||
private String sampleResponse; //api_spec_info
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PropertyDTO implements Serializable {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String application;
|
||||
|
||||
private String profile;
|
||||
|
||||
private String label;
|
||||
|
||||
private String propertyKey;
|
||||
|
||||
private String propertyValue;
|
||||
|
||||
private String description;
|
||||
|
||||
private String descriptionEn;
|
||||
|
||||
private Integer orderNo;
|
||||
|
||||
private boolean useYn;
|
||||
|
||||
private Date registeredOn;
|
||||
|
||||
private String registeredBy;
|
||||
|
||||
private Date modifiedOn;
|
||||
|
||||
private String modifiedBy;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.eactive.apim.portal.apps.apis.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RequestBody {
|
||||
private Boolean isFile;
|
||||
private String payload;
|
||||
|
||||
private List<Param> params = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.eactive.apim.portal.apps.apis.filter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class APISender {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(APISender.class);
|
||||
|
||||
public String requestPost(String uri, String requestBody) throws IOException {
|
||||
|
||||
HttpURLConnection connection = getHttpURLConnection(uri, requestBody);
|
||||
|
||||
String response = getResponse(connection);
|
||||
|
||||
connection.disconnect();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private String appendUriAndParams(String uri, Map<String, String[]> params) throws UnsupportedEncodingException {
|
||||
StringBuilder uriBuilder = new StringBuilder(uri);
|
||||
|
||||
if (params != null && !params.isEmpty()) {
|
||||
uriBuilder.append("?");
|
||||
for (Map.Entry<String, String[]> entry : params.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String[] values = entry.getValue();
|
||||
for (String value : values) {
|
||||
uriBuilder.append(URLEncoder.encode(key, "UTF-8"));
|
||||
uriBuilder.append("=");
|
||||
uriBuilder.append(URLEncoder.encode(value, "UTF-8"));
|
||||
uriBuilder.append("&");
|
||||
}
|
||||
}
|
||||
// Remove the last '&'
|
||||
uriBuilder.deleteCharAt(uriBuilder.length() - 1);
|
||||
}
|
||||
return uriBuilder.toString();
|
||||
}
|
||||
|
||||
public String requestGet(String uri, Map<String, String> headers, Map<String, String[]> params) throws IOException {
|
||||
|
||||
URL endpoint = new URL(appendUriAndParams(uri, params));
|
||||
HttpURLConnection connection = (HttpURLConnection) endpoint.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (key != null && value != null) {
|
||||
connection.setRequestProperty(key, value);
|
||||
}
|
||||
}
|
||||
connection.setDoOutput(true);
|
||||
|
||||
String response = getResponse(connection);
|
||||
connection.disconnect();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public String requestPost(String uri, Map<String, String> headers, Map<String, String[]> params, String requestBody) throws IOException {
|
||||
|
||||
URL endpoint = new URL(appendUriAndParams(uri, params));
|
||||
HttpURLConnection connection = (HttpURLConnection) endpoint.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (key != null && value != null) {
|
||||
connection.setRequestProperty(key, value);
|
||||
}
|
||||
}
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
byte[] requestBodyBytes = requestBody.getBytes(StandardCharsets.UTF_8);
|
||||
outputStream.write(requestBodyBytes);
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
String response = getResponse(connection);
|
||||
connection.disconnect();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
private static String getResponse(HttpURLConnection connection) throws IOException {
|
||||
int responseCode = connection.getResponseCode();
|
||||
StringBuilder response = new StringBuilder();
|
||||
|
||||
try (InputStream stream = (responseCode < 400) ? connection.getInputStream() : connection.getErrorStream(); InputStreamReader isr = new InputStreamReader(stream);
|
||||
BufferedReader reader = new BufferedReader(isr)) {
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
return response.toString();
|
||||
}
|
||||
|
||||
private static HttpURLConnection getHttpURLConnection(String uri, String requestBody) throws IOException {
|
||||
URL endpoint = new URL(uri);
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) endpoint.openConnection();
|
||||
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
byte[] requestBodyBytes = requestBody.getBytes(StandardCharsets.UTF_8);
|
||||
outputStream.write(requestBodyBytes);
|
||||
outputStream.flush();
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.eactive.apim.portal.apps.apis.filter;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||
import com.eactive.apim.portal.common.util.ApplicationContextUtil;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@WebFilter(urlPatterns = "/api/call-api")
|
||||
public class ApiTesterFilter implements Filter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiTesterFilter.class);
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
logger.debug("ApiTesterFilter initialized");
|
||||
}
|
||||
|
||||
|
||||
public String parseUri(String url) {
|
||||
if (url == null || url.trim().isEmpty()) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
String path = uri.getPath();
|
||||
|
||||
// If path is null or empty, return root path
|
||||
return (path == null || path.isEmpty()) ? "/" : path;
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
logger.error("Failed to parse URL: {}", url, e);
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
|
||||
if (httpServletRequest.getRequestURI().contains("/api/call-api")) {
|
||||
ApiService apiSpecInfoDtoService = ApplicationContextUtil.getContext().getBean(ApiService.class);
|
||||
String url = httpServletRequest.getHeader("original-url");
|
||||
|
||||
if (url.contains("/api/v1/oauth/token")){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader reader = httpServletRequest.getReader();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
String body = sb.toString();
|
||||
|
||||
// Parse request parameters
|
||||
Map<String, String> params = new HashMap<>();
|
||||
String[] pairs = body.split("&");
|
||||
for (String pair : pairs) {
|
||||
String[] keyValue = pair.split("=");
|
||||
if (keyValue.length == 2) {
|
||||
params.put(keyValue[0], keyValue[1]);
|
||||
}
|
||||
}
|
||||
String scope = params.getOrDefault("scope", "default");
|
||||
|
||||
String token = "{\n" +
|
||||
" \"access_token\": \"kbank_gw_sample_token\",\n" +
|
||||
" \"token_type\": \"bearer\",\n" +
|
||||
" \"expires_in\": 86400,\n" +
|
||||
" \"scope\": \""+scope +"\",\n" +
|
||||
" \"jti\": \""+ UUID.randomUUID().toString()+"\"\n" +
|
||||
"}";
|
||||
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().println(token);
|
||||
|
||||
} else {
|
||||
ApiSpecInfoDto apiSpecInfoDto = apiSpecInfoDtoService.selectDetailByURLAndMethod(parseUri(url), httpServletRequest.getMethod());
|
||||
|
||||
if (apiSpecInfoDto.getResponseType().equals("sample")) {
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().println(apiSpecInfoDto.getSampleResponse());
|
||||
} else {
|
||||
String mockUrl = apiSpecInfoDto.getMockUrl();
|
||||
String method = apiSpecInfoDto.getApiMethod();
|
||||
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String header = headerNames.nextElement();
|
||||
headers.put(header, httpServletRequest.getHeader(header));
|
||||
}
|
||||
Map<String, String[]> paramMap = new HashMap<>();
|
||||
|
||||
String originalUrl = headers.get("original-url");
|
||||
//sprlit original url with ? get the second part then split with & put into paramMap
|
||||
|
||||
String[] originalUrlArr = originalUrl.split("\\?");
|
||||
if (originalUrlArr.length > 1) {
|
||||
String[] paramArr = originalUrlArr[1].split("&");
|
||||
for (String param : paramArr) {
|
||||
String[] paramKeyValue = param.split("=");
|
||||
if (paramKeyValue.length > 1) {
|
||||
paramMap.put(paramKeyValue[0], new String[]{paramKeyValue[1]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headers.remove("original-url");
|
||||
headers.remove("original-api-id");
|
||||
APISender apiSender = ApplicationContextUtil.getContext().getBean(APISender.class);
|
||||
|
||||
String responseStr = "";
|
||||
if (method.equalsIgnoreCase("post")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader reader = httpServletRequest.getReader();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
String body = sb.toString();
|
||||
responseStr = apiSender.requestPost(mockUrl, headers, paramMap, body);
|
||||
|
||||
} else {
|
||||
responseStr = apiSender.requestGet(mockUrl, headers, paramMap);
|
||||
}
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().println(responseStr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eactive.apim.portal.apps.apis.mapper;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import java.util.Optional;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class)
|
||||
public interface ApiSpecMapper {
|
||||
|
||||
ApiSpecInfoDto mapToDto(ApiSpecInfo apiSpecInfo);
|
||||
|
||||
default ApiSpecInfoDto map(Optional<ApiSpecInfo> optionalApiSpecInfo) {
|
||||
return optionalApiSpecInfo.map(this::mapToDto).orElse(new ApiSpecInfoDto());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.eactive.apim.portal.apps.apis.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSearchData;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ApiSearchFacade {
|
||||
|
||||
ApiSearchData loadAllApiData();
|
||||
|
||||
Map<String, Object> searchApis(ApiGroupSearch search);
|
||||
|
||||
List<ApiSpecInfoDto> sortApisByService(List<ApiSpecInfoDto> filteredApis, List<ApiServiceDTO> services);
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package com.eactive.apim.portal.apps.apis.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSearchData;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
import com.eactive.apim.portal.apps.auth.service.ApiPermissionFilter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ApiSearchFacadeImpl implements ApiSearchFacade {
|
||||
|
||||
private final ApiService apiService;
|
||||
private final ApiServiceService apiServiceService;
|
||||
|
||||
public ApiSearchData loadAllApiData() {
|
||||
|
||||
List<ApiServiceDTO> allServices = apiServiceService.searchApiGroups(new ApiGroupSearch());
|
||||
|
||||
|
||||
Set<String> allApiIds = allServices.stream()
|
||||
.filter(service -> service.getApiGroupApiList() != null)
|
||||
.flatMap(service -> service.getApiGroupApiList().stream())
|
||||
.map(ApiServiceApiDTO::getApiId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
||||
Map<String, ApiSpecInfoDto> apiSpecsById = apiService.findApisForApiIds(new ArrayList<>(allApiIds))
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
ApiSpecInfoDto::getApiId,
|
||||
Function.identity(),
|
||||
(existing, replacement) -> existing
|
||||
));
|
||||
|
||||
|
||||
Map<String, ApiServiceDTO> servicesByApiId = new HashMap<>();
|
||||
allServices.forEach(service -> {
|
||||
if (service.getApiGroupApiList() != null) {
|
||||
service.getApiGroupApiList().forEach(api ->
|
||||
servicesByApiId.put(api.getApiId(), service));
|
||||
}
|
||||
});
|
||||
|
||||
return new ApiSearchData(allServices, apiSpecsById, servicesByApiId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> searchApis(ApiGroupSearch search) {
|
||||
|
||||
ApiSearchData apiData = loadAllApiData();
|
||||
String keyword = search.getKeyword();
|
||||
|
||||
|
||||
Set<String> matchingApiIds = new HashSet<>();
|
||||
if (!StringUtils.isEmpty(keyword)) {
|
||||
|
||||
apiData.getAllServices().stream()
|
||||
.filter(service -> isServiceMatchingKeyword(service, keyword))
|
||||
.filter(service -> service.getApiGroupApiList() != null)
|
||||
.flatMap(service -> service.getApiGroupApiList().stream())
|
||||
.map(ApiServiceApiDTO::getApiId)
|
||||
.forEach(matchingApiIds::add);
|
||||
|
||||
|
||||
apiData.getApiSpecsById().values().stream()
|
||||
.filter(api -> isApiMatchingKeyword(api, keyword))
|
||||
.map(ApiSpecInfoDto::getApiId)
|
||||
.forEach(matchingApiIds::add);
|
||||
} else {
|
||||
|
||||
matchingApiIds.addAll(apiData.getApiSpecsById().keySet());
|
||||
}
|
||||
|
||||
|
||||
if (search.getGroupIds() != null && !search.getGroupIds().isEmpty()) {
|
||||
Set<String> selectedGroupIds = new HashSet<>(search.getGroupIds());
|
||||
Set<String> allowedApiIds = apiData.getAllServices().stream()
|
||||
.filter(service -> selectedGroupIds.contains(service.getId()))
|
||||
.filter(service -> service.getApiGroupApiList() != null)
|
||||
.flatMap(service -> service.getApiGroupApiList().stream())
|
||||
.map(ApiServiceApiDTO::getApiId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
matchingApiIds.retainAll(allowedApiIds);
|
||||
}
|
||||
|
||||
|
||||
List<ApiSpecInfoDto> matchingApis = matchingApiIds.stream()
|
||||
.map(apiId -> apiData.getApiSpecsById().get(apiId))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
List<ApiSpecInfoDto> filteredApis = ApiPermissionFilter.filterApisByPermissions(matchingApis);
|
||||
Set<String> finalApiIds = filteredApis.stream()
|
||||
.map(ApiSpecInfoDto::getApiId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
filteredApis.forEach(api -> {
|
||||
api.setMainIcon(apiData.getServicesByApiId().get(api.getApiId()).getMainIcon());
|
||||
});
|
||||
|
||||
List<ApiServiceDTO> servicesToReturn = apiData.getAllServices().stream()
|
||||
.map(service -> {
|
||||
ApiServiceDTO filteredService = new ApiServiceDTO();
|
||||
BeanUtils.copyProperties(service, filteredService);
|
||||
|
||||
if (service.getApiGroupApiList() != null) {
|
||||
List<ApiServiceApiDTO> filteredApiList = service.getApiGroupApiList().stream()
|
||||
.filter(api -> finalApiIds.contains(api.getApiId()))
|
||||
.collect(Collectors.toList());
|
||||
filteredService.setApiGroupApiList(filteredApiList);
|
||||
} else {
|
||||
filteredService.setApiGroupApiList(new ArrayList<>());
|
||||
}
|
||||
|
||||
return filteredService;
|
||||
})
|
||||
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
List<ApiSpecInfoDto> sortedApis = sortApisByService(filteredApis, servicesToReturn);
|
||||
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("services", servicesToReturn);
|
||||
result.put("apis", sortedApis);
|
||||
result.put("selectedApiCount", sortedApis.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private boolean isServiceMatchingKeyword(ApiServiceDTO service, String keyword) {
|
||||
String lowerKeyword = keyword.toLowerCase();
|
||||
return service.getGroupName() != null &&
|
||||
service.getGroupName().toLowerCase().contains(lowerKeyword);
|
||||
}
|
||||
|
||||
private boolean isApiMatchingKeyword(ApiSpecInfoDto api, String keyword) {
|
||||
String lowerKeyword = keyword.toLowerCase();
|
||||
return (api.getApiName() != null &&
|
||||
api.getApiName().toLowerCase().contains(lowerKeyword)) ||
|
||||
(api.getApiSimpleDescription() != null &&
|
||||
api.getApiSimpleDescription().toLowerCase().contains(lowerKeyword));
|
||||
}
|
||||
|
||||
|
||||
public List<ApiSpecInfoDto> sortApisByService(List<ApiSpecInfoDto> filteredApis, List<ApiServiceDTO> services) {
|
||||
// Create a map of apiId to its service and API details for efficient lookup
|
||||
Map<String, SortingMetadata> sortingMetadataMap = new HashMap<>();
|
||||
|
||||
services.forEach(service -> {
|
||||
if (service.getApiGroupApiList() != null) {
|
||||
for (int i = 0; i < service.getApiGroupApiList().size(); i++) {
|
||||
ApiServiceApiDTO api = service.getApiGroupApiList().get(i);
|
||||
sortingMetadataMap.put(api.getApiId(), new SortingMetadata(service.getDisplayOrder() != null ? service.getDisplayOrder() : Integer.MAX_VALUE, i));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort the filtered APIs based on both service and API display orders
|
||||
return filteredApis.stream()
|
||||
.filter(api -> sortingMetadataMap.containsKey(api.getApiId()))
|
||||
.sorted((api1, api2) -> {
|
||||
SortingMetadata metadata1 = sortingMetadataMap.getOrDefault(
|
||||
api1.getApiId(),
|
||||
new SortingMetadata(Integer.MAX_VALUE, Integer.MAX_VALUE)
|
||||
);
|
||||
|
||||
SortingMetadata metadata2 = sortingMetadataMap.getOrDefault(
|
||||
api2.getApiId(),
|
||||
new SortingMetadata(Integer.MAX_VALUE, Integer.MAX_VALUE)
|
||||
);
|
||||
|
||||
// First compare by service display order
|
||||
int serviceOrderComparison = metadata1.getServiceDisplayOrder().compareTo(metadata2.getServiceDisplayOrder());
|
||||
if (serviceOrderComparison != 0) {
|
||||
return serviceOrderComparison;
|
||||
}
|
||||
|
||||
// If service display orders are equal, compare by API display order
|
||||
return metadata1.getApiDisplayOrder().compareTo(metadata2.getApiDisplayOrder());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.eactive.apim.portal.apps.apis.service;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.apim.portal.apispec.service.ApiSpecInfoService;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.mapper.ApiSpecMapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class ApiService {
|
||||
private final ApiSpecMapper apiSpecMapper;
|
||||
private final ApiSpecInfoService apiSpecInfoService;
|
||||
|
||||
private ApiSpecInfoDto processDTO(ApiSpecInfo apiSpecInfo) {
|
||||
return Optional.ofNullable(apiSpecInfo)
|
||||
.map(apiSpecMapper::mapToDto)
|
||||
.map(dto -> {
|
||||
if (dto.getDescription() != null) {
|
||||
dto.setDescription(StringEscapeUtils.unescapeHtml4(dto.getDescription()));
|
||||
}
|
||||
if (dto.getApiRequestSpec() != null) {
|
||||
dto.setApiRequestSpec(StringEscapeUtils.unescapeHtml4(dto.getApiRequestSpec()));
|
||||
}
|
||||
if (dto.getApiResponseSpec() != null) {
|
||||
dto.setApiResponseSpec(StringEscapeUtils.unescapeHtml4(dto.getApiResponseSpec()));
|
||||
}
|
||||
if (dto.getDescription() != null) {
|
||||
dto.setDescription(StringEscapeUtils.unescapeHtml4(dto.getDescription()));
|
||||
}
|
||||
return dto;
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public ApiSpecInfoDto selectDetail(String eaiSvcName) {
|
||||
Optional<ApiSpecInfo> apiSpecInfo = apiSpecInfoService.findById(eaiSvcName);
|
||||
return apiSpecInfo.map(this::processDTO).orElse(null);
|
||||
}
|
||||
|
||||
public ApiSpecInfoDto selectDetailByURLAndMethod(String url, String method) {
|
||||
Optional<ApiSpecInfo> apiSpecInfo = apiSpecInfoService.findByUrlAndMethod(url, method);
|
||||
return apiSpecMapper.map(apiSpecInfo);
|
||||
}
|
||||
|
||||
public List<ApiSpecInfoDto> findApis() {
|
||||
return apiSpecInfoService.findPublicApis().stream()
|
||||
.map(apiSpecMapper::mapToDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<ApiSpecInfoDto> findApisForApiIds(List<String> apiIds) {
|
||||
return apiSpecInfoService.findApiSpecsByApiIds(apiIds).stream()
|
||||
.map(apiSpecMapper::mapToDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<ApiSpecInfoDto> searchApis(String keyword) {
|
||||
return apiSpecInfoService.searchApis(keyword).stream()
|
||||
.map(this::processDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.eactive.apim.portal.apps.apis.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ParsedRequestDTO;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Properties;
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.apache.velocity.Template;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SampleCodeGenerator {
|
||||
|
||||
private static final String TEMPLATE_PATH = "templates/sample_code/";
|
||||
|
||||
@Value("${sample-code-path}")
|
||||
private String sampleCodePath;
|
||||
|
||||
private VelocityEngine velocityEngine;
|
||||
|
||||
public String generateCodeSnippet(String language, ParsedRequestDTO parsedRequest) {
|
||||
|
||||
Template t = velocityEngine.getTemplate( TEMPLATE_PATH + language + ".vm");
|
||||
|
||||
VelocityContext context = new VelocityContext();
|
||||
context.put("request", parsedRequest);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
t.merge(context, writer);
|
||||
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
velocityEngine = new VelocityEngine();
|
||||
Properties props = new Properties();
|
||||
props.setProperty(RuntimeConstants.RESOURCE_LOADERS, "classpath");
|
||||
props.setProperty( "resource.loader.classpath.class", ClasspathResourceLoader.class.getName() );
|
||||
props.setProperty("resource.loader.classpath.path", sampleCodePath);
|
||||
|
||||
props.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.NullLogSystem");
|
||||
velocityEngine.init(props);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.apps.apis.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class SortingMetadata {
|
||||
|
||||
private Integer serviceDisplayOrder;
|
||||
private Integer apiDisplayOrder;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceTabInfo;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/api-services")
|
||||
@RequiredArgsConstructor
|
||||
public class ApiServiceController {
|
||||
|
||||
private final ApiServiceService apiServiceService;
|
||||
|
||||
|
||||
@GetMapping
|
||||
public ModelAndView apiServiceList(@ModelAttribute ApiGroupSearch search) {
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(search);
|
||||
|
||||
ModelAndView mav = new ModelAndView("apps/apiservice/apiServiceList");
|
||||
mav.addObject("apiServices", apiServices);
|
||||
mav.addObject("search", search);
|
||||
List<ApiServiceTabInfo> tabs = Arrays.asList(
|
||||
new ApiServiceTabInfo("전체", true, apiServices.size()),
|
||||
new ApiServiceTabInfo("공통", false, 0),
|
||||
new ApiServiceTabInfo("이체", false, 0),
|
||||
new ApiServiceTabInfo("조회", false, 0),
|
||||
new ApiServiceTabInfo("인증", false, 0),
|
||||
new ApiServiceTabInfo("결제", false, 0),
|
||||
new ApiServiceTabInfo("펌뱅킹", false, 0),
|
||||
new ApiServiceTabInfo("가상계좌", false, 0)
|
||||
);
|
||||
mav.addObject("tabs", tabs);
|
||||
|
||||
return mav;
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{id}")
|
||||
public String apiServiceDetail(@PathVariable String id, Model model) {
|
||||
ApiServiceDTO apiService = apiServiceService.getApiGroupById(id);
|
||||
model.addAttribute("apiService", apiService);
|
||||
|
||||
return "apps/apiservice/apiServiceDetail";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.dto;
|
||||
|
||||
import com.eactive.apim.portal.common.search.BaseSearch;
|
||||
import com.eactive.apim.portal.common.search.ColumnSearchModel;
|
||||
import com.eactive.apim.portal.common.search.SearchCondition;
|
||||
import com.eactive.apim.portal.common.search.SearchModel;
|
||||
import com.eactive.apim.portal.common.search.SpecificationSearchModel;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroup;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiGroupSearch implements BaseSearch<ApiGroup> {
|
||||
|
||||
private String keyword;
|
||||
private List<String> groupIds = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public List<SearchModel> buildSearchCondition() {
|
||||
List<SearchModel> models = new ArrayList<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
// keyword로 groupName과 groupDesc 모두 검색하는 OR 조건
|
||||
models.add(new SpecificationSearchModel<ApiGroup>(
|
||||
(root, query, cb) -> cb.or(
|
||||
cb.like(cb.lower(root.get("groupName")), "%" + keyword.toLowerCase() + "%"),
|
||||
cb.like(cb.lower(root.get("groupDesc")), "%" + keyword.toLowerCase() + "%")
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
if (groupIds != null && !groupIds.isEmpty()) {
|
||||
models.add(new ColumnSearchModel("id", SearchCondition.IN, groupIds));
|
||||
}
|
||||
models.add(new ColumnSearchModel("displayYn", SearchCondition.EQUAL, "1"));
|
||||
|
||||
return models;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiServiceApiDTO {
|
||||
private String apiGroupId;
|
||||
private String bizCode;
|
||||
private String apiId;
|
||||
private String apiDesc;
|
||||
private String apiUrl;
|
||||
private String apiMethod;
|
||||
private String service;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiServiceDTO {
|
||||
private String id;
|
||||
private String groupName;
|
||||
private String groupDesc;
|
||||
private String contentDetail;
|
||||
private String contentIcon;
|
||||
private String contentSubject;
|
||||
private Integer displayOrder;
|
||||
private Integer menuDisplayOrder;
|
||||
private String mainText;
|
||||
private String displayOrg;
|
||||
private String displayRoleCode;
|
||||
private String displayYn;
|
||||
private String mainIcon;
|
||||
private String createdBy;
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdDate;
|
||||
private String lastModifiedBy;
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime lastModifiedDate;
|
||||
private List<ApiServiceApiDTO> apiGroupApiList;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ApiServiceTabInfo {
|
||||
private String name;
|
||||
private boolean active;
|
||||
private int count;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.mapper;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroupApi;
|
||||
import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {})
|
||||
public interface ApiServiceApiMapper extends GenericMapper<ApiServiceApiDTO, ApiGroupApi> {
|
||||
@Mapping(source = "id.apiGroupId", target = "apiGroupId")
|
||||
@Mapping(source = "id.apiId", target = "apiId")
|
||||
ApiServiceApiDTO toVo(ApiGroupApi apiGroupApi);
|
||||
@InheritInverseConfiguration
|
||||
ApiGroupApi toEntity(ApiServiceApiDTO apiServiceApiDTO);
|
||||
|
||||
@Mapping(source = "eaibzwkdstcd", target = "bizCode")
|
||||
@Mapping(source = "eaisvcname", target = "apiId")
|
||||
@Mapping(source = "eaisvcdesc", target = "apiDesc")
|
||||
ApiServiceApiDTO toVo(EAIMessageEntity eaiMessageEntity);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.mapper;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroup;
|
||||
import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = { ApiServiceApiMapper.class })
|
||||
public interface ApiServiceMapper extends GenericMapper<ApiServiceDTO, ApiGroup> {
|
||||
|
||||
@Mapping(source = "apiGroupApiList", target = "apiGroupApiList")
|
||||
ApiServiceDTO toVo(ApiGroup apiGroup);
|
||||
|
||||
@Mapping(source = "eaiMessageEntityList", target = "apiGroupApiList")
|
||||
ApiServiceDTO toVo(ApiGroup apiGroup, List<EAIMessageEntity> eaiMessageEntityList);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
ApiGroup toEntity(ApiServiceDTO apiServiceDTO);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void updateToEntity(ApiServiceDTO apiServiceDTO, @MappingTarget ApiGroup apiGroup);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.mapper;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {})
|
||||
public interface ApiSpecInfoMapper extends GenericMapper<ApiServiceApiDTO, ApiSpecInfo> {
|
||||
|
||||
|
||||
@Mapping(source = "apiName", target = "apiDesc")
|
||||
ApiServiceApiDTO toVo(ApiSpecInfo apiGroupApi);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.service;
|
||||
|
||||
import com.eactive.apim.gateway.data.apiservice.repository.ApiServiceRepository;
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.apim.portal.apispec.service.ApiSpecInfoService;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.mapper.ApiServiceMapper;
|
||||
import com.eactive.apim.portal.apps.apiservice.mapper.ApiSpecInfoMapper;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroup;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroupApi;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service("apiServiceService")
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ApiServiceService {
|
||||
|
||||
private final ApiServiceRepository apiServiceRepository;
|
||||
private final ApiServiceMapper apiServiceMapper;
|
||||
private final ApiSpecInfoService apiSpecInfoService;
|
||||
private final ApiSpecInfoMapper apiSpecInfoMapper;
|
||||
|
||||
public List<ApiServiceDTO> searchApiGroups(ApiGroupSearch search) {
|
||||
Sort sortByDisplayOrder = Sort.by(Sort.Direction.ASC, "displayOrder");
|
||||
List<ApiGroup> apiGroups = apiServiceRepository.findAll(search.buildSpecification(), sortByDisplayOrder);
|
||||
|
||||
List<ApiServiceDTO> apiServices = apiGroups.stream()
|
||||
.map(apiServiceMapper::toVo)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ApiServiceDTO apiServiceDTO : apiServices) {
|
||||
ApiServiceDTO apiService = getApiGroupById(apiServiceDTO.getId());
|
||||
apiServiceDTO.getApiGroupApiList().clear();
|
||||
apiServiceDTO.getApiGroupApiList().addAll(apiService.getApiGroupApiList());
|
||||
}
|
||||
|
||||
return apiServices;
|
||||
}
|
||||
|
||||
public List<ApiServiceDTO> searchApiGroupsForLnb() {
|
||||
ApiGroupSearch search = new ApiGroupSearch();
|
||||
Sort sortByDisplayOrder = Sort.by(Sort.Direction.ASC, "displayOrder");
|
||||
List<ApiGroup> apiGroups = apiServiceRepository.findAll(search.buildSpecification(), sortByDisplayOrder);
|
||||
|
||||
List<ApiServiceDTO> apiServices = apiGroups.stream()
|
||||
.map(apiServiceMapper::toVo)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ApiServiceDTO apiServiceDTO : apiServices) {
|
||||
ApiServiceDTO apiService = getApiGroupById(apiServiceDTO.getId());
|
||||
apiServiceDTO.getApiGroupApiList().clear();
|
||||
apiServiceDTO.getApiGroupApiList().addAll(apiService.getApiGroupApiList());
|
||||
}
|
||||
|
||||
return apiServices;
|
||||
}
|
||||
|
||||
public ApiServiceDTO getApiGroupById(String id) {
|
||||
ApiGroup apiGroup = apiServiceRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("API Group not found with id: " + id));
|
||||
|
||||
ApiServiceDTO apiServiceDTO = apiServiceMapper.toVo(apiGroup);
|
||||
|
||||
if (apiGroup.getApiGroupApiList() != null && !apiGroup.getApiGroupApiList().isEmpty()) {
|
||||
List<String> apiIds = apiGroup.getApiGroupApiList().stream()
|
||||
.sorted(Comparator.comparing(
|
||||
ApiGroupApi::getDisplayOrder,
|
||||
Comparator.nullsLast(Comparator.naturalOrder())
|
||||
))
|
||||
.map(a -> a.getId().getApiId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<ApiSpecInfo> apiSpecInfos = apiSpecInfoService.findApiSpecsByApiIds(apiIds);
|
||||
|
||||
// Create a map of apiId to index for efficient lookup
|
||||
Map<String, Integer> orderMap = IntStream.range(0, apiIds.size())
|
||||
.boxed()
|
||||
.collect(Collectors.toMap(apiIds::get, i -> i));
|
||||
|
||||
List<ApiServiceApiDTO> apiServiceApiDTOS = apiSpecInfos.stream()
|
||||
.sorted(Comparator.comparing(api -> orderMap.get(api.getApiId())))
|
||||
.map(apiSpecInfoMapper::toVo)
|
||||
.collect(Collectors.toList());
|
||||
apiServiceDTO.setApiGroupApiList(apiServiceApiDTOS);
|
||||
}
|
||||
|
||||
return apiServiceDTO;
|
||||
}
|
||||
|
||||
public ApiServiceDTO findApiServiceByApiId(String apiId) {
|
||||
return apiServiceRepository.findAll().stream()
|
||||
.filter(apiGroup -> apiGroup.getApiGroupApiList().stream()
|
||||
.anyMatch(api -> api.getId().getApiId().equals(apiId)))
|
||||
.map(apiServiceMapper::toVo)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
package com.eactive.apim.portal.apps.apiservice.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.swagger.parser.OpenAPIParser;
|
||||
import io.swagger.v3.core.util.Json;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import io.swagger.v3.oas.models.Paths;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import io.swagger.v3.parser.core.models.SwaggerParseResult;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ApiSpecService {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private final ApiServiceService apiServiceService;
|
||||
private final ApiService apiService;
|
||||
|
||||
public String generateApiServiceTestbedSpec(String apiServiceId) {
|
||||
|
||||
ApiServiceDTO apiServiceDTO = apiServiceService.getApiGroupById(apiServiceId);
|
||||
List<ApiServiceApiDTO> apiGroupApiList = apiServiceDTO.getApiGroupApiList();
|
||||
String groupName = apiServiceDTO.getGroupName();
|
||||
|
||||
OpenAPI mergedOpenAPI = new OpenAPI()
|
||||
.info(new Info()
|
||||
.title(groupName + " API Documentation")
|
||||
.version("1.0.0")
|
||||
.description("API documentation for " + groupName))
|
||||
.tags(Collections.singletonList(new Tag().name(groupName)));
|
||||
|
||||
Paths mergedPaths = new Paths();
|
||||
Map<String, Object> mergedComponents = new HashMap<>();
|
||||
|
||||
// 경로 중복 체크를 위한 맵
|
||||
Map<String, String> pathOwners = new HashMap<>();
|
||||
List<String> duplicateWarnings = new ArrayList<>();
|
||||
|
||||
for (ApiServiceApiDTO apiServiceApiDTO : apiGroupApiList) {
|
||||
String apiId = apiServiceApiDTO.getApiId();
|
||||
ApiSpecInfoDto apiSpecInfoDto = apiService.selectDetail(apiId);
|
||||
OpenAPI currentAPI = parseSpec(apiSpecInfoDto.getTestbedSpec());
|
||||
if (currentAPI == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String basePath = getBasePath(currentAPI);
|
||||
checkAndMergePaths(currentAPI, basePath, groupName, apiId, mergedPaths,
|
||||
apiSpecInfoDto, pathOwners, duplicateWarnings);
|
||||
mergeComponents(currentAPI, apiId, apiSpecInfoDto.getApiName(), mergedComponents, objectMapper);
|
||||
}
|
||||
|
||||
// 중복 경로 경고가 있으면 description에 추가
|
||||
if (!duplicateWarnings.isEmpty()) {
|
||||
String currentDesc = mergedOpenAPI.getInfo().getDescription();
|
||||
String warningDesc = "\n\n⚠️ Warning: Duplicate Path Detected\n" +
|
||||
String.join("\n", duplicateWarnings);
|
||||
mergedOpenAPI.getInfo().setDescription(currentDesc + warningDesc);
|
||||
}
|
||||
|
||||
if (!mergedComponents.isEmpty()) {
|
||||
mergedOpenAPI.setComponents(objectMapper.convertValue(mergedComponents, io.swagger.v3.oas.models.Components.class));
|
||||
}
|
||||
mergedOpenAPI.setPaths(mergedPaths);
|
||||
|
||||
try {
|
||||
return Json.mapper().writeValueAsString(mergedOpenAPI);
|
||||
} catch (Exception e) {
|
||||
log.error("Error generating merged API specification", e);
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
private PathItem mergePathItems(PathItem existing, PathItem newItem, String fullPath,
|
||||
String apiName, Map<String, String> pathOwners, List<String> duplicateWarnings) {
|
||||
|
||||
PathItem merged = new PathItem();
|
||||
|
||||
// HTTP 메소드와 getter/setter를 매핑
|
||||
Map<String, Triple<Operation, Operation, Consumer<Operation>>> methodMap = new HashMap<>();
|
||||
methodMap.put("GET", Triple.of(existing.getGet(), newItem.getGet(), merged::setGet));
|
||||
methodMap.put("POST", Triple.of(existing.getPost(), newItem.getPost(), merged::setPost));
|
||||
methodMap.put("PUT", Triple.of(existing.getPut(), newItem.getPut(), merged::setPut));
|
||||
methodMap.put("DELETE", Triple.of(existing.getDelete(), newItem.getDelete(), merged::setDelete));
|
||||
methodMap.put("PATCH", Triple.of(existing.getPatch(), newItem.getPatch(), merged::setPatch));
|
||||
methodMap.put("HEAD", Triple.of(existing.getHead(), newItem.getHead(), merged::setHead));
|
||||
methodMap.put("OPTIONS", Triple.of(existing.getOptions(), newItem.getOptions(), merged::setOptions));
|
||||
methodMap.put("TRACE", Triple.of(existing.getTrace(), newItem.getTrace(), merged::setTrace));
|
||||
|
||||
// 각 메소드에 대해 처리
|
||||
methodMap.forEach((method, triple) -> {
|
||||
Operation existingOp = triple.getLeft();
|
||||
Operation newOp = triple.getMiddle();
|
||||
Consumer<Operation> setter = triple.getRight();
|
||||
|
||||
if (existingOp != null && newOp != null) {
|
||||
checkAndAddOperation(existingOp, newOp, method, setter, fullPath, apiName, pathOwners, duplicateWarnings);
|
||||
} else {
|
||||
setter.accept(existingOp != null ? existingOp : newOp);
|
||||
}
|
||||
});
|
||||
|
||||
// 공통 속성 병합
|
||||
merged.setParameters(mergeParameters(existing.getParameters(), newItem.getParameters()));
|
||||
merged.setServers(mergeServers(existing.getServers(), newItem.getServers()));
|
||||
merged.setDescription(mergeDescriptions(existing.getDescription(), newItem.getDescription()));
|
||||
merged.setSummary(mergeSummaries(existing.getSummary(), newItem.getSummary()));
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private void checkAndMergePaths(OpenAPI currentAPI, String basePath, String groupName,
|
||||
String apiId, Paths mergedPaths, ApiSpecInfoDto apiSpecInfoDto,
|
||||
Map<String, String> pathOwners, List<String> duplicateWarnings) {
|
||||
|
||||
if (currentAPI.getPaths() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentAPI.getPaths().forEach((path, pathItem) -> {
|
||||
updateOperationTags(pathItem, groupName);
|
||||
updateOperationSummary(pathItem, apiSpecInfoDto.getApiName());
|
||||
updateReferences(pathItem, apiId);
|
||||
|
||||
String tmpFullPath = StringUtils.isNotEmpty(basePath)
|
||||
? StringUtils.join(basePath, "/", StringUtils.removeStart(path, "/"))
|
||||
: path;
|
||||
|
||||
final String fullPath = StringUtils.replacePattern(tmpFullPath, "//+", "/");
|
||||
|
||||
// HTTP 메소드별로 중복 체크
|
||||
pathItem.readOperationsMap().forEach((httpMethod, operation) -> {
|
||||
final String pathKey = fullPath + ":" + httpMethod;
|
||||
final String existingOwner = pathOwners.get(pathKey);
|
||||
|
||||
if (existingOwner != null) {
|
||||
String warning = String.format(
|
||||
"- Path '%s' [%s] is duplicated between '%s' and '%s'",
|
||||
fullPath,
|
||||
httpMethod,
|
||||
existingOwner,
|
||||
apiSpecInfoDto.getApiName()
|
||||
);
|
||||
duplicateWarnings.add(warning);
|
||||
log.warn(warning);
|
||||
}
|
||||
pathOwners.put(pathKey, apiSpecInfoDto.getApiName());
|
||||
});
|
||||
|
||||
// 기존 PathItem 가져오기
|
||||
PathItem existingPathItem = mergedPaths.get(fullPath);
|
||||
if (existingPathItem == null) {
|
||||
// 새로운 경로면 그대로 추가
|
||||
mergedPaths.addPathItem(fullPath, pathItem);
|
||||
} else {
|
||||
// 기존 경로가 있으면 HTTP 메소드별로 병합
|
||||
PathItem mergedPathItem = mergePathItems(existingPathItem, pathItem, fullPath,
|
||||
apiSpecInfoDto.getApiName(), pathOwners, duplicateWarnings);
|
||||
mergedPaths.put(fullPath, mergedPathItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkAndAddOperation(Operation existingOp, Operation newOp, String method,
|
||||
Consumer<Operation> setter, String fullPath, String apiName,
|
||||
Map<String, String> pathOwners, List<String> duplicateWarnings) {
|
||||
|
||||
final String pathKey = fullPath + ":" + method;
|
||||
final String existingOwner = pathOwners.get(pathKey);
|
||||
|
||||
if (existingOwner != null) {
|
||||
String warning = String.format(
|
||||
"- Path '%s' [%s] is duplicated between '%s' and '%s'",
|
||||
fullPath,
|
||||
method,
|
||||
existingOwner,
|
||||
apiName
|
||||
);
|
||||
duplicateWarnings.add(warning);
|
||||
log.warn(warning);
|
||||
}
|
||||
|
||||
// 새로운 오퍼레이션으로 설정
|
||||
setter.accept(newOp);
|
||||
pathOwners.put(pathKey, apiName);
|
||||
}
|
||||
|
||||
private List<Parameter> mergeParameters(List<Parameter> existing, List<Parameter> newParams) {
|
||||
if (existing == null || existing.isEmpty()) {
|
||||
return newParams;
|
||||
}
|
||||
if (newParams == null || newParams.isEmpty()) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
List<Parameter> merged = new ArrayList<>(existing);
|
||||
for (Parameter newParam : newParams) {
|
||||
if (!parameterExists(merged, newParam)) {
|
||||
merged.add(newParam);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
private boolean parameterExists(List<Parameter> parameters, Parameter parameter) {
|
||||
return parameters.stream()
|
||||
.anyMatch(p -> Objects.equals(p.getName(), parameter.getName())
|
||||
&& Objects.equals(p.getIn(), parameter.getIn()));
|
||||
}
|
||||
|
||||
private List<Server> mergeServers(List<Server> existing, List<Server> newServers) {
|
||||
if (existing == null || existing.isEmpty()) {
|
||||
return newServers;
|
||||
}
|
||||
if (newServers == null || newServers.isEmpty()) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
Set<String> existingUrls = existing.stream()
|
||||
.map(Server::getUrl)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<Server> merged = new ArrayList<>(existing);
|
||||
newServers.stream()
|
||||
.filter(server -> !existingUrls.contains(server.getUrl()))
|
||||
.forEach(merged::add);
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private String mergeDescriptions(String existing, String newDesc) {
|
||||
if (StringUtils.isEmpty(existing)) {
|
||||
return newDesc;
|
||||
}
|
||||
if (StringUtils.isEmpty(newDesc)) {
|
||||
return existing;
|
||||
}
|
||||
return existing + "\n\n" + newDesc;
|
||||
}
|
||||
|
||||
private String mergeSummaries(String existing, String newSummary) {
|
||||
if (StringUtils.isEmpty(existing)) {
|
||||
return newSummary;
|
||||
}
|
||||
if (StringUtils.isEmpty(newSummary)) {
|
||||
return existing;
|
||||
}
|
||||
return existing + " | " + newSummary;
|
||||
}
|
||||
|
||||
private OpenAPI parseSpec(String spec) {
|
||||
SwaggerParseResult result = new OpenAPIParser().readContents(spec, null, null);
|
||||
if (result == null || result.getOpenAPI() == null) {
|
||||
log.error("Failed to parse API specification");
|
||||
return null;
|
||||
}
|
||||
return result.getOpenAPI();
|
||||
}
|
||||
|
||||
private String getBasePath(OpenAPI api) {
|
||||
if (CollectionUtils.isEmpty(api.getServers())) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
Server server = api.getServers().get(0);
|
||||
return StringUtils.removeEnd(server.getUrl(), "/");
|
||||
}
|
||||
|
||||
private void mergeComponents(OpenAPI currentAPI, String apiId, String apiName, Map<String, Object> mergedComponents, ObjectMapper objectMapper) {
|
||||
if (currentAPI.getComponents() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode componentsNode = objectMapper.valueToTree(currentAPI.getComponents());
|
||||
if (!componentsNode.isObject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
componentsNode.fields().forEachRemaining(entry -> {
|
||||
if (!entry.getValue().isObject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> section = (Map<String, Object>) mergedComponents
|
||||
.computeIfAbsent(entry.getKey(), k -> new HashMap<String, Object>());
|
||||
|
||||
if ("schemas".equals(entry.getKey())) {
|
||||
entry.getValue().fields().forEachRemaining(schemaEntry -> {
|
||||
String originalSchemaName = schemaEntry.getKey();
|
||||
String newSchemaName = apiId + "-" + originalSchemaName;
|
||||
JsonNode schemaValue = schemaEntry.getValue();
|
||||
|
||||
// 스키마 내용 복사 및 참조 업데이트
|
||||
JsonNode updatedSchema = updateReferencesInSchema(schemaValue.deepCopy(), apiId);
|
||||
|
||||
// title 필드 추가하여 화면에 표시될 이름 설정
|
||||
if (updatedSchema.isObject()) {
|
||||
((ObjectNode) updatedSchema).put("title", apiName + "-" + originalSchemaName);
|
||||
}
|
||||
|
||||
section.put(newSchemaName, objectMapper.convertValue(updatedSchema, Object.class));
|
||||
});
|
||||
} else {
|
||||
entry.getValue().fields().forEachRemaining(subEntry ->
|
||||
section.put(apiId + "-" + subEntry.getKey(),
|
||||
objectMapper.convertValue(subEntry.getValue(), Object.class)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JsonNode updateReferencesInSchema(JsonNode node, String apiId) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.isObject()) {
|
||||
ObjectNode objectNode = (ObjectNode) node;
|
||||
Iterator<Entry<String, JsonNode>> fields = objectNode.fields();
|
||||
|
||||
while (fields.hasNext()) {
|
||||
Map.Entry<String, JsonNode> entry = fields.next();
|
||||
String key = entry.getKey();
|
||||
JsonNode value = entry.getValue();
|
||||
|
||||
if ("$ref".equals(key)) {
|
||||
String ref = value.asText();
|
||||
// 내부 참조인 경우에만 apiId 추가
|
||||
if (ref.startsWith("#/")) {
|
||||
String updatedRef = updateSchemaReference(ref, apiId);
|
||||
objectNode.put(key, updatedRef);
|
||||
}
|
||||
} else if (value.isObject() || value.isArray()) {
|
||||
JsonNode updatedValue = updateReferencesInSchema(value, apiId);
|
||||
objectNode.set(key, updatedValue);
|
||||
}
|
||||
}
|
||||
return objectNode;
|
||||
} else if (node.isArray()) {
|
||||
for (int i = 0; i < node.size(); i++) {
|
||||
JsonNode item = node.get(i);
|
||||
if (item.isObject() || item.isArray()) {
|
||||
((com.fasterxml.jackson.databind.node.ArrayNode) node).set(i,
|
||||
updateReferencesInSchema(item, apiId));
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private String updateSchemaReference(String ref, String apiId) {
|
||||
if (StringUtils.isEmpty(ref) || !ref.startsWith("#/")) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
String[] parts = ref.split("/");
|
||||
if (parts.length < 4) {
|
||||
return ref; // 최소 #/components/schemas/name 형식이어야 함
|
||||
}
|
||||
|
||||
// 마지막 부분만 apiId- 접두어 추가
|
||||
String schemaName = parts[parts.length - 1];
|
||||
parts[parts.length - 1] = apiId + "-" + schemaName;
|
||||
|
||||
return String.join("/", parts);
|
||||
}
|
||||
|
||||
|
||||
private void updateOperationReferences(Operation operation, String apiId) {
|
||||
// RequestBody 참조 업데이트
|
||||
if (operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) {
|
||||
operation.getRequestBody().getContent().forEach((mediaType, content) -> {
|
||||
if (content.getSchema() != null) {
|
||||
updateSchemaReferencesRecursively(content.getSchema(), apiId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// original-api-id 확장 필드 추가
|
||||
if (operation.getExtensions() == null) {
|
||||
operation.setExtensions(new HashMap<>());
|
||||
}
|
||||
operation.getExtensions().put("x-original-api-id", apiId);
|
||||
|
||||
// Responses 참조 업데이트
|
||||
if (operation.getResponses() != null) {
|
||||
operation.getResponses().forEach((code, response) -> {
|
||||
if (response.getContent() != null) {
|
||||
response.getContent().forEach((mediaType, content) -> {
|
||||
if (content.getSchema() != null) {
|
||||
updateSchemaReferencesRecursively(content.getSchema(), apiId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Parameters 참조 업데이트
|
||||
if (operation.getParameters() != null) {
|
||||
operation.getParameters().forEach(parameter -> {
|
||||
if (parameter.getSchema() != null) {
|
||||
updateSchemaReferencesRecursively(parameter.getSchema(), apiId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSchemaReferencesRecursively(io.swagger.v3.oas.models.media.Schema<?> schema, String apiId) {
|
||||
if (schema == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (schema.get$ref() != null) {
|
||||
schema.set$ref(updateSchemaReference(schema.get$ref(), apiId));
|
||||
}
|
||||
|
||||
// Handle properties
|
||||
if (schema.getProperties() != null) {
|
||||
schema.getProperties().forEach((propertyName, propertySchema) ->
|
||||
updateSchemaReferencesRecursively((io.swagger.v3.oas.models.media.Schema<?>) propertySchema, apiId));
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (schema.getItems() != null) {
|
||||
updateSchemaReferencesRecursively(schema.getItems(), apiId);
|
||||
}
|
||||
|
||||
// Handle allOf, anyOf, oneOf
|
||||
Stream.of(schema.getAllOf(), schema.getAnyOf(), schema.getOneOf())
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(List::stream)
|
||||
.forEach(s -> updateSchemaReferencesRecursively(s, apiId));
|
||||
|
||||
// Handle additional properties
|
||||
if (schema.getAdditionalProperties() instanceof io.swagger.v3.oas.models.media.Schema) {
|
||||
updateSchemaReferencesRecursively(
|
||||
(io.swagger.v3.oas.models.media.Schema<?>) schema.getAdditionalProperties(),
|
||||
apiId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOperationTags(PathItem pathItem, String groupName) {
|
||||
Stream.of(
|
||||
pathItem.getGet(), pathItem.getPost(),
|
||||
pathItem.getPut(), pathItem.getDelete(),
|
||||
pathItem.getPatch(), pathItem.getHead(),
|
||||
pathItem.getOptions(), pathItem.getTrace()
|
||||
)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(operation -> operation.setTags(Collections.singletonList(groupName)));
|
||||
}
|
||||
|
||||
private void updateReferences(PathItem pathItem, String apiId) {
|
||||
Stream.of(
|
||||
pathItem.getGet(), pathItem.getPost(),
|
||||
pathItem.getPut(), pathItem.getDelete(),
|
||||
pathItem.getPatch(), pathItem.getHead(),
|
||||
pathItem.getOptions(), pathItem.getTrace()
|
||||
)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(operation -> updateOperationReferences(operation, apiId));
|
||||
}
|
||||
|
||||
private void updateOperationSummary(PathItem pathItem, String apiName) {
|
||||
Stream.of(
|
||||
pathItem.getGet(), pathItem.getPost(),
|
||||
pathItem.getPut(), pathItem.getDelete(),
|
||||
pathItem.getPatch(), pathItem.getHead(),
|
||||
pathItem.getOptions(), pathItem.getTrace()
|
||||
)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(operation -> {
|
||||
// 기존 summary가 있으면 API 이름을 앞에 추가
|
||||
String existingSummary = operation.getSummary();
|
||||
if (StringUtils.isNotEmpty(existingSummary)) {
|
||||
operation.setSummary(apiName + " - " + existingSummary);
|
||||
} else {
|
||||
operation.setSummary(apiName);
|
||||
}
|
||||
|
||||
// operationId가 있다면 API 이름을 앞에 추가
|
||||
String existingOperationId = operation.getOperationId();
|
||||
if (StringUtils.isNotEmpty(existingOperationId)) {
|
||||
operation.setOperationId(apiName + "-" + existingOperationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
private static class Triple<L, M, R> {
|
||||
|
||||
private final L left;
|
||||
private final M middle;
|
||||
private final R right;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.eactive.apim.portal.apps.app.controller;
|
||||
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequestType;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSearchData;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiSearchFacade;
|
||||
import com.eactive.apim.portal.apps.app.dto.AppRequestDTO;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientSimpleDTO;
|
||||
import com.eactive.apim.portal.apps.app.mapper.CredentialMapper;
|
||||
import com.eactive.apim.portal.apps.app.service.AppServiceFacade;
|
||||
import com.eactive.apim.portal.common.dto.ResponseDTO;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import com.eactive.apim.portal.common.util.EncryptionUtil;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.ValidationUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/myapikey")
|
||||
@RequiredArgsConstructor
|
||||
@Secured("ROLE_API_KEY_REQUEST")
|
||||
public class ApiKeyRestController {
|
||||
|
||||
private final static String PROD_SERVER = "PROD";
|
||||
private final static String STG_SERVER = "STG";
|
||||
|
||||
private final AppServiceFacade appServiceFacade;
|
||||
private final CredentialMapper credentialMapper;
|
||||
private final EncryptionUtil encryptionUtil;
|
||||
private final ApiSearchFacade apiSearchFacade;
|
||||
|
||||
@PostMapping("/api_key_request")
|
||||
public ResponseDTO apiRequest(@RequestBody AppRequestDTO appRequest) {
|
||||
appRequest.setOrg(SecurityUtil.getUserOrg());
|
||||
if (appRequest.getType() == null) {
|
||||
appRequest.setType(AppRequestType.NEW);
|
||||
}
|
||||
AppRequestDTO appRequestDTO = appServiceFacade.createAppRequest(appRequest);
|
||||
appServiceFacade.beginApproval(appRequestDTO.getApproval().getId());
|
||||
return new ResponseDTO(200, "SUCCESS", appRequestDTO.getType().getDescription() + " 신청이 완료되었습니다.");
|
||||
}
|
||||
|
||||
@PostMapping("/api_key_request_prod")
|
||||
public ResponseDTO requestProdApiKey(@RequestBody AppRequestDTO appRequest) {
|
||||
appRequest.setOrg(SecurityUtil.getUserOrg());
|
||||
|
||||
if (appRequest.getClientId() != null && !appRequest.getClientId().isEmpty()) {
|
||||
try {
|
||||
appRequest.setClientId(encryptionUtil.decrypt(appRequest.getClientId()));
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
return new ResponseDTO(400, "FAILURE", "API 키 정보가 올바르지 않습니다.");
|
||||
}
|
||||
appRequest.setType(AppRequestType.PROD_MODIFY);
|
||||
} else {
|
||||
appRequest.setType(AppRequestType.PROD_NEW);
|
||||
}
|
||||
|
||||
AppRequestDTO appRequestDTO = appServiceFacade.createAppRequest(appRequest);
|
||||
appServiceFacade.beginApproval(appRequestDTO.getApproval().getId());
|
||||
|
||||
return new ResponseDTO(200, "SUCCESS", appRequestDTO.getType().getDescription() + " 신청이 완료되었습니다.");
|
||||
}
|
||||
|
||||
@GetMapping("/dev")
|
||||
public List<ClientSimpleDTO> apiKeyList() {
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
List<ClientDTO> apikeys = appServiceFacade.getApiKeyList(user.getPortalOrg(), STG_SERVER);
|
||||
|
||||
ApiSearchData apiSearchData = apiSearchFacade.loadAllApiData();
|
||||
for (ClientDTO apikey : apikeys) {
|
||||
apikey.getApiList().forEach(api -> {
|
||||
api.setService(apiSearchData.getServicesByApiId().get(api.getApiId()).getGroupName());
|
||||
});
|
||||
}
|
||||
|
||||
return apikeys.stream().map(credentialMapper::mapToClientSimpleDTO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@PostMapping("/prod")
|
||||
public ClientSimpleDTO apiKeyProdList(@RequestBody AppRequestDTO appRequest) {
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
|
||||
try {
|
||||
ClientDTO apiKey = appServiceFacade.getApiKey(user.getPortalOrg().getId(), encryptionUtil.decrypt(appRequest.getClientId()), PROD_SERVER);
|
||||
ApiSearchData apiSearchData = apiSearchFacade.loadAllApiData();
|
||||
apiKey.getApiList().forEach(api -> {
|
||||
api.setService(apiSearchData.getServicesByApiId().get(api.getApiId()).getGroupName());
|
||||
});
|
||||
apiKey.setClientid(encryptionUtil.encrypt(apiKey.getClientid()));
|
||||
return credentialMapper.mapToClientSimpleDTO(apiKey);
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
return new ClientSimpleDTO();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.eactive.apim.portal.apps.app.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
import com.eactive.apim.portal.apps.app.dto.AppRequestDTO;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.apim.portal.apps.app.service.AppServiceFacade;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import com.eactive.apim.portal.common.util.ApiServiceHelper;
|
||||
import com.eactive.apim.portal.common.util.EncryptionUtil;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/myapikey")
|
||||
@RequiredArgsConstructor
|
||||
public class MyAppController {
|
||||
|
||||
private final static String PROD_SERVER = "PROD";
|
||||
private final static String STG_SERVER = "STG";
|
||||
|
||||
public static final String API_KEY_LIST = "apps/mypage/apiKeyList";
|
||||
|
||||
public static final String API_KEY_DETAIL = "apps/mypage/apiKeyDetail";
|
||||
|
||||
public static final String API_KEY_PROD_LIST = "apps/mypage/apiKeyProdList";
|
||||
|
||||
public static final String API_KEY_PROD_DETAIL = "apps/mypage/apiKeyProdDetail";
|
||||
|
||||
public static final String API_KEY_REQUEST_HISTORY = "apps/mypage/apiRequestHistory";
|
||||
|
||||
public static final String API_KEY_REQUEST_DETAIL = "apps/mypage/apiRequestDetail";
|
||||
|
||||
public static final String API_KEY_REQUEST_PROD_HISTORY = "apps/mypage/apiRequestProdHistory";
|
||||
|
||||
public static final String API_KEY_REQUEST_PROD_DETAIL = "apps/mypage/apiRequestProdDetail";
|
||||
|
||||
private final AppServiceFacade appServiceFacade;
|
||||
private final ApiServiceService apiServiceService;
|
||||
private final ApiService apiService;
|
||||
private final EncryptionUtil encryptionUtil;
|
||||
private final ApiServiceHelper apiServiceHelper;
|
||||
|
||||
@GetMapping
|
||||
@Secured("ROLE_APP")
|
||||
public ModelAndView apiKeyList(@PageableDefault Pageable pageable, Model model) {
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
Page<ClientDTO> apikeys = appServiceFacade.getApiKeyPageForServer(pageable, user.getPortalOrg(), STG_SERVER);
|
||||
model.addAttribute("apiKeys", apikeys.getContent());
|
||||
model.addAttribute("page", apikeys);
|
||||
return new ModelAndView(API_KEY_LIST);
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_prod_page")
|
||||
@Secured("ROLE_APP")
|
||||
public ModelAndView apiKeyProdList(@PageableDefault Pageable pageable, Model model) {
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
Page<ClientDTO> apikeys = appServiceFacade.getApiKeyPageForServer(pageable, user.getPortalOrg(), PROD_SERVER);
|
||||
model.addAttribute("apiKeys", apikeys.getContent());
|
||||
model.addAttribute("page", apikeys);
|
||||
return new ModelAndView(API_KEY_PROD_LIST);
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_prod_detail")
|
||||
@Secured("ROLE_APP")
|
||||
public ModelAndView apiKeyProdDetail(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
|
||||
if(id == null) {
|
||||
return new ModelAndView("redirect:/myapikey/api_key_prod_page");
|
||||
}
|
||||
ClientDTO apiKey = null;
|
||||
try {
|
||||
apiKey = appServiceFacade.getApiKey(SecurityUtil.getPortalAuthenticatedUser().getPortalOrg().getId(), encryptionUtil.decrypt(id), PROD_SERVER);
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
return new ModelAndView("/");
|
||||
}
|
||||
|
||||
if (apiKey == null) {
|
||||
return new ModelAndView("/");
|
||||
} else {
|
||||
ApiGroupSearch search = new ApiGroupSearch();
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(search);
|
||||
Map<String, ApiServiceDTO> mainIconsMap = apiServiceHelper.getMainIconsFromServiceDtos(apiServices);
|
||||
|
||||
apiKey.getApiList().forEach(api -> {
|
||||
ApiServiceDTO serviceDTO = mainIconsMap.get(api.getApiId());
|
||||
api.setService(serviceDTO.getGroupName());
|
||||
});
|
||||
|
||||
model.addAttribute("apiKey", apiKey);
|
||||
return new ModelAndView(API_KEY_PROD_DETAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_detail")
|
||||
@Secured("ROLE_APP")
|
||||
public ModelAndView apiKeyDetail(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
|
||||
if(id == null) {
|
||||
return new ModelAndView("redirect:/myapikey");
|
||||
}
|
||||
|
||||
ClientDTO apiKey = appServiceFacade.getApiKey(SecurityUtil.getPortalAuthenticatedUser().getPortalOrg().getId(), id, STG_SERVER);
|
||||
if (apiKey == null) {
|
||||
return new ModelAndView("/");
|
||||
} else {
|
||||
ApiGroupSearch search = new ApiGroupSearch();
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(search);
|
||||
Map<String, ApiServiceDTO> mainIconsMap = apiServiceHelper.getMainIconsFromServiceDtos(apiServices);
|
||||
|
||||
apiKey.getApiList().forEach(api -> {
|
||||
ApiServiceDTO serviceDTO = mainIconsMap.get(api.getApiId());
|
||||
api.setService(serviceDTO.getGroupName());
|
||||
});
|
||||
|
||||
List<AppRequestDTO> prodHistory = appServiceFacade.getProdApiRequestHistory(apiKey.getClientid());
|
||||
model.addAttribute("apiKey", apiKey);
|
||||
model.addAttribute("prodhistory", prodHistory);
|
||||
return new ModelAndView(API_KEY_DETAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/api_key_request/cancel")
|
||||
@Secured("ROLE_API_KEY_REQUEST")
|
||||
public String apiRequestCancel(@RequestParam String id) {
|
||||
appServiceFacade.cancelApiRequest(id, SecurityUtil.getPortalAuthenticatedUser().getPortalOrg());
|
||||
return "redirect:/myapikey/api_key_request/history";
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_request/history")
|
||||
@Secured("ROLE_API_KEY_REQUEST_VIEW")
|
||||
public ModelAndView apiRequestHistory(@PageableDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable, Model model) {
|
||||
Page<AppRequestDTO> apiRequests = appServiceFacade.getApiRequestHistory(pageable, SecurityUtil.getUserOrg());
|
||||
|
||||
for (AppRequestDTO request : apiRequests.getContent()) {
|
||||
|
||||
if (!StringUtils.isEmpty(request.getApiGroupList())) {
|
||||
String[] apiGroupList = request.getApiGroupList().split(",");
|
||||
|
||||
ApiServiceDTO firstService = apiServiceService.getApiGroupById(apiGroupList[0]);
|
||||
|
||||
if (apiGroupList.length == 1) {
|
||||
request.setContents(firstService.getGroupName());
|
||||
} else {
|
||||
request.setContents(firstService.getGroupName() + " 외 " + (apiGroupList.length - 1) + "개의 API 그룹");
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(request.getContents())) {
|
||||
if (!StringUtils.isEmpty(request.getApiList())) {
|
||||
String[] apiList = request.getApiList().split(",");
|
||||
|
||||
ApiSpecInfoDto firstApi = apiService.selectDetail(apiList[0]);
|
||||
|
||||
if (apiList.length == 1) {
|
||||
request.setContents(firstApi.getApiName());
|
||||
} else {
|
||||
request.setContents(firstApi.getApiName() + " 외 " + (apiList.length - 1) + "개의 API");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.addAttribute("requests", apiRequests.getContent());
|
||||
model.addAttribute("page", apiRequests);
|
||||
return new ModelAndView(API_KEY_REQUEST_HISTORY);
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_request/prod_history")
|
||||
@Secured("ROLE_API_KEY_REQUEST_VIEW")
|
||||
public ModelAndView apiRequestProdHistory(@PageableDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable, Model model) {
|
||||
Page<AppRequestDTO> apiRequests = appServiceFacade.getApiRequestProdHistory(pageable, SecurityUtil.getUserOrg());
|
||||
|
||||
for (AppRequestDTO request : apiRequests.getContent()) {
|
||||
|
||||
if (!StringUtils.isEmpty(request.getApiGroupList())) {
|
||||
String[] apiGroupList = request.getApiGroupList().split(",");
|
||||
|
||||
ApiServiceDTO firstService = apiServiceService.getApiGroupById(apiGroupList[0]);
|
||||
|
||||
if (apiGroupList.length == 1) {
|
||||
request.setContents(firstService.getGroupName());
|
||||
} else {
|
||||
request.setContents(firstService.getGroupName() + " 외 " + (apiGroupList.length - 1) + "개의 API 그룹");
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(request.getContents())) {
|
||||
if (!StringUtils.isEmpty(request.getApiList())) {
|
||||
String[] apiList = request.getApiList().split(",");
|
||||
|
||||
ApiSpecInfoDto firstApi = apiService.selectDetail(apiList[0]);
|
||||
|
||||
if (apiList.length == 1) {
|
||||
request.setContents(firstApi.getApiName());
|
||||
} else {
|
||||
request.setContents(firstApi.getApiName() + " 외 " + (apiList.length - 1) + "개의 API");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.addAttribute("requests", apiRequests.getContent());
|
||||
model.addAttribute("page", apiRequests);
|
||||
return new ModelAndView(API_KEY_REQUEST_PROD_HISTORY);
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_request/detail")
|
||||
@Secured("ROLE_API_KEY_REQUEST_VIEW")
|
||||
public ModelAndView apiRequestDetail(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
|
||||
if (id == null) {
|
||||
return new ModelAndView("redirect:/myapikey/api_key_request/history");
|
||||
}
|
||||
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
AppRequestDTO appRequest = appServiceFacade.getApiRequestByIdAndPortalOrg(id, user.getPortalOrg());
|
||||
|
||||
model.addAttribute("appRequest", appRequest);
|
||||
return new ModelAndView(API_KEY_REQUEST_DETAIL);
|
||||
}
|
||||
|
||||
@GetMapping("/api_key_request/prod_detail")
|
||||
@Secured("ROLE_API_KEY_REQUEST_VIEW")
|
||||
public ModelAndView apiRequestProdDetail(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
|
||||
if(id == null) {
|
||||
return new ModelAndView("redirect:/myapikey/api_key_request/prod_history");
|
||||
}
|
||||
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
AppRequestDTO appRequest = appServiceFacade.getApiRequestProdByIdAndPortalOrg(id, user.getPortalOrg());
|
||||
|
||||
|
||||
model.addAttribute("appRequest", appRequest);
|
||||
return new ModelAndView(API_KEY_REQUEST_PROD_DETAIL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.eactive.apim.portal.apps.app.dto;
|
||||
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequestType;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.approval.dto.ApprovalDTO;
|
||||
import com.eactive.apim.portal.apps.user.dto.PortalOrgDTO;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AppRequestDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
private PortalOrgDTO org;
|
||||
|
||||
private ApprovalStatus status; // 승인 대기, 승인 완료, 승인 거부
|
||||
|
||||
private AppRequestType type; //신규, 변경
|
||||
|
||||
private ApprovalDTO approval;
|
||||
|
||||
@NotEmpty
|
||||
private String apiList = ""; //comma separated api id list
|
||||
|
||||
private String apiGroupList = ""; //comma separated api group id list
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String clientName;
|
||||
|
||||
private String refClient;
|
||||
|
||||
private List<ApiServiceDTO> apiServiceDTOList = new ArrayList<>();
|
||||
|
||||
private List<ApiSpecInfoDto> apiSpecList = new ArrayList<>();
|
||||
|
||||
private String contents;
|
||||
|
||||
private String reason;
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private String prevApiList;
|
||||
|
||||
private List<ApiSpecInfoDto> removedApiList = new ArrayList<>();
|
||||
|
||||
public String getPrevApiList() {
|
||||
return prevApiList == null ? "" : prevApiList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.eactive.apim.portal.apps.app.dto;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ClientDTO {
|
||||
|
||||
private String clientid;
|
||||
|
||||
private String accesstokenvalidityseconds;
|
||||
|
||||
private String allowedips;
|
||||
|
||||
private String authorities;
|
||||
|
||||
private String autoapprove;
|
||||
|
||||
private String clientname;
|
||||
|
||||
private String clientsecret;
|
||||
|
||||
private String granttypes;
|
||||
|
||||
private String modifiedby;
|
||||
|
||||
private LocalDateTime modifiedon;
|
||||
|
||||
private String redirecturi;
|
||||
|
||||
private String refreshtokenvalidityseconds;
|
||||
|
||||
private String resourceids;
|
||||
|
||||
private String scope;
|
||||
|
||||
private String securitykey;
|
||||
|
||||
private String orgid;
|
||||
|
||||
private String orgname;
|
||||
|
||||
private String appstatus;
|
||||
|
||||
private List<ApiServiceApiDTO> apiList = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eactive.apim.portal.apps.app.dto;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ClientSimpleDTO {
|
||||
|
||||
private String clientid;
|
||||
|
||||
private String clientname;
|
||||
|
||||
private List<ApiServiceApiDTO> apiList = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.apps.app.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PortalOrgDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String id;
|
||||
private String orgName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.eactive.apim.portal.apps.app.mapper;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequest;
|
||||
import com.eactive.apim.portal.apps.apiservice.mapper.ApiServiceApiMapper;
|
||||
import com.eactive.apim.portal.apps.app.dto.AppRequestDTO;
|
||||
import com.eactive.apim.portal.apps.approval.mapper.ApprovalMapper;
|
||||
import com.eactive.apim.portal.apps.user.mapper.PortalOrgMapper;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {PortalOrgMapper.class, ApiServiceApiMapper.class, ApprovalMapper.class})
|
||||
public interface AppRequestMapper extends GenericMapper<AppRequestDTO, AppRequest> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.eactive.apim.portal.apps.app.mapper;
|
||||
|
||||
import com.eactive.apim.portal.apps.apiservice.mapper.ApiServiceApiMapper;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.eai.data.entity.onl.authserver.ClientEntity;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {ApiServiceApiMapper.class})
|
||||
public interface ClientEntityMapper extends GenericMapper<ClientDTO, ClientEntity> {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.eactive.apim.portal.apps.app.mapper;
|
||||
|
||||
import com.eactive.apim.portal.app.entity.Credential;
|
||||
import com.eactive.apim.portal.apps.apiservice.mapper.ApiSpecInfoMapper;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientSimpleDTO;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {ApiSpecInfoMapper.class})
|
||||
public interface CredentialMapper extends GenericMapper<ClientDTO, Credential> {
|
||||
|
||||
|
||||
ClientSimpleDTO mapToClientSimpleDTO(ClientDTO client);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.eactive.apim.portal.apps.app.mapper;
|
||||
|
||||
import com.eactive.apim.portal.app.entity.ProdClient;
|
||||
import com.eactive.apim.portal.apps.apiservice.mapper.ApiSpecInfoMapper;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {ApiSpecInfoMapper.class})
|
||||
public interface ProdClientMapper extends GenericMapper<ClientDTO, ProdClient> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.eactive.apim.portal.apps.app.service;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.apim.portal.app.entity.Credential;
|
||||
import com.eactive.apim.portal.app.repository.CredentialRepository;
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequest;
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequestType;
|
||||
import com.eactive.apim.portal.apprequest.repository.AppRequestRepository;
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.service.ApiServiceService;
|
||||
import com.eactive.apim.portal.apps.app.dto.AppRequestDTO;
|
||||
import com.eactive.apim.portal.apps.app.dto.ClientDTO;
|
||||
import com.eactive.apim.portal.apps.app.mapper.AppRequestMapper;
|
||||
import com.eactive.apim.portal.apps.app.mapper.CredentialMapper;
|
||||
import com.eactive.apim.portal.apps.approval.service.ApprovalService;
|
||||
import com.eactive.apim.portal.apps.user.dto.PortalOrgDTO;
|
||||
import com.eactive.apim.portal.apps.user.mapper.PortalOrgMapper;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import com.eactive.apim.portal.common.util.ApiServiceHelper;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.transaction.Transactional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class AppServiceFacade {
|
||||
|
||||
private final CredentialRepository credentialRepository;
|
||||
private final AppRequestRepository appRequestRepository;
|
||||
private final ApprovalService approvalService;
|
||||
private final ApiServiceService apiServiceService;
|
||||
private final ApiService apiService;
|
||||
private final CredentialMapper credentialMapper;
|
||||
private final AppRequestMapper appRequestMapper;
|
||||
private final PortalOrgMapper portalOrgMapper;
|
||||
private final ApiServiceHelper apiServiceHelper;
|
||||
|
||||
public Page<ClientDTO> getApiKeyPageForServer(Pageable pageable, PortalOrg portalOrg, String server) {
|
||||
Page<Credential> clients = credentialRepository.findAllByOrgidAndServer(pageable, portalOrg.getId(), server);
|
||||
return clients.map(credentialMapper::toVo);
|
||||
}
|
||||
|
||||
public List<ClientDTO> getApiKeyList(PortalOrg portalOrg, String server) {
|
||||
List<Credential> clients = credentialRepository.findAllByOrgidAndServer(portalOrg.getId(), server);
|
||||
return clients.stream().map(credentialMapper::toVo).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
public ClientDTO getApiKey(String orgid, String clientId, String server) {
|
||||
return credentialRepository.findByClientidAndOrgidAndServer(clientId, orgid, server).map(credentialMapper::toVo).orElse(null);
|
||||
}
|
||||
|
||||
|
||||
public Page<AppRequestDTO> getApiRequestHistory(Pageable pageable, PortalOrgDTO portalOrg) {
|
||||
return appRequestRepository.findAllByOrgAndTypeIsIn(pageable, portalOrgMapper.toEntity(portalOrg), Arrays.asList(AppRequestType.NEW,AppRequestType.MODIFY, AppRequestType.DELETE)).map(appRequestMapper::toVo);
|
||||
}
|
||||
|
||||
public Page<AppRequestDTO> getApiRequestProdHistory(Pageable pageable, PortalOrgDTO portalOrg) {
|
||||
return appRequestRepository.findAllByOrgAndTypeIsIn(pageable, portalOrgMapper.toEntity(portalOrg), Arrays.asList(AppRequestType.PROD_NEW,AppRequestType.PROD_MODIFY, AppRequestType.PROD_DELETE)).map(appRequestMapper::toVo);
|
||||
}
|
||||
|
||||
public AppRequestDTO createAppRequest(AppRequestDTO appRequest) {
|
||||
|
||||
setApiGroupsFromApiList(appRequest);
|
||||
AppRequest request = appRequestRepository.save(appRequestMapper.toEntity(appRequest));
|
||||
|
||||
if (request.getType() != null && (request.getType().equals(AppRequestType.MODIFY) || request.getType().equals(AppRequestType.DELETE) ||
|
||||
request.getType().equals(AppRequestType.PROD_MODIFY) || request.getType().equals(AppRequestType.PROD_DELETE))) {
|
||||
|
||||
Optional<Credential> client = credentialRepository.findById(request.getClientId());
|
||||
|
||||
if (client.isPresent()) {
|
||||
request.setClientName(client.get().getClientname());
|
||||
|
||||
if(client.get().getApiList() != null) {
|
||||
// 기존 API 목록을 저장
|
||||
String existingApiList = client.get().getApiList().stream()
|
||||
.map(ApiSpecInfo::getApiId)
|
||||
.collect(Collectors.joining(","));
|
||||
request.setPrevApiList(existingApiList);
|
||||
}
|
||||
|
||||
if(AppRequestType.DELETE.equals(request.getType()) || AppRequestType.PROD_DELETE.equals(request.getType())) {
|
||||
request.setApiList(request.getPrevApiList());
|
||||
}
|
||||
} else {
|
||||
throw new NotFoundException("Client not found");
|
||||
}
|
||||
}
|
||||
|
||||
request.setCreatedDate(LocalDateTime.now());
|
||||
Approval approval = approvalService.createAppApproval(request);
|
||||
request.setApproval(approval);
|
||||
request = appRequestRepository.save(request);
|
||||
return appRequestMapper.toVo(request);
|
||||
}
|
||||
|
||||
private void setApiGroupsFromApiList(AppRequestDTO appRequest) {
|
||||
if (StringUtils.isEmpty(appRequest.getApiList())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> uniqueGroupIds = Arrays.stream(appRequest.getApiList().split(","))
|
||||
.map(apiId -> apiServiceService.findApiServiceByApiId(apiId))
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(apiService -> apiService.getApiGroupApiList().stream())
|
||||
.map(ApiServiceApiDTO::getApiGroupId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
appRequest.setApiGroupList(String.join(",", uniqueGroupIds));
|
||||
}
|
||||
|
||||
|
||||
public void beginApproval(String approvalId) {
|
||||
approvalService.beginApproval(approvalId);
|
||||
}
|
||||
|
||||
public void cancelApiRequest(String id, PortalOrg portalOrg) {
|
||||
appRequestRepository.findByIdAndOrg(id, portalOrg).ifPresent(approvalService::cancelAppApproval);
|
||||
}
|
||||
|
||||
public List<AppRequestDTO> getProdApiRequestHistory(String clientid) {
|
||||
return appRequestRepository.findAllByClientIdsContainsAndTypeIsIn(clientid,Arrays.asList(AppRequestType.PROD_NEW,AppRequestType.PROD_MODIFY, AppRequestType.PROD_DELETE)).stream().map(appRequestMapper::toVo).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public AppRequestDTO getApiRequestProdByIdAndPortalOrg(String id, PortalOrg portalOrg) {
|
||||
AppRequestDTO appRequest = appRequestRepository.findByIdAndOrgAndTypeIsIn(id, portalOrg,
|
||||
Arrays.asList(AppRequestType.PROD_NEW,AppRequestType.PROD_MODIFY, AppRequestType.PROD_DELETE)).map(this::convertToAppRequestDTO).orElse(null);
|
||||
|
||||
if(appRequest != null) {
|
||||
processApiGroups(appRequest);
|
||||
processApiList(appRequest);
|
||||
}
|
||||
return appRequest;
|
||||
}
|
||||
|
||||
public AppRequestDTO getApiRequestByIdAndPortalOrg(String id, PortalOrg org) {
|
||||
AppRequestDTO appRequest = appRequestRepository.findByIdAndOrg(id, org).map(this::convertToAppRequestDTO).orElse(null);
|
||||
|
||||
if(appRequest != null) {
|
||||
processApiGroups(appRequest);
|
||||
processApiList(appRequest);
|
||||
}
|
||||
return appRequest;
|
||||
}
|
||||
|
||||
private AppRequestDTO convertToAppRequestDTO(AppRequest entity) {
|
||||
AppRequestDTO dto = appRequestMapper.toVo(entity);
|
||||
// prevApiList null 처리
|
||||
if (dto.getPrevApiList() == null) {
|
||||
dto.setPrevApiList("");
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
private void processApiGroups(AppRequestDTO appRequest) {
|
||||
|
||||
if (!StringUtils.isEmpty(appRequest.getApiGroupList())) {
|
||||
String[] apiGroupList = appRequest.getApiGroupList().split(",");
|
||||
for (String apiGroupId : apiGroupList) {
|
||||
appRequest.getApiServiceDTOList().add(apiServiceService.getApiGroupById(apiGroupId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processApiList(AppRequestDTO appRequest) {
|
||||
|
||||
if (!StringUtils.isEmpty(appRequest.getApiList())) {
|
||||
String[] apiList = appRequest.getApiList().split(",");
|
||||
List<ApiServiceDTO> apiServices = apiServiceService.searchApiGroups(new ApiGroupSearch());
|
||||
Map<String, ApiServiceDTO> mainIconsMap = apiServiceHelper.getMainIconsFromServiceDtos(apiServices);
|
||||
|
||||
for (String apiId : apiList) {
|
||||
ApiServiceDTO serviceDTO = mainIconsMap.get(apiId);
|
||||
ApiSpecInfoDto spec = apiService.selectDetail(apiId);
|
||||
spec.setService(serviceDTO.getGroupName());
|
||||
appRequest.getApiSpecList().add(spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.eactive.apim.portal.apps.approval.dto;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalType;
|
||||
import com.eactive.apim.portal.approval.entity.Approver;
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalState;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
private ApprovalState approvalStatus;
|
||||
|
||||
private ApprovalType approvalType;
|
||||
|
||||
private String targetId;
|
||||
|
||||
private PortalUser requester;
|
||||
|
||||
private List<Approver> approvers = new ArrayList<>();
|
||||
|
||||
private String approvalSubject; //제목
|
||||
|
||||
private String approvalDetail; //승인 요청 내용
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private LocalDateTime approvalDate;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.eactive.apim.portal.apps.approval.dto;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalType;
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalState;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApproverDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
private ApprovalState approvalStatus;
|
||||
|
||||
private ApprovalType approvalType;
|
||||
|
||||
private String targetId;
|
||||
|
||||
private PortalUser requester;
|
||||
|
||||
private List<ApproverDTO> approvers = new ArrayList<>();
|
||||
|
||||
private String approvalSubject; //제목
|
||||
|
||||
private String approvalDetail; //승인 요청 내용
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private LocalDateTime approvalDate;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.apps.approval.mapper;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.apps.approval.dto.ApprovalDTO;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {ApproverMapper.class})
|
||||
public interface ApprovalMapper extends GenericMapper<ApprovalDTO, Approval> {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.eactive.apim.portal.apps.approval.mapper;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approver;
|
||||
import com.eactive.apim.portal.approval.entity.ApproverId;
|
||||
import com.eactive.apim.portal.apps.approval.dto.ApproverDTO;
|
||||
import com.eactive.eai.data.mapper.BaseMapperConfig;
|
||||
import com.eactive.eai.data.mapper.GenericMapper;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(config = BaseMapperConfig.class, uses = {})
|
||||
public interface ApproverMapper extends GenericMapper<ApproverDTO, Approver> {
|
||||
|
||||
@Mapping(target = "id", expression = "java(approverIdToString(approver.getId()))")
|
||||
@Override
|
||||
ApproverDTO toVo(Approver approver);
|
||||
|
||||
@Mapping(target = "id", expression = "java(stringToApproverId(approverDTO.getId()))")
|
||||
@Override
|
||||
Approver toEntity(ApproverDTO approverDTO);
|
||||
|
||||
|
||||
default String approverIdToString(ApproverId approverId) {
|
||||
if (approverId == null) {
|
||||
return null;
|
||||
}
|
||||
return approverId.getOrdinal().toString();
|
||||
}
|
||||
|
||||
default ApproverId stringToApproverId(String id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = id.split(":");
|
||||
if (parts.length != 2) {
|
||||
throw new IllegalArgumentException("Invalid ApproverId string format");
|
||||
}
|
||||
ApproverId approverId = new ApproverId();
|
||||
approverId.setApproval(parts[0]);
|
||||
approverId.setOrdinal(Integer.parseInt(parts[1]));
|
||||
return approverId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.eactive.apim.portal.apps.approval.service;
|
||||
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequest;
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalType;
|
||||
import com.eactive.apim.portal.approval.entity.Approver;
|
||||
import com.eactive.apim.portal.approval.entity.ApproverId;
|
||||
import com.eactive.apim.portal.approval.entity.ApproverStatus;
|
||||
import com.eactive.apim.portal.approval.repository.ApprovalRepository;
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalEvent;
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalStateMachine;
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLine;
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLineUser;
|
||||
import com.eactive.apim.portal.approvalline.repository.PortalApprovalLineRepository;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
import com.eactive.apim.portal.user.entity.UserInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class ApprovalService {
|
||||
|
||||
private final ApprovalRepository approvalRepository;
|
||||
|
||||
private final PortalApprovalLineRepository portalApprovalLineRepository;
|
||||
|
||||
private final ApprovalStateMachine approvalStateMachine;
|
||||
|
||||
public Approval createAppApproval(AppRequest request) {
|
||||
|
||||
Optional<PortalApprovalLine> optLine = portalApprovalLineRepository.findPortalApprovalLineByApprovalType(ApprovalType.APP.name());
|
||||
|
||||
if (optLine.isPresent()) {
|
||||
Approval approval = new Approval();
|
||||
approval.setApprovalType(ApprovalType.APP);
|
||||
approval.setTargetId(request.getId());
|
||||
approval.setRequester(SecurityUtil.getPortalAuthenticatedUser());
|
||||
approval.setApprovalSubject("[" + request.getOrg().getOrgName() + "] " + request.getType().getDescription() + " 승인");
|
||||
|
||||
for (PortalApprovalLineUser user : optLine.get().getPortalApprovalLineUsers()) {
|
||||
this.addApprover(approval, user.getUser(), user.getApprovalOrder());
|
||||
}
|
||||
|
||||
return approvalRepository.save(approval);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Approval createUserApproval(PortalUser newPortalUser) {
|
||||
|
||||
Optional<PortalApprovalLine> optLine = portalApprovalLineRepository.findPortalApprovalLineByApprovalType(ApprovalType.USER.name());
|
||||
|
||||
if (optLine.isPresent()) {
|
||||
Approval approval = new Approval();
|
||||
approval.setApprovalType(ApprovalType.USER);
|
||||
approval.setTargetId(newPortalUser.getId());
|
||||
approval.setRequester(newPortalUser);
|
||||
approval.setApprovalSubject( newPortalUser.getPortalOrg().getOrgName() + " 사용 신청 승인");
|
||||
|
||||
for (PortalApprovalLineUser user : optLine.get().getPortalApprovalLineUsers()) {
|
||||
this.addApprover(approval, user.getUser(), user.getApprovalOrder());
|
||||
}
|
||||
approvalStateMachine.transition(approval, ApprovalEvent.BEGIN, new HashMap<>());
|
||||
return approvalRepository.save(approval);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addApprover(Approval apprval, UserInfo user, int ordinal) {
|
||||
Approver newApprover = new Approver();
|
||||
ApproverId approverId = new ApproverId(apprval.getId(), ordinal);
|
||||
newApprover.setId(approverId);
|
||||
newApprover.setUser(user);
|
||||
newApprover.setApproverStatus(ApproverStatus.PENDING);
|
||||
newApprover.setApproval(apprval);
|
||||
apprval.getApprovers().add(newApprover);
|
||||
}
|
||||
|
||||
public void beginApproval(String approvalId) {
|
||||
Optional<Approval> approval = approvalRepository.findById(approvalId);
|
||||
|
||||
if (approval.isPresent()) {
|
||||
approvalStateMachine.transition(approval.get(), ApprovalEvent.BEGIN, new HashMap<>());
|
||||
approvalRepository.save(approval.get());
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelAppApproval(AppRequest appRequest) {
|
||||
Optional<Approval> optApproval = approvalRepository.findById(appRequest.getApproval().getId());
|
||||
if (optApproval.isPresent()) {
|
||||
approvalStateMachine.transition(optApproval.get(), ApprovalEvent.CANCEL, new HashMap<>());
|
||||
approvalRepository.save(optApproval.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.eactive.apim.portal.apps.auth.exception;
|
||||
|
||||
public class AuthNumberNotFoundException extends RuntimeException {
|
||||
public AuthNumberNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AuthNumberNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import lombok.var;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ApiPermissionFilter {
|
||||
|
||||
public static List<ApiSpecInfoDto> filterApisByPermissions(List<ApiSpecInfoDto> apis) {
|
||||
boolean isAuthenticated = SecurityUtil.isAuthenticated();
|
||||
String roleCode = null;
|
||||
String org = null;
|
||||
|
||||
if (isAuthenticated) {
|
||||
// null 체크 추가
|
||||
var user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
if (user != null && user.getRoleCode() != null) {
|
||||
roleCode = user.getRoleCode().toString();
|
||||
}
|
||||
|
||||
var userOrg = SecurityUtil.getUserOrg();
|
||||
if (userOrg != null) {
|
||||
org = userOrg.getId();
|
||||
}
|
||||
}
|
||||
|
||||
String finalRoleCode = roleCode;
|
||||
String finalOrg = org;
|
||||
|
||||
return apis.stream()
|
||||
.filter(spec -> checkApiPermissions(spec, isAuthenticated, finalOrg, finalRoleCode))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<ApiServiceDTO> filterServicesByPermissions(List<ApiServiceDTO> services) {
|
||||
boolean isAuthenticated = SecurityUtil.isAuthenticated();
|
||||
String roleCode = null;
|
||||
String org = null;
|
||||
|
||||
if (isAuthenticated) {
|
||||
// null 체크 추가
|
||||
var user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
if (user != null && user.getRoleCode() != null) {
|
||||
roleCode = user.getRoleCode().toString();
|
||||
}
|
||||
|
||||
var userOrg = SecurityUtil.getUserOrg();
|
||||
if (userOrg != null) {
|
||||
org = userOrg.getId();
|
||||
}
|
||||
}
|
||||
|
||||
String finalRoleCode = roleCode;
|
||||
String finalOrg = org;
|
||||
|
||||
return services.stream()
|
||||
.filter(service -> checkServicePermissions(service, isAuthenticated, finalOrg, finalRoleCode))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static boolean checkServicePermissions(ApiServiceDTO spec, boolean isAuthenticated,
|
||||
String org, String roleCode) {
|
||||
if (!isAuthenticated) {
|
||||
return (spec.getDisplayOrg() == null || spec.getDisplayOrg().isEmpty()) && (spec.getDisplayRoleCode() == null || spec.getDisplayRoleCode().isEmpty());
|
||||
}
|
||||
|
||||
boolean orgMatch = org != null && (spec.getDisplayOrg() == null ||
|
||||
spec.getDisplayOrg().isEmpty() ||
|
||||
spec.getDisplayOrg().contains(org));
|
||||
boolean roleMatch = roleCode != null && (spec.getDisplayRoleCode() == null ||
|
||||
spec.getDisplayRoleCode().isEmpty() ||
|
||||
spec.getDisplayRoleCode().contains(roleCode));
|
||||
return orgMatch || roleMatch;
|
||||
}
|
||||
|
||||
private static boolean checkApiPermissions(ApiSpecInfoDto spec, boolean isAuthenticated,
|
||||
String org, String roleCode) {
|
||||
if (!isAuthenticated) {
|
||||
return (spec.getDisplayOrg() == null || spec.getDisplayOrg().isEmpty()) && (spec.getDisplayRoleCode() == null || spec.getDisplayRoleCode().isEmpty());
|
||||
}
|
||||
|
||||
boolean orgMatch = org != null && (spec.getDisplayOrg() == null ||
|
||||
spec.getDisplayOrg().isEmpty() ||
|
||||
spec.getDisplayOrg().contains(org));
|
||||
boolean roleMatch = roleCode != null && (spec.getDisplayRoleCode() == null ||
|
||||
spec.getDisplayRoleCode().isEmpty() ||
|
||||
spec.getDisplayRoleCode().contains(roleCode));
|
||||
return orgMatch || roleMatch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Component
|
||||
public class AuthNumberGenerator {
|
||||
private final Environment env;
|
||||
|
||||
@Autowired
|
||||
public AuthNumberGenerator(Environment env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public String generateAuthNumber() {
|
||||
if (isDevProfile()) {
|
||||
return "123456";
|
||||
}
|
||||
SecureRandom rand = new SecureRandom();
|
||||
return String.format("%06d", 100000 + rand.nextInt(900000));
|
||||
}
|
||||
|
||||
private boolean isDevProfile() {
|
||||
return Arrays.asList(env.getActiveProfiles()).contains("dev");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
public interface AuthNumberService {
|
||||
|
||||
void sendRequestAuthNumber(String recipientKey, String msgType);
|
||||
|
||||
boolean verifyAuthNumber(String recipientKey, String authNumber);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.entity.TwoFactorAuth;
|
||||
import com.eactive.apim.portal.portaluser.service.AuthNumberException;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class AuthNumberServiceImpl implements AuthNumberService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthNumberServiceImpl.class);
|
||||
|
||||
private final AuthNumberStorage storage;
|
||||
private final AuthNumberGenerator generator;
|
||||
private final MessageSender messageSender;
|
||||
|
||||
@Value("${auth-ttl:300}")
|
||||
private int authNumberExpirationTime;
|
||||
|
||||
@Value("${auth.resend.limit.seconds:30}")
|
||||
private int resendLimitSeconds;
|
||||
|
||||
@Autowired
|
||||
public AuthNumberServiceImpl(AuthNumberStorage storage,
|
||||
AuthNumberGenerator generator,
|
||||
MessageSender messageSender) {
|
||||
this.storage = storage;
|
||||
this.generator = generator;
|
||||
this.messageSender = messageSender;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(noRollbackFor = AuthNumberException.class)
|
||||
public void sendRequestAuthNumber(String recipientKey, String msgType) {
|
||||
logger.info("Sending auth number to: {} via {}", recipientKey, msgType);
|
||||
|
||||
validateResendTime(recipientKey);
|
||||
|
||||
String authNumber = generator.generateAuthNumber();
|
||||
|
||||
MessageRecipient recipient = createMessageRecipient(recipientKey, msgType);
|
||||
messageSender.sendAuthMessage(recipient, authNumber, msgType);
|
||||
|
||||
storage.saveAuthNumber(recipientKey, authNumber,
|
||||
LocalDateTime.now().plusSeconds(authNumberExpirationTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(noRollbackFor = AuthNumberException.class)
|
||||
public boolean verifyAuthNumber(String recipientKey, String authNumber) {
|
||||
logger.info("Verifying auth number for: {}", recipientKey);
|
||||
|
||||
TwoFactorAuth storedAuth = storage.getAuthNumber(recipientKey)
|
||||
.orElseThrow(() -> new AuthNumberException("인증번호가 존재하지 않습니다. 인증번호를 다시 발송해주세요."));
|
||||
|
||||
if (storedAuth.getExpiresAt().isBefore(LocalDateTime.now())) {
|
||||
storage.deleteAuthNumber(recipientKey);
|
||||
throw new AuthNumberException("입력 시간이 초과되었습니다. 인증번호를 다시 발송해주세요.");
|
||||
}
|
||||
|
||||
if (authNumber.equals(storedAuth.getAuthNumber())) {
|
||||
return true;
|
||||
} else {
|
||||
throw new AuthNumberException("입력된 인증번호가 올바르지 않습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateResendTime(String recipientKey) {
|
||||
storage.getAuthNumber(recipientKey).ifPresent(existingAuth -> {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (existingAuth.getExpiresAt().minusSeconds(authNumberExpirationTime)
|
||||
.plusSeconds(resendLimitSeconds).isAfter(now)) {
|
||||
throw new AuthNumberException("잠시 후에 다시 시도해 주세요.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private MessageRecipient createMessageRecipient(String recipientKey, String msgType) {
|
||||
MessageRecipient recipient = new MessageRecipient();
|
||||
recipient.setUserId(recipientKey);
|
||||
if ("SMS".equalsIgnoreCase(msgType)) {
|
||||
recipient.setPhone(recipientKey);
|
||||
} else if ("EMAIL".equalsIgnoreCase(msgType)) {
|
||||
recipient.setUserId(recipientKey);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported message type: " + msgType);
|
||||
}
|
||||
return recipient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.entity.TwoFactorAuth;
|
||||
import com.eactive.apim.portal.portaluser.repository.TwoFactorAuthRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class AuthNumberStorage {
|
||||
private final TwoFactorAuthRepository twoFactorAuthRepository;
|
||||
|
||||
@Autowired
|
||||
public AuthNumberStorage(TwoFactorAuthRepository twoFactorAuthRepository) {
|
||||
this.twoFactorAuthRepository = twoFactorAuthRepository;
|
||||
}
|
||||
|
||||
public void saveAuthNumber(String recipientKey, String authNumber, LocalDateTime expiresAt) {
|
||||
|
||||
Optional<TwoFactorAuth> exist = twoFactorAuthRepository.findById(recipientKey);
|
||||
if (exist.isPresent()) {
|
||||
TwoFactorAuth old = exist.get();
|
||||
old.setAuthNumber(authNumber);
|
||||
old.setExpiresAt(expiresAt);
|
||||
twoFactorAuthRepository.save(old);
|
||||
} else {
|
||||
TwoFactorAuth auth = new TwoFactorAuth(recipientKey, authNumber, expiresAt);
|
||||
twoFactorAuthRepository.save(auth);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Optional<TwoFactorAuth> getAuthNumber(String recipientKey) {
|
||||
return twoFactorAuthRepository.findById(recipientKey);
|
||||
}
|
||||
|
||||
public void deleteAuthNumber(String recipientKey) {
|
||||
twoFactorAuthRepository.deleteById(recipientKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.event.RequestAuthNumberEvent;
|
||||
import com.eactive.apim.portal.template.service.MessageHandlerService;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Component
|
||||
public class MessageSender {
|
||||
private final MessageHandlerService messageHandlerService;
|
||||
|
||||
@Autowired
|
||||
public MessageSender(MessageHandlerService messageHandlerService) {
|
||||
this.messageHandlerService = messageHandlerService;
|
||||
}
|
||||
|
||||
public void sendAuthMessage(MessageRecipient recipient, String authNumber, String msgType) {
|
||||
if ("sms".equalsIgnoreCase(msgType)) {
|
||||
recipient.setPhone(recipient.getUserId());
|
||||
} else if ("email".equalsIgnoreCase(msgType)) {
|
||||
recipient.setUserId(recipient.getUserId());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported message type: " + msgType);
|
||||
}
|
||||
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("authNumber", authNumber);
|
||||
|
||||
messageHandlerService.publishEvent(RequestAuthNumberEvent.KEY, recipient, params);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.entity.PasswordResetToken;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PasswordResetTokenRepository extends JpaRepository<PasswordResetToken, String> {
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.entity.PasswordResetToken;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class PasswordResetTokenService {
|
||||
@Autowired
|
||||
private PasswordTokenRepository passwordTokenRepository;
|
||||
|
||||
public PasswordResetToken createToken(String email) {
|
||||
String token = UUID.randomUUID().toString();
|
||||
LocalDateTime expiresAt = LocalDateTime.now().plusHours(24);
|
||||
PasswordResetToken tokenObj = new PasswordResetToken(token, email, expiresAt);
|
||||
return passwordTokenRepository.save(tokenObj);
|
||||
}
|
||||
|
||||
public PasswordResetToken findByToken(String token) {
|
||||
return passwordTokenRepository.findById(token).orElse(null);
|
||||
}
|
||||
|
||||
public void deleteToken(PasswordResetToken token) {
|
||||
passwordTokenRepository.delete(token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.apps.auth.service;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.entity.PasswordResetToken;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PasswordTokenRepository extends JpaRepository<PasswordResetToken, String> {
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqDTO;
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqSearch;
|
||||
import com.eactive.apim.portal.apps.community.faq.service.FaqFacade;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
@Controller
|
||||
public class FaqController {
|
||||
|
||||
public static final String FAQ_QUESTION = "faqQuestion";
|
||||
|
||||
private final FaqFacade faqFacade;
|
||||
|
||||
@Autowired
|
||||
public FaqController(FaqFacade faqFacade){
|
||||
this.faqFacade = faqFacade;
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/faq_list")
|
||||
public String postSearchView(@ModelAttribute("search") FaqSearch search, Model model,
|
||||
@PageableDefault(sort = FAQ_QUESTION, direction = Sort.Direction.ASC) Pageable pageable) {
|
||||
return searchView(search, pageable, model);
|
||||
}
|
||||
|
||||
@GetMapping("/faq_list")
|
||||
public String getSearchView(@PageableDefault(sort = FAQ_QUESTION,
|
||||
direction = Sort.Direction.ASC) Pageable pageable, Model model) {
|
||||
return searchView(new FaqSearch(), pageable, model);
|
||||
}
|
||||
|
||||
private String searchView(FaqSearch search, Pageable pageable, Model model) {
|
||||
Page<FaqDTO> page = faqFacade.getFaqs(search, pageable);
|
||||
|
||||
model.addAttribute("search", search);
|
||||
model.addAttribute("page", page);
|
||||
|
||||
return "apps/community/mainFaqList";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class FaqDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 답변내용
|
||||
*/
|
||||
@NotEmpty(message = "{field.required}")
|
||||
private String faqAnswer;
|
||||
|
||||
/**
|
||||
* 질문내용
|
||||
*/
|
||||
@NotEmpty(message = "{field.required}")
|
||||
private String faqQuestion;
|
||||
|
||||
/**
|
||||
* 사용여부
|
||||
*/
|
||||
private String useYn;
|
||||
|
||||
/**
|
||||
* 첨부파일ID
|
||||
*/
|
||||
private String fileId;
|
||||
|
||||
|
||||
private String createdBy;
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private String lastModifiedBy;
|
||||
|
||||
private LocalDateTime lastModifiedDate;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.dto;
|
||||
|
||||
import com.eactive.apim.portal.common.search.*;
|
||||
import com.eactive.apim.portal.portalfaq.entity.PortalFaq;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FaqSearch implements BaseSearch<PortalFaq> {
|
||||
|
||||
private String searchWrd;
|
||||
|
||||
public String getSearchWrd() {
|
||||
return searchWrd;
|
||||
}
|
||||
|
||||
public void setSearchWrd(String searchWrd) {
|
||||
this.searchWrd = searchWrd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchModel> buildSearchCondition() {
|
||||
List<SearchModel> models = new ArrayList<>();
|
||||
|
||||
if(StringUtils.isNotEmpty(searchWrd)) {
|
||||
models.add(new SpecificationSearchModel((root, query, criteriaBuilder) ->
|
||||
criteriaBuilder.or(
|
||||
criteriaBuilder.like(root.get("faqQuestion"), "%" + searchWrd + "%"),
|
||||
criteriaBuilder.like(root.get("faqAnswer"), "%" + searchWrd + "%")
|
||||
)
|
||||
));
|
||||
}
|
||||
return models;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.mapper;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqDTO;
|
||||
import com.eactive.apim.portal.common.mapper.CommonMapper;
|
||||
import com.eactive.apim.portal.portalfaq.entity.PortalFaq;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Mapper(componentModel = "spring",
|
||||
uses = {CommonMapper.class},
|
||||
unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
@Component
|
||||
|
||||
public interface FaqMapper {
|
||||
|
||||
PortalFaq map(FaqDTO dto);
|
||||
FaqDTO map(PortalFaq entity);
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.repository;
|
||||
|
||||
import com.eactive.apim.portal.portalfaq.entity.PortalFaq;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@EMSDataSource
|
||||
public interface FaqRepository extends JpaRepository<PortalFaq, String>, JpaSpecificationExecutor<PortalFaq> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqDTO;
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqSearch;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
public interface FaqFacade {
|
||||
Page<FaqDTO> getFaqs(FaqSearch search, Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqDTO;
|
||||
import com.eactive.apim.portal.apps.community.faq.dto.FaqSearch;
|
||||
import com.eactive.apim.portal.apps.community.faq.mapper.FaqMapper;
|
||||
import com.eactive.apim.portal.portalfaq.entity.PortalFaq;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FaqFacadeImpl implements FaqFacade {
|
||||
|
||||
private final FaqService faqService;
|
||||
private final FaqMapper faqMapper;
|
||||
|
||||
@Autowired
|
||||
public FaqFacadeImpl(FaqService faqService, FaqMapper faqMapper) {
|
||||
this.faqService = faqService;
|
||||
this.faqMapper = faqMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<FaqDTO> getFaqs(FaqSearch search, Pageable pageable) {
|
||||
Specification<PortalFaq> useYnSpec = (root, query, criteriaBuilder) ->
|
||||
criteriaBuilder.equal(root.get("useYn"), "Y");
|
||||
|
||||
Specification<PortalFaq> searchSpec = search.buildSpecification();
|
||||
Specification<PortalFaq> finalSpec = useYnSpec.and(searchSpec);
|
||||
|
||||
Page<PortalFaq> faqPage = faqService.findAll(finalSpec, pageable);
|
||||
return faqPage.map(faqMapper::map);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.eactive.apim.portal.apps.community.faq.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.faq.repository.FaqRepository;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import com.eactive.apim.portal.portalfaq.entity.PortalFaq;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class FaqService {
|
||||
|
||||
private final FaqRepository faqRepository;
|
||||
|
||||
@Autowired
|
||||
public FaqService(FaqRepository faqRepository) {
|
||||
this.faqRepository = faqRepository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<PortalFaq> findAll(Specification<PortalFaq> spec, Pageable pageable) {
|
||||
return faqRepository.findAll(spec, pageable);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PortalFaq findById(String id) {
|
||||
return faqRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("FAQ not found with id: " + id));
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeSearch;
|
||||
import com.eactive.apim.portal.apps.community.notice.service.PortalNoticeFacade;
|
||||
import com.eactive.apim.portal.file.service.FileService;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* 공지사항 컨트롤러
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/portalnotice")
|
||||
public class PortalNoticeController {
|
||||
|
||||
private final PortalNoticeFacade portalNoticeFacade;
|
||||
|
||||
public PortalNoticeController(PortalNoticeFacade portalNoticeFacade) {
|
||||
this.portalNoticeFacade = portalNoticeFacade;
|
||||
}
|
||||
|
||||
@Resource(name = "fileService")
|
||||
private FileService fileService;
|
||||
|
||||
@GetMapping
|
||||
public String listNotices(@PageableDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable,
|
||||
@ModelAttribute("search") PortalNoticeSearch portalNoticeSearch, Model model) {
|
||||
Page<PortalNoticeDTO> portalNotices = portalNoticeFacade.getNotices(portalNoticeSearch, pageable);
|
||||
model.addAttribute("portalNotices", portalNotices.getContent());
|
||||
model.addAttribute("page", portalNotices);
|
||||
return "apps/community/mainNoticeList";
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
public String viewNotice(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
if(id == null) {
|
||||
return "redirect:/portalnotice";
|
||||
}
|
||||
|
||||
PortalNoticeDTO portalNotice = portalNoticeFacade.getNotice(id);
|
||||
model.addAttribute("portalNotice", portalNotice);
|
||||
return "apps/community/mainNoticeDetail";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PortalNoticeDTO {
|
||||
private String id;
|
||||
|
||||
@NotEmpty(message = "제목을 입력해주세요.")
|
||||
@Size(max = 255, message = "제목은 255자를 초과할 수 없습니다.")
|
||||
private String noticeSubject;
|
||||
|
||||
@NotEmpty(message = "내용을 입력해주세요.")
|
||||
@Length(max = 4000, message = "내용은 4000자를 초과할 수 없습니다.")
|
||||
private String noticeDetail;
|
||||
|
||||
private String fileId;
|
||||
|
||||
private Long readCount;
|
||||
|
||||
private boolean useYn;
|
||||
|
||||
private String inquirerName;
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private LocalDateTime lastModifiedDate;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.dto;
|
||||
|
||||
import com.eactive.apim.portal.common.search.BaseSearch;
|
||||
import com.eactive.apim.portal.common.search.ColumnSearchModel;
|
||||
import com.eactive.apim.portal.common.search.SearchCondition;
|
||||
import com.eactive.apim.portal.common.search.SearchModel;
|
||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PortalNoticeSearch implements BaseSearch<PortalNotice> {
|
||||
|
||||
private String searchSubjectOrDetail;
|
||||
|
||||
private String searchUseYn;
|
||||
|
||||
|
||||
@Override
|
||||
public List<SearchModel> buildSearchCondition() {
|
||||
List<SearchModel> models = new ArrayList<>();
|
||||
|
||||
if (StringUtils.isNotEmpty(searchSubjectOrDetail)) {
|
||||
models.add(new ColumnSearchModel("noticeSubject", SearchCondition.LIKE, searchSubjectOrDetail));
|
||||
models.add(new ColumnSearchModel("noticeDetail", SearchCondition.LIKE, searchSubjectOrDetail));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(searchUseYn)) {
|
||||
models.add(new ColumnSearchModel("useYn", SearchCondition.LIKE, searchUseYn));
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.mapper;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeDTO;
|
||||
import com.eactive.apim.portal.common.mapper.CommonMapper;
|
||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Mapper(componentModel = "spring",
|
||||
uses = {CommonMapper.class},
|
||||
unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
@Component
|
||||
public interface PortalNoticeMapper {
|
||||
|
||||
PortalNotice toEntity(PortalNoticeDTO noticeDTO);
|
||||
|
||||
PortalNoticeDTO map(PortalNotice portalNotice);
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.repository;
|
||||
|
||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
@EMSDataSource
|
||||
public interface PortalNoticeRepository extends JpaRepository<PortalNotice, String>, JpaSpecificationExecutor<PortalNotice> {
|
||||
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeSearch;
|
||||
import java.util.List;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
public interface PortalNoticeFacade {
|
||||
List<PortalNoticeDTO> getLatestNotices();
|
||||
Page<PortalNoticeDTO> getNotices(PortalNoticeSearch search, Pageable pageable);
|
||||
PortalNoticeDTO getNotice(String id);
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeSearch;
|
||||
import com.eactive.apim.portal.apps.community.notice.mapper.PortalNoticeMapper;
|
||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service("portalNoticeFacade")
|
||||
@RequiredArgsConstructor
|
||||
public class PortalNoticeFacadeImpl implements PortalNoticeFacade {
|
||||
|
||||
private final PortalNoticeService portalNoticeService;
|
||||
private final PortalNoticeMapper portalNoticeMapper;
|
||||
|
||||
@Override
|
||||
public List<PortalNoticeDTO> getLatestNotices() {
|
||||
PortalNoticeSearch search = new PortalNoticeSearch();
|
||||
Pageable pageable = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "createdDate"));
|
||||
// Pageable pageable = Pageable.ofSize(3);
|
||||
Page<PortalNoticeDTO> notices = getNotices(search, pageable);
|
||||
return notices.getContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<PortalNoticeDTO> getNotices(PortalNoticeSearch search, Pageable pageable) {
|
||||
/* 사용여부 Y일경우만 조회 */
|
||||
Specification<PortalNotice> spec = Specification.where(search.buildSpecification())
|
||||
.and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("useYn"),"Y"));
|
||||
|
||||
Page<PortalNotice> noticesPage = portalNoticeService.getNotices(spec, pageable);
|
||||
return noticesPage.map(portalNoticeMapper::map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PortalNoticeDTO getNotice(String id) {
|
||||
PortalNotice portalNotice = portalNoticeService.getNotice(id);
|
||||
return portalNoticeMapper.map(portalNotice);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.notice.repository.PortalNoticeRepository;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class PortalNoticeService {
|
||||
|
||||
private final PortalNoticeRepository portalNoticeRepository;
|
||||
|
||||
@Autowired
|
||||
public PortalNoticeService(PortalNoticeRepository portalNoticeRepository) {
|
||||
this.portalNoticeRepository = portalNoticeRepository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<PortalNotice> getNotices(Specification<PortalNotice> spec, Pageable pageable) {
|
||||
return portalNoticeRepository.findAll(spec, pageable);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PortalNotice getNotice(String id) {
|
||||
return portalNoticeRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("PortalNotice not found with id: " + id));
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.dto.PartnershipApplicationDTO;
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.service.PartnershipApplicationFacade;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import java.io.IOException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/partnership")
|
||||
public class PartnershipApplicationController {
|
||||
|
||||
private final PartnershipApplicationFacade partnershipApplicationFacade;
|
||||
|
||||
@Autowired
|
||||
public PartnershipApplicationController(PartnershipApplicationFacade partnershipApplicationFacade){
|
||||
this.partnershipApplicationFacade = partnershipApplicationFacade;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping
|
||||
public String newPartnershipApplicationForm(Model model, HttpServletRequest request) {
|
||||
if (!SecurityUtil.isAuthenticated()) {
|
||||
return "redirect:/login?redirect=/partnership";
|
||||
}
|
||||
|
||||
String referer = request.getHeader("Referer");
|
||||
if(referer != null && !referer.contains("/partnership")) {
|
||||
request.getSession().setAttribute("previousPage", referer);
|
||||
}
|
||||
|
||||
model.addAttribute("partnership", new PartnershipApplicationDTO());
|
||||
return "apps/community/mainPartnershipForm";
|
||||
}
|
||||
|
||||
|
||||
@PostMapping
|
||||
public String createPartnershipApplication(@Valid @ModelAttribute PartnershipApplicationDTO partnershipApplicationDTO,
|
||||
BindingResult bindingResult, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes) throws IOException {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return "apps/community/mainPartnershipForm";
|
||||
}
|
||||
|
||||
partnershipApplicationFacade.createPartnershipApplication(partnershipApplicationDTO);
|
||||
|
||||
// 성공 메시지 추가
|
||||
redirectAttributes.addFlashAttribute("success", "사업제휴신청이 완료되었습니다.");
|
||||
|
||||
httpServletRequest.getSession().removeAttribute("previousPage");
|
||||
return "redirect:/partnership";
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.dto;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PartnershipApplicationDTO {
|
||||
|
||||
@Size(max = 255, message = "제목은 255자를 초과할 수 없습니다.")
|
||||
private String bizSubject;
|
||||
|
||||
private String bizDetail;
|
||||
|
||||
/**
|
||||
* 첨부파일ID
|
||||
*/
|
||||
private String fileId;
|
||||
|
||||
private List<MultipartFile> files;
|
||||
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.mapper;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.dto.PartnershipApplicationDTO;
|
||||
import com.eactive.apim.portal.common.mapper.CommonMapper;
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Mapper(componentModel = "spring",
|
||||
uses = {CommonMapper.class},
|
||||
unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
@Component
|
||||
|
||||
public interface PartnershipApplicationMapper {
|
||||
|
||||
PartnershipApplication toEntity(PartnershipApplicationDTO dto);
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.repository;
|
||||
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@EMSDataSource
|
||||
public interface PartnershipApplicationRepository extends JpaRepository<PartnershipApplication, String>, JpaSpecificationExecutor<PartnershipApplication> {
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.dto.PartnershipApplicationDTO;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface PartnershipApplicationFacade {
|
||||
|
||||
void createPartnershipApplication(PartnershipApplicationDTO partnershipApplicationDTO) throws IOException;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.dto.PartnershipApplicationDTO;
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.mapper.PartnershipApplicationMapper;
|
||||
import com.eactive.apim.portal.file.entity.FileInfo;
|
||||
import com.eactive.apim.portal.file.service.FileService;
|
||||
import com.eactive.apim.portal.file.service.FileTypeContext;
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PartnershipApplicationFacadeImpl implements PartnershipApplicationFacade {
|
||||
|
||||
private final PartnershipApplicationService partnershipApplicationService;
|
||||
private final PartnershipApplicationMapper partnershipApplicationMapper;
|
||||
private final FileService fileService;
|
||||
|
||||
|
||||
@Override
|
||||
public void createPartnershipApplication(PartnershipApplicationDTO partnershipApplicationDTO) throws IOException {
|
||||
|
||||
FileInfo file = null;
|
||||
if (!partnershipApplicationDTO.getFiles().isEmpty()) {
|
||||
FileTypeContext.setFileType("business");
|
||||
file = fileService.createFile(partnershipApplicationDTO.getFiles());
|
||||
}
|
||||
PartnershipApplication partnershipApplication = partnershipApplicationMapper.toEntity(partnershipApplicationDTO);
|
||||
if (file != null) {
|
||||
partnershipApplication.setFileId(file.getFileId());
|
||||
}
|
||||
partnershipApplicationService.createPartnershipApplication(partnershipApplication);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.eactive.apim.portal.apps.community.partnershipapplication.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.partnershipapplication.repository.PartnershipApplicationRepository;
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class PartnershipApplicationService {
|
||||
|
||||
private final PartnershipApplicationRepository partnershipApplicationRepository;
|
||||
|
||||
@Autowired
|
||||
public PartnershipApplicationService(PartnershipApplicationRepository partnershipApplicationRepository) {
|
||||
this.partnershipApplicationRepository = partnershipApplicationRepository;
|
||||
}
|
||||
|
||||
public void createPartnershipApplication(PartnershipApplication partnershipApplication) {
|
||||
partnershipApplicationRepository.save(partnershipApplication);
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquiryDTO;
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquirySearch;
|
||||
import com.eactive.apim.portal.apps.community.qna.service.InquiryFacade;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.eactive.apim.portal.file.exception.InvalidFileException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/inquiry")
|
||||
@Secured("ROLE_INQUIRY")
|
||||
public class InquiryController {
|
||||
|
||||
public static final String APPS_COMMUNITY_MAIN_INQUIRY_FORM = "apps/community/mainInquiryForm";
|
||||
public static final String REDIRECT_INQUIRY = "redirect:/inquiry";
|
||||
private final InquiryFacade inquiryFacade;
|
||||
|
||||
public InquiryController(InquiryFacade inquiryFacade) {
|
||||
this.inquiryFacade = inquiryFacade;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. inquiry 목록 조회 페이지
|
||||
*/
|
||||
@GetMapping
|
||||
public String listInquiries(@PageableDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable, @ModelAttribute("search") InquirySearch search,
|
||||
Model model) {
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
Page<InquiryDTO> inquiries = inquiryFacade.getInquiries(search, pageable, user);
|
||||
model.addAttribute("inquiries", inquiries.getContent());
|
||||
model.addAttribute("page", inquiries);
|
||||
return "apps/community/mainInquiryList";
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. inquiry 상세 조회 페이지
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
public String viewInquiry(@RequestParam(value = "id", required = false) String id, Model model) {
|
||||
|
||||
if(id == null) {
|
||||
return REDIRECT_INQUIRY;
|
||||
}
|
||||
|
||||
InquiryDTO inquiry = inquiryFacade.getMyInquiry(SecurityUtil.getPortalAuthenticatedUser(), id);
|
||||
model.addAttribute("inquiry", inquiry);
|
||||
return "apps/community/mainInquiryDetail";
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. inquiry 등록 페이지
|
||||
*/
|
||||
@GetMapping("/new")
|
||||
public String newInquiryForm(Model model) {
|
||||
model.addAttribute("inquiry", new InquiryDTO());
|
||||
return APPS_COMMUNITY_MAIN_INQUIRY_FORM;
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. inquiry 수정 페이지
|
||||
*/
|
||||
@GetMapping("/edit")
|
||||
public String editInquiryForm(@RequestParam("id") String id, Model model) {
|
||||
InquiryDTO inquiry = inquiryFacade.getMyInquiry(SecurityUtil.getPortalAuthenticatedUser(), id);
|
||||
model.addAttribute("inquiry", inquiry);
|
||||
return APPS_COMMUNITY_MAIN_INQUIRY_FORM;
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. inquiry 등록 요청
|
||||
*/
|
||||
@PostMapping
|
||||
public String createInquiry(@Valid @ModelAttribute InquiryDTO inquiryDTO, BindingResult bindingResult,
|
||||
RedirectAttributes redirectAttributes,
|
||||
Model model) throws IOException {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("inquiry", inquiryDTO);
|
||||
return APPS_COMMUNITY_MAIN_INQUIRY_FORM;
|
||||
}
|
||||
|
||||
inquiryFacade.createInquiry(inquiryDTO);
|
||||
|
||||
redirectAttributes.addFlashAttribute("success", "Q&A 작성이 완료되었습니다.");
|
||||
return REDIRECT_INQUIRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 6. inquiry 수정 요청
|
||||
*/
|
||||
@PostMapping("/edit")
|
||||
public String updateInquiry(@Valid @ModelAttribute InquiryDTO inquiryDTO, BindingResult bindingResult,
|
||||
RedirectAttributes redirectAttributes) throws IOException {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return APPS_COMMUNITY_MAIN_INQUIRY_FORM;
|
||||
}
|
||||
inquiryFacade.updateUserInquiry(SecurityUtil.getPortalAuthenticatedUser(), inquiryDTO.getId(), inquiryDTO);
|
||||
redirectAttributes.addFlashAttribute("success", "Q&A 수정이 완료되었습니다.");
|
||||
return "redirect:/inquiry/detail?id=" + inquiryDTO.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 7. inquiry 삭제 요청
|
||||
*/
|
||||
@PostMapping("/{id}/delete")
|
||||
public String deleteInquiry(@PathVariable String id, RedirectAttributes redirectAttributes) {
|
||||
inquiryFacade.deleteUserInquiry(SecurityUtil.getPortalAuthenticatedUser(), id);
|
||||
// 성공 메시지 추가
|
||||
redirectAttributes.addFlashAttribute("success", "삭제 되었습니다.");
|
||||
return REDIRECT_INQUIRY;
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidFileException.class)
|
||||
public String handleInvalidFileException(InvalidFileException e, Model model) {
|
||||
model.addAttribute("inquiry", new InquiryDTO());
|
||||
model.addAttribute("error", e.getMessage());
|
||||
return "redirect:/inquiry/new";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.dto;
|
||||
|
||||
import com.eactive.apim.portal.apps.user.dto.PortalUserDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InquiryDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
@NotEmpty(message = "제목을 입력해주세요.")
|
||||
@Size(max = 50, message = "제목은 50자를 초과할 수 없습니다.")
|
||||
private String inquirySubject;
|
||||
|
||||
@NotEmpty(message = "내용을 입력해주세요.")
|
||||
@Size(max = 4000, message = "제목은 50자를 초과할 수 없습니다.")
|
||||
private String inquiryDetail;
|
||||
|
||||
@Pattern(regexp = "^(PENDING|IN_PROGRESS|CLOSED)$", message = "유효하지 않은 문의 상태입니다.")
|
||||
private String inquiryStatus;
|
||||
|
||||
private String inquirerName;
|
||||
|
||||
private PortalUserDTO inquirer;
|
||||
|
||||
@Size(max = 4000, message = "응답 내용은 4000자를 초과할 수 없습니다.")
|
||||
private String responseDetail;
|
||||
|
||||
private LocalDateTime responseDate;
|
||||
|
||||
private String responderName;
|
||||
|
||||
private String createdBy;
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private String lastModifiedBy;
|
||||
|
||||
private LocalDateTime lastModifiedDate;
|
||||
|
||||
private List<MultipartFile> files;
|
||||
|
||||
private String attachFile;
|
||||
|
||||
private String maskedInquirerName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.dto;
|
||||
|
||||
import com.eactive.apim.portal.common.search.BaseSearch;
|
||||
import com.eactive.apim.portal.common.search.ColumnSearchModel;
|
||||
import com.eactive.apim.portal.common.search.SearchCondition;
|
||||
import com.eactive.apim.portal.common.search.SearchModel;
|
||||
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 게시물 검색 조건
|
||||
*/
|
||||
public class InquirySearch implements BaseSearch<Inquiry> {
|
||||
|
||||
private String searchCnd;
|
||||
|
||||
private String searchWrd;
|
||||
|
||||
|
||||
@Override
|
||||
public List<SearchModel> buildSearchCondition() {
|
||||
List<SearchModel> models = new ArrayList<>();
|
||||
|
||||
if (StringUtils.isNotEmpty(searchCnd) && StringUtils.isNotEmpty(searchWrd)) {
|
||||
switch (searchCnd) {
|
||||
case "0":
|
||||
models.add(new ColumnSearchModel("inquirySubject", SearchCondition.LIKE, searchWrd));
|
||||
break;
|
||||
case "1":
|
||||
models.add(new ColumnSearchModel("inquiryDetail", SearchCondition.LIKE, searchWrd));
|
||||
break;
|
||||
case "2":
|
||||
models.add(new ColumnSearchModel("inquirer.userName", SearchCondition.LIKE, searchWrd));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
public String getSearchCnd() {
|
||||
return searchCnd;
|
||||
}
|
||||
|
||||
public void setSearchCnd(String searchCnd) {
|
||||
this.searchCnd = searchCnd;
|
||||
}
|
||||
|
||||
public String getSearchWrd() {
|
||||
return searchWrd;
|
||||
}
|
||||
|
||||
public void setSearchWrd(String searchWrd) {
|
||||
this.searchWrd = searchWrd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.mapper;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquiryDTO;
|
||||
import com.eactive.apim.portal.common.mapper.CommonMapper;
|
||||
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Mapper(componentModel = "spring",
|
||||
uses = {CommonMapper.class},
|
||||
unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
@Component
|
||||
public interface InquiryMapper {
|
||||
|
||||
Inquiry toEntity(InquiryDTO inquiry);
|
||||
|
||||
@Mapping(target = "inquirerName", source = "inquirer.userName")
|
||||
InquiryDTO map(Inquiry inquiry);
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.repository;
|
||||
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@EMSDataSource
|
||||
public interface InquiryRepository extends JpaRepository<Inquiry, String>, JpaSpecificationExecutor<Inquiry> {
|
||||
|
||||
Optional<Inquiry> findByInquirerAndId(PortalUser inquirer, String id);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquiryDTO;
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquirySearch;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import java.io.IOException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
public interface InquiryFacade {
|
||||
|
||||
Page<InquiryDTO> getInquiries(InquirySearch search, Pageable pageable, PortalAuthenticatedUser user);
|
||||
|
||||
InquiryDTO getUserInquiry(PortalAuthenticatedUser user, String id);
|
||||
|
||||
InquiryDTO getMyInquiry(PortalAuthenticatedUser user, String id);
|
||||
|
||||
void createInquiry(InquiryDTO inquiryDTO) throws IOException;
|
||||
|
||||
void updateUserInquiry(PortalAuthenticatedUser user, String id, InquiryDTO inquiryDTO) throws IOException;
|
||||
|
||||
void deleteUserInquiry(PortalAuthenticatedUser user, String id);
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquiryDTO;
|
||||
import com.eactive.apim.portal.apps.community.qna.dto.InquirySearch;
|
||||
import com.eactive.apim.portal.apps.community.qna.mapper.InquiryMapper;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
import com.eactive.apim.portal.common.util.StringMaskingUtil;
|
||||
import com.eactive.apim.portal.file.entity.FileInfo;
|
||||
import com.eactive.apim.portal.file.service.FileService;
|
||||
import com.eactive.apim.portal.file.service.FileTypeContext;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums;
|
||||
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InquiryFacadeImpl implements InquiryFacade {
|
||||
|
||||
private final FileService fileService;
|
||||
private final InquiryService inquiryService;
|
||||
private final InquiryMapper inquiryMapper;
|
||||
|
||||
@Override
|
||||
public Page<InquiryDTO> getInquiries(InquirySearch search, Pageable pageable, PortalAuthenticatedUser user) {
|
||||
Specification<Inquiry> spec = Specification.where(search.buildSpecification());
|
||||
|
||||
spec = user.getRoleCode().equals(PortalUserEnums.RoleCode.ROLE_USER)
|
||||
? spec.and((root, query, criteriaBuilder) ->
|
||||
criteriaBuilder.equal(root.get("inquirer").get("id"), user.getId()))
|
||||
: spec.and((root, query, criteriaBuilder) ->
|
||||
criteriaBuilder.equal(root.get("inquirer").get("portalOrg").get("id"), user.getPortalOrg().getId()));
|
||||
|
||||
Page<Inquiry> inquiriesPage = inquiryService.getInquiries(spec, pageable);
|
||||
|
||||
return inquiriesPage.map(inquiry -> {
|
||||
InquiryDTO dto = inquiryMapper.map(inquiry);
|
||||
if (dto != null && dto.getInquirer() != null) {
|
||||
dto.setMaskedInquirerName(StringMaskingUtil.maskName(
|
||||
inquiry.getInquirer().getUserName(),
|
||||
inquiry.getInquirer().getId(),
|
||||
user.getId()
|
||||
));
|
||||
dto.getInquirer().setUserName(null); // 원본 데이터 제거
|
||||
}
|
||||
return dto;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InquiryDTO getUserInquiry(PortalAuthenticatedUser user, String id) {
|
||||
Inquiry inquiry = inquiryService.getInquiry(id);
|
||||
InquiryDTO dto = inquiryMapper.map(inquiry);
|
||||
|
||||
if (dto != null && dto.getInquirer() != null) {
|
||||
dto.setMaskedInquirerName(StringMaskingUtil.maskName(
|
||||
inquiry.getInquirer().getUserName(),
|
||||
inquiry.getInquirer().getId(),
|
||||
user.getId()
|
||||
));
|
||||
dto.getInquirer().setUserName(null); // 원본 데이터 제거
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InquiryDTO getMyInquiry(PortalAuthenticatedUser user, String id) {
|
||||
Inquiry inquiry = inquiryService.getMyInquiry(user, id);
|
||||
return inquiryMapper.map(inquiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createInquiry(InquiryDTO inquiryDTO) throws IOException {
|
||||
|
||||
FileInfo file = null;
|
||||
if (!inquiryDTO.getFiles().isEmpty()) {
|
||||
FileTypeContext.setFileType("qna");
|
||||
file = fileService.createFile(inquiryDTO.getFiles());
|
||||
}
|
||||
|
||||
Inquiry inquiry = inquiryMapper.toEntity(inquiryDTO);
|
||||
if (file != null) {
|
||||
inquiry.setAttachFile(file.getFileId());
|
||||
}
|
||||
inquiry.setInquirer(SecurityUtil.getPortalAuthenticatedUser());
|
||||
inquiryService.createInquiry(inquiry);
|
||||
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("inquiryId", inquiry.getId());
|
||||
params.put("subject", inquiry.getInquirySubject());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserInquiry(PortalAuthenticatedUser user, String id, InquiryDTO inquiryDTO) throws IOException {
|
||||
Inquiry existingInquiry = inquiryService.getMyInquiry(user, id);
|
||||
|
||||
// 파일이 새로 업로드된 경우
|
||||
if (!inquiryDTO.getFiles().isEmpty() && !inquiryDTO.getFiles().get(0).isEmpty()) {
|
||||
FileTypeContext.setFileType("qna");
|
||||
FileInfo file = fileService.createFile(inquiryDTO.getFiles());
|
||||
if(file != null) {
|
||||
inquiryDTO.setAttachFile(file.getFileId());
|
||||
}
|
||||
} else if (existingInquiry.getAttachFile() != null && inquiryDTO.getAttachFile() == null) {
|
||||
fileService.deleteFile(inquiryDTO.getAttachFile());
|
||||
inquiryDTO.setAttachFile(null);
|
||||
}
|
||||
|
||||
Inquiry inquiry = inquiryMapper.toEntity(inquiryDTO);
|
||||
inquiry.setInquirer(existingInquiry.getInquirer());
|
||||
inquiryService.updateInquiry(id, inquiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserInquiry(PortalAuthenticatedUser user, String id) {
|
||||
inquiryService.deleteMyInquiry(user, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.eactive.apim.portal.apps.community.qna.service;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.apps.community.qna.repository.InquiryRepository;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
|
||||
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class InquiryService {
|
||||
|
||||
public static final String PENDING = "PENDING";
|
||||
private final InquiryRepository inquiryRepository;
|
||||
|
||||
public InquiryService(InquiryRepository inquiryRepository) {
|
||||
this.inquiryRepository = inquiryRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Inquiry 조회
|
||||
*
|
||||
* @param id Inquiry의 ID
|
||||
* @return 조회된 Inquiry 객체
|
||||
* @throws NotFoundException Inquiry가 존재하지 않을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Inquiry getInquiry(String id) {
|
||||
return inquiryRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("Inquiry not found with id: " + id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inquiry 목록 조회 (페이징 및 필터링 지원)
|
||||
*
|
||||
* @param spec 필터링 조건
|
||||
* @param pageable 페이징 정보
|
||||
* @return 페이징된 Inquiry 목록
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Page<Inquiry> getInquiries(Specification<Inquiry> spec, Pageable pageable) {
|
||||
return inquiryRepository.findAll(spec, pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. 새로운 Inquiry 등록
|
||||
*
|
||||
* @param inquiry 등록할 Inquiry 객체
|
||||
* @return 등록된 Inquiry 객체
|
||||
*/
|
||||
public Inquiry createInquiry(Inquiry inquiry) {
|
||||
inquiry.setId(null);
|
||||
inquiry.setInquiryStatus(PENDING);
|
||||
return inquiryRepository.save(inquiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. Inquiry 수정
|
||||
*
|
||||
* @param id 수정할 Inquiry의 ID
|
||||
* @param updatedInquiry 수정할 내용이 담긴 Inquiry 객체
|
||||
* @return 수정된 Inquiry 객체
|
||||
* @throws RuntimeException Inquiry가 존재하지 않을 경우
|
||||
*/
|
||||
public Inquiry updateInquiry(String id, Inquiry updatedInquiry) {
|
||||
Inquiry inquiry = getInquiry(id);
|
||||
inquiry.setInquirySubject(updatedInquiry.getInquirySubject());
|
||||
inquiry.setInquiryDetail(updatedInquiry.getInquiryDetail());
|
||||
inquiry.setAttachFile(updatedInquiry.getAttachFile());
|
||||
return inquiryRepository.save(inquiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. Inquiry 삭제
|
||||
*
|
||||
* @param id 삭제할 Inquiry의 ID
|
||||
* @throws NotFoundException Inquiry가 존재하지 않을 경우
|
||||
*/
|
||||
public void deleteMyInquiry(PortalAuthenticatedUser user, String id) {
|
||||
Inquiry myInquiry = inquiryRepository.findByInquirerAndId(user, id).orElseThrow(() -> new NotFoundException("내 질문을 찾을 수 없습니다."));
|
||||
inquiryRepository.delete(myInquiry);
|
||||
}
|
||||
|
||||
public Inquiry getMyInquiry(PortalAuthenticatedUser user, String id) {
|
||||
return inquiryRepository.findByInquirerAndId(user, id).orElseThrow(() -> new NotFoundException("내 질문을 찾을 수 없습니다."));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.eactive.apim.portal.apps.dashboard;
|
||||
|
||||
public class ApiDailyStatistics {
|
||||
private final String apiId;
|
||||
private final String apiName;
|
||||
private final String date;
|
||||
private final Long requestCount;
|
||||
|
||||
public ApiDailyStatistics(String apiId, String apiName, String date, Long requestCount) {
|
||||
this.apiId = apiId;
|
||||
this.apiName = apiName;
|
||||
this.date = date;
|
||||
this.requestCount = requestCount;
|
||||
}
|
||||
|
||||
public String getApiId() { return apiId; }
|
||||
public String getApiName() { return apiName; }
|
||||
public String getDate() { return date; }
|
||||
public Long getRequestCount() { return requestCount; }
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user