Merge branch 'master' of http://192.168.0.25:9001/r/owl-kisa
| | |
| | | .antMatchers("/language/change").permitAll() |
| | | .antMatchers("/security/*").permitAll() |
| | | .antMatchers("/api/issue").permitAll() |
| | | .antMatchers("/api/issue/*").permitAll() |
| | | .antMatchers("/**/*").authenticated(); |
| | | |
| | | // http.addFilter(new CustomAuthenticationFilter()); |
| | |
| | | */ |
| | | public static final String API_ISSUE_STATUS_NOT_EXIST = "API_ISSUE_STATUS_NOT_EXIST"; |
| | | /** |
| | | * 워크플로우에 해당 이슈 상태를 찾을수 없습니다 |
| | | */ |
| | | public static final String API_ISSUE_STATUS_NOT_EXIST_IN_WORKFLOW = "API_ISSUE_STATUS_NOT_EXIST_IN_WORKFLOW"; |
| | | /** |
| | | * 이슈 상태 값이 없습니다. |
| | | */ |
| | | public static final String API_ISSUE_STATUS_IS_NULL = "API_ISSUE_STATUS_IS_NULL"; |
| | |
| | | * 이슈 상태가 워크플로우에 포함되어 있지 않습니다 |
| | | */ |
| | | public static final String API_ISSUE_STATUS_NOT_IN_WORKFLOW = "API_ISSUE_STATUS_NOT_IN_WORKFLOW"; |
| | | /** |
| | | * api 이슈 생성 완료 |
| | | */ |
| | | public static final String API_ADD_ISSUE_OK = "API_ADD_ISSUE_OK"; |
| | | } |
| | |
| | | |
| | | @Query(name="WorkflowTransition.findBySourceIssueStatusIdAndWorkflowId") |
| | | List<WorkflowTransitionVo> findBySourceIssueStatusIdAndWorkflowId(@Param("sourceIssueStatusId") Long sourceIssueStatusId, @Param("workflowId") Long workflowId); |
| | | |
| | | @Query(name="WorkflowTransition.findBySourceIssueStatusIdAndTargetIssueStatusIdAndWorkflowId") |
| | | List<WorkflowTransitionVo> findBySourceIssueStatusIdAndTargetIssueStatusIdAndWorkflowId(@Param("sourceIssueStatusId") Long sourceIssueStatusId, @Param("workflowId") Long workflowId); |
| | | } |
| | |
| | | @Autowired |
| | | private IssueRelationMapper issueRelationMapper; |
| | | |
| | | @Autowired |
| | | private WorkflowTransitionService workflowTransitionService; |
| | | |
| | | @Override |
| | | protected JpaRepository<Issue, Long> getRepository() { |
| | | return this.issueRepository; |
| | |
| | | } |
| | | } else if (issueApiForm.getIssueStatusId() == null){ |
| | | throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_ISSUE_STATUS_NOT_EXIST)); |
| | | } else if (!this.workflowTransitionService.contains(issueApiForm.getIssueStatusId(), workflow.getId())) { |
| | | //이슈 상태 유효성 확인 |
| | | throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_ISSUE_STATUS_NOT_EXIST_IN_WORKFLOW)); |
| | | } |
| | | |
| | | // 프로젝트 입력 |
| | |
| | | // 워크플로우에 사용되는 이슈 상태인지 확인한다 |
| | | @Override |
| | | public boolean contains(Long issueStatusId, Long workflowId) { |
| | | List<WorkflowTransitionVo> workflowTransitionVos = this.findBySourceIssueStatusIdAndWorkflowId(issueStatusId, workflowId); |
| | | List<WorkflowTransitionVo> workflowTransitionVos = workflowTransitionRepository.findBySourceIssueStatusIdAndTargetIssueStatusIdAndWorkflowId(issueStatusId, workflowId); |
| | | return workflowTransitionVos != null && workflowTransitionVos.size() > 0; |
| | | } |
| | | |
| | |
| | | package kr.wisestone.owl.web.controller.Api; |
| | | |
| | | import io.swagger.annotations.ApiImplicitParam; |
| | | import io.swagger.annotations.ApiImplicitParams; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import kr.wisestone.owl.constant.Constants; |
| | | import kr.wisestone.owl.constant.MsgConstants; |
| | | import kr.wisestone.owl.domain.Issue; |
| | | import kr.wisestone.owl.exception.ApiParameterException; |
| | |
| | | */ |
| | | @PostMapping(value = "/issue") |
| | | @ApiOperation(value = "이슈 추가", notes = "새로운 이슈를 추가한다.") |
| | | @ApiImplicitParam(name = "files", dataType = "file") |
| | | @ApiImplicitParams( |
| | | { |
| | | @ApiImplicitParam(name = "files", dataType = "file"), |
| | | @ApiImplicitParam(name = "token", paramType = "form"), |
| | | @ApiImplicitParam(name = "title", paramType = "form"), |
| | | @ApiImplicitParam(name = "description", paramType = "form"), |
| | | @ApiImplicitParam(name = "issueTypeId", paramType = "form"), |
| | | @ApiImplicitParam(name = "customFields", paramType = "form") |
| | | } |
| | | ) |
| | | |
| | | @ResponseStatus(HttpStatus.CREATED) |
| | | public |
| | | @ResponseBody |
| | | void addIssue(ApiIssueAddForm apiIssueAddForm, @RequestParam("files") List<MultipartFile> files) |
| | | Map<String, Object> addIssue(ApiIssueAddForm apiIssueAddForm, @RequestParam("files") List<MultipartFile> files) |
| | | throws OwlRuntimeException, CloneNotSupportedException { |
| | | Map<String, Object> resJsonData = new HashMap<>(); |
| | | |
| | |
| | | |
| | | List<Issue> issues = this.issueService.addApiIssue(issueApiForm); |
| | | // 버전 생성 |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (Issue issue : issues) { |
| | | this.issueService.addIssueVersion(issue.getId(), issue.getRegisterId()); |
| | | if (sb.length() > 0) |
| | | sb.append(","); |
| | | sb.append(issue.getProject().getProjectKey()).append("-").append(issue.getIssueNumber()); |
| | | } |
| | | |
| | | resJsonData.put(Constants.RES_KEY_MSG_SUCCESS, sb.toString()); |
| | | return resJsonData; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @throws OwlRuntimeException 파라미터 오류시 발생 |
| | | * @throws CloneNotSupportedException 이슈 복사 시에 발생 |
| | | */ |
| | | @PutMapping(value = "/issue") |
| | | @PostMapping(value = "/issue/status") |
| | | @ApiOperation(value = "이슈 상태 수정", notes = "사용자 정의 필드가 동일한 기존 이슈를 변경한다.") |
| | | @ResponseStatus(HttpStatus.OK) |
| | | public |
| | | @ResponseBody |
| | | void modifyIssue(ApiIssueModifyForm apiIssueModifyForm) throws OwlRuntimeException, CloneNotSupportedException { |
| | | Map<String, Object> modifyIssue(ApiIssueModifyForm apiIssueModifyForm) throws OwlRuntimeException, CloneNotSupportedException { |
| | | Map<String, Object> resJsonData = new HashMap<>(); |
| | | |
| | | IssueApiForm issueApiForm = ConvertUtil.copyProperties(apiIssueModifyForm, IssueApiForm.class); |
| | | issueApiForm.parseCustomFields(apiIssueModifyForm.getCustomFields()); |
| | | issueApiForm.setApiType(IssueApiForm.ApiType.add); |
| | | issueApiForm.setApiType(IssueApiForm.ApiType.modify); |
| | | // 사용자 정의 필드가 없을 경우 검색을 할 수 없기 때문에 예외처리 |
| | | if (issueApiForm.getCustomFieldValues() == null || issueApiForm.getCustomFieldValues().size() == 0) { |
| | | throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_CUSTOM_FIELD_NOT_EXIST)); |
| | | } |
| | | |
| | | this.issueService.modifyIssue(issueApiForm, new ArrayList<>()); |
| | | List<Issue> issues = this.issueService.modifyIssue(issueApiForm, new ArrayList<>()); |
| | | if (issues != null && issues.size() > 0) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (Issue issue : issues) { |
| | | if (sb.length() > 0) |
| | | sb.append(","); |
| | | sb.append(issue.getProject().getProjectKey()).append("-").append(issue.getIssueNumber()); |
| | | } |
| | | resJsonData.put(Constants.RES_KEY_MSG_SUCCESS, sb.toString()); |
| | | } |
| | | return resJsonData; |
| | | } |
| | | } |
| | | |
| | |
| | | * API 이슈 추가용 Form class |
| | | */ |
| | | public class ApiIssueAddForm { |
| | | @ApiParam(value = "사용자 토큰", required = true) |
| | | @ApiParam(value = "사용자 토큰", required = true, type = "form") |
| | | private String token; |
| | | @ApiParam(value = "이슈 제목") |
| | | @ApiParam(value = "이슈 제목", type = "form") |
| | | private String title; |
| | | @ApiParam(value = "이슈 내용") |
| | | @ApiParam(value = "이슈 내용", type = "form") |
| | | private String description; |
| | | @ApiParam(value = "이슈 타입 ID", required = true) |
| | | @ApiParam(value = "이슈 타입 ID", required = true, type = "form") |
| | | private Long issueTypeId; |
| | | @ApiParam(value = "사용자 정의 필드", required = true) |
| | | @ApiParam(value = "사용자 정의 필드", required = true, type = "form") |
| | | private String customFields; |
| | | |
| | | public ApiIssueAddForm() { |
| | |
| | | Map<String, Object> resJsonData = new HashMap<String, Object>(); |
| | | resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(ex, Constants.RES_KEY_MSG_FAIL)); |
| | | |
| | | return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request); |
| | | return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); |
| | | } |
| | | |
| | | @ExceptionHandler({ StackOverflowError.class, |
| | |
| | | Map<String, Object> resJsonData = new HashMap<String, Object>(); |
| | | resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(MsgConstants.NOT_READABLE_JSON_DATA, Constants.RES_KEY_MSG_FAIL)); |
| | | |
| | | return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request); |
| | | return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.REQUEST_TIMEOUT, request); |
| | | } |
| | | |
| | | @ExceptionHandler({ Exception.class }) |
| | |
| | | Map<String, Object> resJsonData = new HashMap<String, Object>(); |
| | | resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(MsgConstants.NOT_READABLE_JSON_DATA, Constants.RES_KEY_MSG_FAIL)); |
| | | |
| | | return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request); |
| | | return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); |
| | | } |
| | | |
| | | @Override |
| | |
| | | resJsonData.put(Constants.RES_KEY_MESSAGE, |
| | | this.messageAccessor.getResMessage(MsgConstants.NOT_READABLE_JSON_DATA, Constants.RES_KEY_MSG_FAIL)); |
| | | |
| | | return this.handleExceptionInternal(ex, resJsonData, headers, HttpStatus.OK, request); |
| | | return this.handleExceptionInternal(ex, resJsonData, headers, HttpStatus.BAD_REQUEST, request); |
| | | } |
| | | |
| | | @Override |
| | |
| | | final MethodArgumentNotValidException ex, final HttpHeaders headers, |
| | | final HttpStatus status, final WebRequest request) { |
| | | final String bodyOfResponse = "This should be application specific4"; |
| | | return this.handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.OK, request); |
| | | return this.handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); |
| | | } |
| | | |
| | | @Override |
| | |
| | | </query> |
| | | </named-query> |
| | | |
| | | <named-query name="WorkflowTransition.findBySourceIssueStatusIdAndTargetIssueStatusIdAndWorkflowId"> |
| | | <query> |
| | | SELECT new kr.wisestone.owl.vo.WorkflowTransitionVo( |
| | | wt.id |
| | | , sis.id |
| | | , sis.name |
| | | , tis.id |
| | | , tis.name |
| | | , wt.correctX |
| | | , wt.correctY |
| | | , wt.direct |
| | | ) |
| | | FROM WorkflowTransition wt |
| | | INNER JOIN wt.workflow w |
| | | INNER JOIN wt.sourceIssueStatus sis |
| | | INNER JOIN wt.targetIssueStatus tis |
| | | WHERE (sis.id = ?1 OR tis.id = ?1) AND w.id = ?2 |
| | | </query> |
| | | </named-query> |
| | | |
| | | |
| | | </entity-mappings> |
| | | |
| | |
| | | "successToApiIssueOverlap" : "중복값 설정 완료", |
| | | "failedToApiIssueOverlap" : "중복값 설정 실패", |
| | | "failedToApiMonitor": "API 기록 조회 실패", |
| | | "requestSample": "API 요청 샘플", |
| | | "requestSample": "사용자 정의 필드 JSON 샘플", |
| | | "requestSampleAdd": "이슈 추가", |
| | | "requestSampleModify": "이슈 상태 수정", |
| | | "downIssueOverlapSetting" : "하위 이슈 처리 기준 항목", |
| | |
| | | </form> |
| | | </div> |
| | | |
| | | <!-- |
| | | <div class="row"> |
| | | <div class="col-md-4"> |
| | | <label for="issue-detectingInfo" class="issue-label"> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | --> |
| | |
| | | owl-auto-focus |
| | | ng-model="vm.searchAll.keyWord" |
| | | placeholder="{{'issue.pleaseEnterIssueKeyWord' | translate}}" |
| | | ng-enter="fn.searchAll(0)"> |
| | | ng-enter="fn.searchAll(0)">ㅐ |
| | | <div class="input-group-prepend ml-10"> |
| | | <button class="btn btn-navy" ng-click="fn.searchAll(0)"> <span translate="common.search">검색</span></button> |
| | | </div> |