refactor: JSP API call -> server call 전환
parent
c4d30fb023
commit
4b990b159c
@ -1,236 +1,105 @@
|
||||
// package cokr.xit.adds.cmm.model;
|
||||
//
|
||||
// import java.io.FileInputStream;
|
||||
// import java.io.IOException;
|
||||
// import java.io.StringReader;
|
||||
// import java.net.http.HttpResponse;
|
||||
// import java.time.LocalDate;
|
||||
// import java.time.format.DateTimeFormatter;
|
||||
// import java.time.format.DateTimeParseException;
|
||||
// import java.util.List;
|
||||
// import java.util.Set;
|
||||
//
|
||||
// import javax.validation.ConstraintViolation;
|
||||
// import javax.validation.Validator;
|
||||
// import javax.xml.XMLConstants;
|
||||
// import javax.xml.transform.stream.StreamSource;
|
||||
// import javax.xml.validation.Schema;
|
||||
// import javax.xml.validation.SchemaFactory;
|
||||
//
|
||||
// import org.apache.commons.lang3.ObjectUtils;
|
||||
// import org.apache.commons.lang3.StringUtils;
|
||||
// import org.json.simple.JSONObject;
|
||||
// import org.springframework.http.HttpHeaders;
|
||||
// import org.xml.sax.SAXException;
|
||||
//
|
||||
// import com.fasterxml.jackson.core.type.TypeReference;
|
||||
//
|
||||
// import cokr.xit.adds.core.spring.exception.ApiCustomException;
|
||||
// import cokr.xit.foundation.data.JSON;
|
||||
// import cokr.xit.foundation.web.WebClient;
|
||||
//
|
||||
// /**
|
||||
// * <pre>
|
||||
// * description :
|
||||
// *
|
||||
// * author : limju
|
||||
// * date : 2024-04-04
|
||||
// * ======================================================================
|
||||
// * 변경일 변경자 변경 내용
|
||||
// * ----------------------------------------------------------------------
|
||||
// * 2024-04-04 limju 최초 생성
|
||||
// *
|
||||
// * </pre>
|
||||
// */
|
||||
// public class ApiUtil {
|
||||
//
|
||||
// /**
|
||||
// * 유효성 검증
|
||||
// * errList가 null인 경우 유효성 검증 실패 시 예외 throw ApiCustomException
|
||||
// *
|
||||
// * @param t validatable object
|
||||
// * @param errList error list
|
||||
// * @param validator validator
|
||||
// */
|
||||
// public static <T> void validate(final T t, final List<String> errList, final Validator validator) {
|
||||
// final Set<ConstraintViolation<T>> list = validator.validate(t);
|
||||
//
|
||||
// if(!list.isEmpty()) {
|
||||
// final List<String> errors = list.stream()
|
||||
// .map(row -> String.format("%s=%s", row.getPropertyPath(), row.getMessageTemplate()))
|
||||
// .toList();
|
||||
//
|
||||
// // 추가적인 유효성 검증이 필요 없는 경우
|
||||
// if(errList == null){
|
||||
// if(!errors.isEmpty()) throw ApiCustomException.create(errors.toString());
|
||||
// return;
|
||||
// }
|
||||
// errList.addAll(errors);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static void checkYmdError(String dt, String fieldName) {
|
||||
// String msg = "유효한 일자가 아닙니다.";
|
||||
// if(ObjectUtils.isEmpty(fieldName)){
|
||||
// msg = "ymd=" + msg;
|
||||
// }else{
|
||||
// msg = fieldName + "=" + msg;
|
||||
// }
|
||||
// try {
|
||||
// final LocalDate rdt = LocalDate.parse(dt, DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
// if(!dt.equals(DateTimeFormatter.ofPattern("yyyyMMdd").format(rdt))){
|
||||
// throw ApiCustomException.create(msg);
|
||||
// }
|
||||
// } catch (DateTimeParseException e) {
|
||||
// throw ApiCustomException.create(msg);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static void checkDatetimeError(String dt, String fieldName) {
|
||||
// String msg = "유효한 일시가 아닙니다.";
|
||||
// if(ObjectUtils.isEmpty(fieldName)){
|
||||
// msg = "ymd=" + msg;
|
||||
// }else{
|
||||
// msg = fieldName + "=" + msg;
|
||||
// }
|
||||
// try {
|
||||
// final LocalDate rdt = LocalDate.parse(dt, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
// if(!dt.equals(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(rdt))){
|
||||
// throw ApiCustomException.create(msg);
|
||||
// }
|
||||
// } catch (DateTimeParseException e) {
|
||||
// throw ApiCustomException.create(msg);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * NimsApi 호출 - x-www-form-urlencoded 방식
|
||||
// * @param uri String
|
||||
// * @param cls T
|
||||
// * @return String
|
||||
// */
|
||||
// public static <T> String callNimsApi(String uri, T cls) {
|
||||
// HttpResponse<String> rslt = new WebClient().post(request -> {
|
||||
// request.contentType(WebClient.Request.ContentType.FORM);
|
||||
// request.uri(uri);
|
||||
// toData(request, cls);
|
||||
// });
|
||||
// return rslt.body();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * irosApi 호출 - x-www-form-urlencoded 방식
|
||||
// * @param uri String
|
||||
// * @param param String
|
||||
// * @return String
|
||||
// */
|
||||
// public static String callIrosApi(String uri, String param) {
|
||||
// HttpResponse<String> rslt = new WebClient().get(request -> {
|
||||
// request.header(HttpHeaders.CONTENT_TYPE, "application/json");
|
||||
// request.contentType(WebClient.Request.ContentType.FORM);
|
||||
// request.uri(uri + param);
|
||||
// });
|
||||
// return rslt.body();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Object -> data로 변환
|
||||
// * @param request WebClient.Request
|
||||
// * @param obj Object
|
||||
// */
|
||||
// public static void toData(final WebClient.Request request, final Object obj){
|
||||
// if(ObjectUtils.isEmpty(obj)) return;
|
||||
//
|
||||
// JSONObject jsonObj = toObjByObj(obj, JSONObject.class);
|
||||
// for (Object key : jsonObj.keySet()) {
|
||||
// // API 호출 시 필요없는 파라메터 제외
|
||||
// if("userId".equals(key) || "dbSkipYn".equals(key)) continue;
|
||||
// request.data((String) key, ObjectUtils.isEmpty(jsonObj.get(key))? StringUtils.EMPTY: jsonObj.get(key));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Object -> class로 변환
|
||||
// * @param obj Object
|
||||
// * @param cls Class
|
||||
// * @return T
|
||||
// */
|
||||
// public static <T> T toObjByObj(final Object obj, final Class<T> cls) {
|
||||
// try {
|
||||
// return ObjectUtils.isNotEmpty(obj)? new JSON().getObjectMapper().convertValue(obj, cls) : null;
|
||||
// } catch (IllegalArgumentException e) {
|
||||
// throw ApiCustomException.create(e.getLocalizedMessage());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Object -> TypeReference로 변환
|
||||
// * @param obj Object
|
||||
// * @param typeRef TypeReference
|
||||
// * @return T
|
||||
// */
|
||||
// public static <T> T toObjByObj(final Object obj, final TypeReference<T> typeRef) {
|
||||
// try {
|
||||
// return ObjectUtils.isNotEmpty(obj)? new JSON().getObjectMapper().convertValue(obj, typeRef) : null;
|
||||
// } catch (IllegalArgumentException e) {
|
||||
// throw ApiCustomException.create(e.getLocalizedMessage());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * XML 유효성 검증
|
||||
// * @param xmlStr String
|
||||
// * @param xsdFilePathName String
|
||||
// * @return boolean
|
||||
// */
|
||||
// public static boolean validateXml(final String xmlStr, final String xsdFilePathName) {
|
||||
// try {
|
||||
// FileInputStream fis = new FileInputStream(xsdFilePathName);
|
||||
// SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
// Schema schema = sf.newSchema(new StreamSource(fis));
|
||||
//
|
||||
// javax.xml.validation.Validator validator = schema.newValidator();
|
||||
// validator.validate(new StreamSource(new StringReader(xmlStr)));
|
||||
// return true;
|
||||
//
|
||||
// } catch (SAXException | IOException e) {
|
||||
// throw ApiCustomException.create(e.getMessage());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * XML 유효성 검증
|
||||
// * @param xmlStr String
|
||||
// * @param xsdFilePath String
|
||||
// * @return boolean
|
||||
// */
|
||||
// public static boolean validateXmlFromXmlStr(final String xmlStr, final String xsdFilePath) {
|
||||
// try {
|
||||
// FileInputStream fis = new FileInputStream(xsdFilePath);
|
||||
// SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
// Schema schema = sf.newSchema(new StreamSource(fis));
|
||||
//
|
||||
// javax.xml.validation.Validator validator = schema.newValidator();
|
||||
// validator.validate(new StreamSource(new StringReader(xmlStr)));
|
||||
// return true;
|
||||
//
|
||||
// } catch (SAXException | IOException e) {
|
||||
// throw ApiCustomException.create(e.getMessage());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * XML 유효성 검증
|
||||
// * @param xmlFilePath String
|
||||
// * @param xsdFilePath String
|
||||
// * @return boolean
|
||||
// */
|
||||
// public static boolean validateXmlFromFile(String xmlFilePath, final String xsdFilePath) {
|
||||
// try (FileInputStream fileInputStream = new FileInputStream(xmlFilePath)) {
|
||||
// byte[] bytes = fileInputStream.readAllBytes();
|
||||
// return validateXmlFromXmlStr(new String(bytes), xsdFilePath);
|
||||
// }catch (IOException e) {
|
||||
// throw ApiCustomException.create(e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
package cokr.xit.adds.cmm.model;
|
||||
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import cokr.xit.foundation.data.JSON;
|
||||
import cokr.xit.foundation.web.WebClient;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* description :
|
||||
*
|
||||
* author : limju
|
||||
* date : 2024-04-04
|
||||
* ======================================================================
|
||||
* 변경일 변경자 변경 내용
|
||||
* ----------------------------------------------------------------------
|
||||
* 2024-04-04 limju 최초 생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public class ApiUtil {
|
||||
|
||||
|
||||
/**
|
||||
* NimsApi 호출 - x-www-form-urlencoded 방식
|
||||
* @param uri String
|
||||
* @param cls T
|
||||
* @return String
|
||||
*/
|
||||
public static <T> String callNimsApi(String uri, T cls) {
|
||||
HttpResponse<String> rslt = new WebClient().post(request -> {
|
||||
request.contentType(WebClient.Request.ContentType.FORM);
|
||||
request.uri(uri);
|
||||
toData(request, cls);
|
||||
});
|
||||
return rslt.body();
|
||||
}
|
||||
|
||||
/**
|
||||
* irosApi 호출 - x-www-form-urlencoded 방식
|
||||
* @param uri String
|
||||
* @param param String
|
||||
* @return String
|
||||
*/
|
||||
public static String callIrosApi(String uri, String param) {
|
||||
HttpResponse<String> rslt = new WebClient().get(request -> {
|
||||
request.header(HttpHeaders.CONTENT_TYPE, "application/json");
|
||||
request.contentType(WebClient.Request.ContentType.FORM);
|
||||
request.uri(uri + param);
|
||||
});
|
||||
return rslt.body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Object -> data로 변환
|
||||
* @param request WebClient.Request
|
||||
* @param obj Object
|
||||
*/
|
||||
public static void toData(final WebClient.Request request, final Object obj){
|
||||
if(ObjectUtils.isEmpty(obj)) return;
|
||||
|
||||
JSONObject jsonObj = toObjByObj(obj, JSONObject.class);
|
||||
for (Object key : jsonObj.keySet()) {
|
||||
// API 호출 시 필요없는 파라메터 제외
|
||||
request.data((String) key, ObjectUtils.isEmpty(jsonObj.get(key))? StringUtils.EMPTY: jsonObj.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object -> class로 변환
|
||||
* @param obj Object
|
||||
* @param cls Class
|
||||
* @return T
|
||||
*/
|
||||
public static <T> T toObjByObj(final Object obj, final Class<T> cls) {
|
||||
try {
|
||||
return ObjectUtils.isNotEmpty(obj)? new JSON().getObjectMapper().convertValue(obj, cls) : null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
//throw new Exception(e.getLocalizedMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object -> TypeReference로 변환
|
||||
* @param obj Object
|
||||
* @param typeRef TypeReference
|
||||
* @return T
|
||||
*/
|
||||
public static <T> T toObjByObj(final Object obj, final TypeReference<T> typeRef) {
|
||||
try {
|
||||
return ObjectUtils.isNotEmpty(obj)? new JSON().getObjectMapper().convertValue(obj, typeRef) : null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
//throw ApiCustomException.create(e.getLocalizedMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,346 @@
|
||||
package cokr.xit.adds.cmm.model;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* description :
|
||||
*
|
||||
* packageName : cokr.xit.adds.inf.nims.model
|
||||
* fileName : NimsApiRequest
|
||||
* author : limju
|
||||
* date : 2024-03-21
|
||||
* ======================================================================
|
||||
* 변경일 변경자 변경 내용
|
||||
* ----------------------------------------------------------------------
|
||||
* 2024-03-21 limju 최초 생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
public class NimsApiRequest {
|
||||
/**
|
||||
* 마약류 취급자 정보 조회 request
|
||||
*/
|
||||
@Data
|
||||
public static class BsshInfoReq {
|
||||
|
||||
/**
|
||||
* 인증키
|
||||
*/
|
||||
private String k;
|
||||
|
||||
/**
|
||||
* 조회범위
|
||||
* 1-전체, 2-내거래처
|
||||
*/
|
||||
private String fg;
|
||||
|
||||
/**
|
||||
* 조회 페이지
|
||||
*/
|
||||
private String pg;
|
||||
|
||||
/**
|
||||
* 사업자 등록 번호
|
||||
*/
|
||||
private String bi;
|
||||
|
||||
/**
|
||||
* 요양기관번호
|
||||
*/
|
||||
private String hp;
|
||||
|
||||
/**
|
||||
* 업체명(like 검색)
|
||||
*/
|
||||
private String bn;
|
||||
|
||||
/**
|
||||
* 취급자식별번호
|
||||
* 보고자의 마약류취급자 식별번호
|
||||
*/
|
||||
private String bc;
|
||||
|
||||
/**
|
||||
* 기준일자 이후
|
||||
* yyyyMMdd
|
||||
*/
|
||||
private String ymd;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 조회범위2
|
||||
* 1:NK(취급승인)포함 - default
|
||||
* 2:NK(취급승인)제외
|
||||
* </pre>
|
||||
*/
|
||||
private String fg2 = "1";
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* DB 조회 skip 여부
|
||||
* DB먼저 조회 하고 없는 경우 NIMS API 조회 호출
|
||||
* true 인 경우 DB 조회 skip
|
||||
* </pre>
|
||||
*/
|
||||
private String dbSkipYn = "N";
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 업무상 필요에 의해 추가
|
||||
* </pre>
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상품 정보 조회 request
|
||||
*/
|
||||
@Data
|
||||
public static class ProductInfoReq {
|
||||
/**
|
||||
* 인증키
|
||||
*/
|
||||
private String k;
|
||||
|
||||
/**
|
||||
* 조회범위
|
||||
* 1-전체, 2-내거래품목, 3-청구코드매핑
|
||||
*/
|
||||
private String fg;
|
||||
|
||||
/**
|
||||
* 조회 페이지
|
||||
*/
|
||||
private String pg;
|
||||
|
||||
/**
|
||||
* 기준일자 이후
|
||||
* yyyyMMdd
|
||||
*/
|
||||
private String ymd;
|
||||
|
||||
/**
|
||||
* 중점/일반 구분
|
||||
* 1:중점
|
||||
* 2:일반
|
||||
*/
|
||||
private String fg2;
|
||||
|
||||
/**
|
||||
* 제품코드
|
||||
* 제품코드(like 검색)
|
||||
* 조회범위(pg)가 3인 경우 청구 코드
|
||||
*/
|
||||
private String p;
|
||||
|
||||
/**
|
||||
* 제품명(like 검색)
|
||||
*/
|
||||
private String pn;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* DB 조회 skip 여부
|
||||
* DB먼저 조회 하고 없는 경우 NIMS API 조회 호출
|
||||
* true 인 경우 DB 조회 skip
|
||||
* 업무상 필요에 의해 추가
|
||||
* </pre>
|
||||
*/
|
||||
private String dbSkipYn = "N";
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 업무상 필요에 의해 추가
|
||||
* </pre>
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DsuseRptInfoReq {
|
||||
/**
|
||||
* 인증키
|
||||
*/
|
||||
private String k;
|
||||
|
||||
/**
|
||||
* 조회기준일자(1-보고일,2-취급일)
|
||||
* 1-보고일자 : 병의원에서 NIMS에 폐기 보고한 날짜 - 본 시스템에서는 사용하지 않음
|
||||
* 2-취급일자
|
||||
*/
|
||||
private String fg = "2";
|
||||
|
||||
/**
|
||||
* 조회 페이지
|
||||
*/
|
||||
private String pg;
|
||||
|
||||
/**
|
||||
* 보고유형
|
||||
* 빈값 : 전체
|
||||
* 0: 신규, 1: 취소, 2: 변경
|
||||
*/
|
||||
private String fg2;
|
||||
|
||||
/**
|
||||
* 보고구분코드
|
||||
* AAR - 폐기보고 고정
|
||||
*/
|
||||
private String se = "AAR";
|
||||
|
||||
/**
|
||||
* 조회 시작일(yyyyMMdd)
|
||||
* 최대 1개월
|
||||
*/
|
||||
private String sdt;
|
||||
|
||||
/**
|
||||
* 조회 종료일(yyyyMMdd)
|
||||
* 최대 1개월
|
||||
*/
|
||||
private String edt;
|
||||
|
||||
/**
|
||||
* 마약류 취급자 식별 번호
|
||||
*/
|
||||
private String bc;
|
||||
|
||||
/**
|
||||
* 업체명
|
||||
*/
|
||||
private String bn;
|
||||
|
||||
/**
|
||||
* 사용자 보고 식별 번호
|
||||
*/
|
||||
private String ur;
|
||||
|
||||
/**
|
||||
* 폐기 보고 상태
|
||||
* "": 전체, 01: 확인, 02: 보류, 03: 정정, 04: 미처리
|
||||
*/
|
||||
private String ps;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 관할관청기관코드
|
||||
* 4050147 - 처인구보건소
|
||||
* 4050148 - 기흥구보건소
|
||||
* 4050149 - 수지구보건소
|
||||
* </pre>
|
||||
*/
|
||||
private String gc;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 업무상 필요에 의해 추가
|
||||
* </pre>
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 제품 일련 번호 정보 조회 request
|
||||
*/
|
||||
@Data
|
||||
public static class MnfSeqInfoReq {
|
||||
/**
|
||||
* 인증키
|
||||
*/
|
||||
private String k;
|
||||
|
||||
/**
|
||||
* 조회범위 : 실제는 동일
|
||||
* 1-제조번호, 2-일련번호, 3-바코드/RFID
|
||||
*/
|
||||
private String fg;
|
||||
|
||||
/**
|
||||
* 조회 페이지
|
||||
*/
|
||||
private String pg;
|
||||
|
||||
/**
|
||||
* 제품코드
|
||||
* 제품코드(like 검색)
|
||||
* 조회범위(pg)가 3인 경우 청구 코드
|
||||
*/
|
||||
private String p;
|
||||
|
||||
|
||||
/**
|
||||
* 기준일자 이후
|
||||
* yyyyMMdd
|
||||
*/
|
||||
private String ymd;
|
||||
|
||||
/**
|
||||
* 제품코드 : like 검색 - 오류 -> 사용하지 말것
|
||||
*/
|
||||
private String t = StringUtils.EMPTY;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 업무상 필요에 의해 추가
|
||||
* </pre>
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 관할 허가 관청 정보 조회 request
|
||||
*/
|
||||
@Data
|
||||
public static class JurisdictionGovInfoReq {
|
||||
/**
|
||||
* 인증키
|
||||
*/
|
||||
private String k;
|
||||
|
||||
/**
|
||||
* 조회범위
|
||||
* 1-전체
|
||||
*/
|
||||
private String fg;
|
||||
|
||||
/**
|
||||
* 조회 페이지
|
||||
*/
|
||||
private String pg;
|
||||
|
||||
/**
|
||||
* 기관명
|
||||
*/
|
||||
private String onm;
|
||||
|
||||
/**
|
||||
* 기관 코드
|
||||
*/
|
||||
private String ocd;
|
||||
|
||||
/**
|
||||
* 주소
|
||||
*/
|
||||
private String adr;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* DB 조회 skip 여부
|
||||
* DB먼저 조회 하고 없는 경우 NIMS API 조회 호출
|
||||
* true 인 경우 DB 조회 skip
|
||||
* </pre>
|
||||
*/
|
||||
private String dbSkipYn = "N";
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 업무상 필요에 의해 추가
|
||||
* </pre>
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue