Skip to content

Commit

Permalink
feat(the-camp): 카페 가입 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiteKiwi committed Mar 1, 2022
1 parent aefedcf commit de956a8
Show file tree
Hide file tree
Showing 16 changed files with 265 additions and 15 deletions.
25 changes: 25 additions & 0 deletions src/core/string/extractInnerText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export function extractInnerText(
target: string,
lefts: string[],
right: string,
): string {
let beforeStartIdx = -1;
let startIdx = -1;
for (const left of lefts) {
startIdx = target.indexOf(left, beforeStartIdx);

if (startIdx < 0) {
break;
}

startIdx += left.length;
beforeStartIdx = startIdx;
}

const endIdx = target.indexOf(right, startIdx);
if (startIdx < 0 || endIdx < 0) {
return '';
}

return target.substring(startIdx, endIdx);
}
24 changes: 24 additions & 0 deletions src/core/string/extractInnerTexts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function extractInnerTexts(
target: string,
left: string,
right: string,
): string[] {
const extractedTexts = [];
let offset = 0;

while (target.includes(left) && target.includes(right)) {
const startIdx = target.indexOf(left, offset);
const endIdx = target.indexOf(right, startIdx);

if (startIdx === -1 || endIdx === -1) {
break;
}

const extractedText = target.substring(startIdx + left.length, endIdx);

extractedTexts.push(extractedText);
offset = endIdx + right.length;
}

return extractedTexts;
}
2 changes: 2 additions & 0 deletions src/core/string/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './extractInnerText';
export * from './extractInnerTexts';
1 change: 1 addition & 0 deletions src/services/the-camp/requesters/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './register-cafe';
export * from './login';
export * from './register-soldier';
34 changes: 19 additions & 15 deletions src/services/the-camp/requesters/login/parse-login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@ import 실패_가입회원아님 from './test/실패-가입회원아님.json';

describe('parseLogin', () => {
it('성공', () => {
const cookies = parseLogin(성공 as any);

expect(cookies).toEqual([
{ key: 'userId', value: 'paul2314%40naver.com' },
{ key: 'Token', value: 'sUl%2B4ShTmCqsX%2BXtf7X8MQ%3D%3D' },
{ key: 'iuid', value: '5302760' },
{
key: 'nickname',
value: '%EC%A0%95%EC%9B%90%EB%B0%B0_%28%EC%9D%BC%EB%B0%98%29',
const session = parseLogin(성공 as any);
expect(session).toEqual({
cookies: [
{ key: 'userId', value: 'paul2314%40naver.com' },
{ key: 'Token', value: 'sUl%2B4ShTmCqsX%2BXtf7X8MQ%3D%3D' },
{ key: 'iuid', value: '5302760' },
{
key: 'nickname',
value: '%EC%A0%95%EC%9B%90%EB%B0%B0_%28%EC%9D%BC%EB%B0%98%29',
},
{ key: 'name', value: '%EC%A0%95%EC%9B%90%EB%B0%B0' },
{ key: 'memberGradeCd', value: '0000340003' },
{ key: 'userId', value: 'paul2314%40naver.com' },
{ key: 'checkAutoLogin', value: 'off' },
{ key: 'checkSaveId', value: 'off' },
],
extra: {
IUID: '5302760',
},
{ key: 'name', value: '%EC%A0%95%EC%9B%90%EB%B0%B0' },
{ key: 'memberGradeCd', value: '0000340003' },
{ key: 'userId', value: 'paul2314%40naver.com' },
{ key: 'checkAutoLogin', value: 'off' },
{ key: 'checkSaveId', value: 'off' },
]);
});
});

it('실패_credentail', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { assertsResponse } from './asserts-response';
import 성공 from './test/성공.json';
import 성공_가입 from './test/성공-가입.json';
import 실패_개설전 from './test/실패-개설전.json';
import 실패_오픈대기 from './test/실패-오픈대기.json';

describe('assertsResponse', () => {
it('성공', () => {
expect(() => assertsResponse(성공 as any)).not.toThrow();
});

it('성공-가입', () => {
expect(() => assertsResponse(성공_가입 as any)).not.toThrow();
});

it('실패-개설전', () => {
expect(() => assertsResponse(실패_개설전 as any)).toThrow(
'입영일 부터 10일 이내에 개설됩니다. (오픈카페 전체보기를 통해 확인 가능합니다.)',
);
});

it('실패-오픈대기', () => {
expect(() => assertsResponse(실패_오픈대기 as any)).toThrow(
'훈련병 정보 대기중. 카페 개설은 되었으나 훈련병의 정보가 업데이트 되지 않아 카페 가입이 불가능합니다.(입영일부터 10일 이내 업데이트 됩니다.)',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AxiosResponse } from 'axios';

export function assertsResponse(response: AxiosResponse) {
if (response.data.resultCd !== '0000') {
throw new Error(
response.data.resultMsg || '알 수 없는 오류가 발생하였습니다.',
);
}
}
1 change: 1 addition & 0 deletions src/services/the-camp/requesters/register-cafe/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './register-cafe.requester';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { loginRequester } from '../login';
import { registerCafeRequester } from './register-cafe.requester';

describe.skip('RegisterCafeRequester e2e', () => {
it('성공', async () => {
const session = await loginRequester.request({
id: '',
password: '',
});
await registerCafeRequester.request(
{
이름: '홍길동',
생년월일Code: '08IyuIy6/tXS/vveGiNc+Q==',
입영일: '2022-02-14',
군종: '육군',
입영부대: '육군훈련소-논산',
입영부대TypeCode: '0000140001',
관계: '배우자',
},
session,
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { TheCampSession } from '@common/types';
import {
관계,
관계CodeMap,
군종,
군종CodeMap,
입영부대,
입영부대CodeMap,
} from '@core/types';
import axios, { AxiosRequestConfig } from 'axios';

import { Parameter } from '../../../../core/http';
import { assertsResponse } from './asserts-response';

export class RegisterCafeRequester {
constructor(private readonly asserts = assertsResponse) {}

async request(dto: RegisterCafeDto, session: TheCampSession): Promise<void> {
const response = await axios.post(
'https://www.thecamp.or.kr/main/cafeCreateCheckA.do',
this.createPayload(dto),
this.createOptions(session),
);

this.asserts(response);
}

private createPayload({
이름,
군종,
관계,
입영부대,
입영일,
생년월일Code,
입영부대TypeCode,
}: RegisterCafeDto): string {
return new Parameter({
regOrder: '',
name: 이름,
enterDate: 입영일.replaceAll('-', ''),
birth: 생년월일Code,
trainUnitTypeCd: 입영부대TypeCode,
trainUnitCd: 입영부대CodeMap[입영부대],
grpCd: 군종CodeMap[군종],
traineeRelationshipCd: 관계CodeMap[관계],
}).toString();
}

private createOptions(session: TheCampSession): AxiosRequestConfig {
return {
headers: {
Accept: 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.70 Whale/3.13.131.27 Safari/537.36',
Host: 'www.thecamp.or.kr',
Origin: 'https://www.thecamp.or.kr',
Referer: 'https://www.thecamp.or.kr/eduUnitCafe/viewEduUnitCafeMain.do',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="98", "Whale";v="3"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
Cookie: session.cookies
.map(({ key, value }) => `${key}=${value}`)
.join('; '),
},
};
}
}
export interface RegisterCafeDto {
이름: string;
입영부대: 입영부대;
군종: 군종;
관계: 관계;
// yyyy-MM-dd
입영일: string;

생년월일Code: string;
입영부대TypeCode: string;
}

export const registerCafeRequester = new RegisterCafeRequester();
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"resultCd": "0000",
"resultMsg": "정상처리되었습니다.",
"trainCafeMemberSeq": "8148406"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"data": {
"resultCd": "0000",
"resultMsg": "정상처리되었습니다."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"resultCd": "M004",
"resultMsg": "입영일 부터 10일 이내에 개설됩니다. (오픈카페 전체보기를 통해 확인 가능합니다.)",
"listResult": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"resultCd": "M006",
"resultMsg": "훈련병 정보 대기중. 카페 개설은 되었으나 훈련병의 정보가 업데이트 되지 않아 카페 가입이 불가능합니다.(입영일부터 10일 이내 업데이트 됩니다.)",
"listResult": []
}
}
10 changes: 10 additions & 0 deletions src/services/the-camp/the-camp.service.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { theCampService } from './the-camp.service';

describe.skip('TheCampService e2e', () => {
it('성공', async () => {
const session = await theCampService.login({
id: '',
password: '',
});
});
});
10 changes: 10 additions & 0 deletions src/services/the-camp/the-camp.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Credential } from '@core/types';

import {
loginRequester as _loginRequester,
RegisterCafeDto,
registerCafeRequester as _registerCafeRequester,
RegisterSoldierDto,
registerSoldierRequester as _registerSoldierRequester,
} from './requesters';
Expand All @@ -11,6 +13,7 @@ export class TheCampService {
constructor(
private readonly loginRequester = _loginRequester,
private readonly registerSoldierRequester = _registerSoldierRequester,
private readonly registerCafeRequester = _registerCafeRequester,
) {}

async login(credential: Credential): Promise<TheCampSession> {
Expand All @@ -26,5 +29,12 @@ export class TheCampService {
): Promise<void> {
await this.registerSoldierRequester.request(dto, session);
}

async registerCafe(
dto: RegisterCafeDto,
session: TheCampSession,
): Promise<void> {
await this.registerCafeRequester.request(dto, session);
}
}
export const theCampService = new TheCampService();

0 comments on commit de956a8

Please sign in to comment.