efriend
Version:
EFriend Node Library
260 lines (259 loc) • 10.5 kB
JavaScript
/**
* 한국투자증권 EFriendExpert API
*
* @file packages/EFriendExpert/efriends/efriend.ts
* @version 0.0.1
* @license GNU General Public License v3.0
* @copyright 2017~2023, EFriendExport Community Team
* @author gye hyun james kim <pnuskgh@gmail.com>
*/
import moment from 'moment'; //--- format : YYYYMMDDHHmmss.SSS ZZ - 20191220172919.083 +0900
import { BaseError, ERROR_CODE } from '../common/error/index.js';
import { EFriendLimit } from './efriend.limit.js';
import { EFriend_LIMIT } from './efriend.limit.constant.js';
// import EFriend_JSON_TRID, { METADATA } from './efriend.constant.js';
import { EFriendRest } from './efriendRest.js';
export const limit = new EFriendLimit();
export class EFriend {
logger;
efriendRest;
secrets = [];
// private indexQuery: number = -1;
// private indexOrder: number = -1;
// private limit = new EFriendLimit();
constructor({ logger }) {
this.logger = logger ?? console;
this.efriendRest = new EFriendRest({ logger: this.logger });
this.initialize();
}
isOperatingTime(today = moment()) {
const day = today.day(); //--- 요일, 0. 일요일, 1. 월요일, ..., 6. 토요일
if ((day < 1) || (5 < day)) {
return { code: 1, message: '평일에만 작업 가능 합니다.' };
}
const hhmm = today.format('HH:mm'); //--- 시간과 분
if ((hhmm < '09:00') || ('15:30' < hhmm)) {
return { code: 2, message: '오전 9시부터 오후 5시 30분까지만 작업 가능 합니다.' };
}
return { code: 0, message: '운영 시간' };
}
initialize() {
//--- To-Do: EFriendRest 함수를 EFriend에 구현
//--- To-Do: EFriend에서 EFriendRest 함수를 호출할 때, 장애 발생시 재시도 또는 복구 작업 추가
//--- To-Do: EFriend에서 EFriendRest 함수를 호출할 때, secret 생략
// for (const [_key, value] of Object.entries(EFriend_JSON_TRID)) {
// const trid: string = value.info.trid;
// this[trid] = async function(secret, requestHeader, requestBody, responseHeader) {
// return await this.efriendRest.request(secret, trid, requestHeader, requestBody, responseHeader);
// };
// }
//--- To-Do: EFriendWs의 함수를 EFriend에 구현
//--- To-Do: EFriend에서 EFriendWs의 함수를 호출할 때, 장애 발생시 재시도 또는 복구 작업 추가
//--- To-Do: EFriend에서 EFriendWs의 함수를 호출할 때, secret 생략
}
get rest() {
return this.efriendRest;
}
getSecrets() {
return this.secrets;
}
tokenToSecret(secret) {
if ((typeof (secret.tokens) != 'undefined') && (0 < secret.tokens.length)) {
secret.access_token = secret.tokens[0].access_token;
secret.token_type = secret.tokens[0].token_type;
secret.expires_in = secret.tokens[0].expires_in;
secret.access_token_token_expired = secret.tokens[0].access_token_token_expired;
}
return secret;
}
tokenToSecrets(secrets) {
for (let idx = 0; idx < secrets.length; idx++) {
secrets[idx] = this.tokenToSecret(secrets[idx]);
}
return secrets;
}
setLimit(secrets) {
limit.initialize(secrets);
}
async setSecrets(secrets, isSimpleSet = false) {
try {
if (isSimpleSet) {
this.secrets = secrets;
}
else {
this.setLimit(secrets);
this.secrets = await this.getActiveSecrets(secrets, true, true);
}
this.secrets = this.tokenToSecrets(this.secrets);
return this.secrets;
}
catch (ex) {
throw ex;
}
}
async getActiveSecrets(secrets = this.secrets, refresh = true, isWaiting = false) {
try {
const today = moment().format('YYYY-MM-DD');
const results = [];
for (let secret of secrets) {
if (today <= secret.periodTo) {
if (refresh) {
try {
secret.tokens = await this.getActiveTokens(secret, refresh, isWaiting);
results.push(this.tokenToSecret(secret));
}
catch (ex) {
console.error(ex);
}
}
else {
results.push(secret);
}
}
}
return results;
}
catch (ex) {
throw ex;
}
}
async getActiveTokens(secret, refresh = true, isWaiting = false) {
try {
const now = moment().format('YYYY-MM-DD HH:mm:ss');
const secretTokens = secret.tokens ?? [];
const tokens = [];
for (const token of secretTokens) {
if ((token.access_token_token_expired != null) && (token.access_token_token_expired != '') && (now <= token.access_token_token_expired)) {
tokens.push(token);
}
else {
if (token.access_token != '') {
await this.fetchTokenRemove(secret, token);
}
}
}
if ((0 == secretTokens.length) || (secretTokens[0].access_token != secret.access_token)) {
const token = {
id: -1,
access_token: secret.access_token ?? '',
token_type: secret.token_type ?? '',
expires_in: secret.expires_in ?? 0,
access_token_token_expired: secret.access_token_token_expired ?? '',
secretId: secret.id ?? -1
};
if ((token.access_token_token_expired != null) && (token.access_token_token_expired != '') && (now <= token.access_token_token_expired)) {
tokens.push(token);
}
else {
if (token.access_token != '') {
await this.fetchTokenRemove(secret, token);
}
}
}
if ((refresh) && (tokens.length == 0)) {
tokens.push(await this.fetchToken(secret, isWaiting));
}
return tokens;
}
catch (ex) {
throw ex;
}
}
async fetchToken(secret, isWaiting = false) {
try {
await limit.waitingTokenP(secret, isWaiting);
const requestHeader = {};
const requestBody = {
grant_type: 'client_credentials',
appkey: secret.appkey || secret.appKey,
appsecret: secret.appsecret || secret.appSecret
};
const response = await this.efriendRest.tokenP(secret, requestHeader, requestBody);
if (response.code == 0) {
if (typeof (response.body) != 'undefined') {
const token = {
id: -1,
access_token: response.body?.access_token ?? '',
token_type: response.body?.token_type ?? '',
expires_in: response.body?.expires_in ?? 0,
access_token_token_expired: response.body?.access_token_token_expired ?? '',
secretId: secret.id ?? -1
};
return token;
}
else {
throw new BaseError({ code: ERROR_CODE.REQUIRED, data: `tokenP: ${response.message}.` });
}
}
else {
throw new BaseError({ code: ERROR_CODE.REQUIRED, data: `tokenP: ${response.message}` });
}
}
catch (ex) {
throw ex;
}
}
async fetchTokenRemove(secret, token) {
try {
const requestHeader = {};
const requestBody = {
appkey: secret.appkey || secret.appKey,
appsecret: secret.appsecret || secret.appSecret,
token: token.access_token
};
const response = await this.efriendRest.revokeP(secret, requestHeader, requestBody);
if (response.code == 0) {
return true;
}
else {
console.log('Error : fetchTokenRemove', response);
// throw new BaseError({ code: ERROR_CODE.REQUIRED, data: `revokeP: ${response.message}` });
return true;
}
}
catch (ex) {
console.error(ex);
return false;
}
}
//--- 접속키 유효기간: 24시간
//--- 접속키는 세션 연결시 초기 1회만 사용
async resetApprovalKey(secret) {
try {
const [approval_key, approval_key_expired] = await this.fetchApprovalKey(secret);
secret.approval_key = approval_key;
secret.approval_key_expired = approval_key_expired;
return secret;
}
catch (ex) {
throw ex;
}
}
async fetchApprovalKey(secret) {
try {
const requestHeader = {
"content-type": 'application/json; charset=utf-8',
};
const requestBody = {
grant_type: 'client_credentials',
appkey: secret.appkey || secret.appKey,
secretkey: secret.appsecret || secret.appSecret
};
const response = await this.efriendRest.Approval(secret, requestHeader, requestBody);
if (response.code == 0) {
secret.approval_key = response.body?.approval_key ?? undefined;
if (typeof (secret.approval_key) != 'undefined') {
secret.approval_key_expired = moment().add(EFriend_LIMIT.ws_api.expiration_period, 'hours').format('YYYY-MM-DD HH:mm:ss');
}
return [secret.approval_key ?? '', secret.approval_key_expired ?? ''];
}
else {
throw new BaseError({ code: ERROR_CODE.REQUIRED, data: `Approval: ${response.message}` });
}
}
catch (ex) {
throw ex;
}
}
}
export default EFriend;