refac, feat: API 호출 구조 재작성

- api.php에서 param path 강제
- api.php에서 GET param 허용
- SammoAPI 호출자를 axios에서 fetch 기반(ky)로 변경
- SammoAPI에서 단순 POST대신 REST에 따라 지정 가능하도록 재구성
- SammoAPI에서 NumVar, StrVar를 PathParam으로 변경하도록 변경
- API CallType들을 def/API로 분리 시작
  - 일부 API를 시험삼아 변경(login)
This commit is contained in:
2022-04-02 23:31:05 +09:00
parent ad1f070557
commit 5879661267
14 changed files with 282 additions and 172 deletions
+5 -1
View File
@@ -8,5 +8,9 @@ require(__DIR__ . '/vendor/autoload.php');
if (!class_exists('\\sammo\\RootDB')) { if (!class_exists('\\sammo\\RootDB')) {
Json::dieWithReason('No DB'); Json::dieWithReason('No DB');
} }
$eParams = $_GET;
if(key_exists('path', $eParams)){
unset($eParams['path']);
}
APIHelper::launch(dirname(__FILE__), $_GET['path']??null); APIHelper::launch(dirname(__FILE__), $_GET['path']??'', $eParams, true);
+6 -1
View File
@@ -5,4 +5,9 @@ namespace sammo;
include "lib.php"; include "lib.php";
include "func.php"; include "func.php";
APIHelper::launch(dirname(__FILE__), $_GET['path']??null); $eParams = $_GET;
if(key_exists('path', $eParams)){
unset($eParams['path']);
}
APIHelper::launch(dirname(__FILE__), $_GET['path']??'', $eParams, true);
+40 -34
View File
@@ -1,65 +1,71 @@
import type { InvalidResponse, ReserveBulkCommandResponse } from "./defs"; import type { BettingDetailResponse, ReserveBulkCommandResponse } from "./defs";
import type { Args } from "./processing/args"; import type { Args } from "./processing/args";
import { callSammoAPI, done, type CallbackT, type RawArgType, type ValidResponse } from "./util/callSammoAPI"; import { callSammoAPI, extractHttpMethod, GET, PATCH, POST, PUT, type APITail, type APICallT, type RawArgType, type ValidResponse, type InvalidResponse } from "./util/callSammoAPI";
export type { ValidResponse, InvalidResponse }; export type { ValidResponse, InvalidResponse };
import { APIPathGen } from "./util/APIPathGen.js"; import { APIPathGen, NumVar } from "./util/APIPathGen.js";
const apiRealPath = { const apiRealPath = {
Betting: { Betting: {
Bet: done, Bet: PUT,
GetBettingDetail: done, GetBettingDetail: NumVar('betting_id',
GetBettingList: done, GET as APICallT<undefined, BettingDetailResponse>
),
GetBettingList: GET,
}, },
Command: { Command: {
GetReservedCommand: done, GetReservedCommand: GET as APICallT<undefined>,
PushCommand: done, PushCommand: PATCH,
RepeatCommand: done, RepeatCommand: PATCH,
ReserveCommand: done, ReserveCommand: PUT,
ReserveBulkCommand: done as CallbackT<{ ReserveBulkCommand: PUT as APICallT<{
turnList: number[], turnList: number[],
action: string, action: string,
arg: Args arg: Args
}[], ReserveBulkCommandResponse>, }[], ReserveBulkCommandResponse>,
}, },
General: { General: {
Join: done, Join: POST,
}, },
InheritAction: { InheritAction: {
BuyHiddenBuff: done, BuyHiddenBuff: PUT,
BuyRandomUnique: done, BuyRandomUnique: PUT,
BuySpecificUnique: done, BuySpecificUnique: PUT,
ResetSpecialWar: done, ResetSpecialWar: PUT,
ResetTurnTime: done, ResetTurnTime: PUT,
SetNextSpecialWar: done, SetNextSpecialWar: PUT,
}, },
Misc: { UploadImage: done }, Misc: { UploadImage: POST },
NationCommand: { NationCommand: {
GetReservedCommand: done, GetReservedCommand: GET,
PushCommand: done, PushCommand: PATCH,
RepeatCommand: done, RepeatCommand: PATCH,
ReserveCommand: done, ReserveCommand: PUT,
ReserveBulkCommand: done as CallbackT<{ ReserveBulkCommand: PUT as APICallT<{
turnList: number[], turnList: number[],
action: string, action: string,
arg: Args arg: Args
}[], ReserveBulkCommandResponse>, }[], ReserveBulkCommandResponse>,
}, },
Nation: { Nation: {
SetNotice: done, SetNotice: PUT,
SetScoutMsg: done, SetScoutMsg: PUT,
SetBill: done, SetBill: PUT,
SetRate: done, SetRate: PUT,
SetSecretLimit: done, SetSecretLimit: PUT,
SetBlockWar: done, SetBlockWar: PUT,
SetBlockScout: done, SetBlockScout: PUT,
}, },
Test: NumVar('id', {
SetThis: PUT,
})
} as const; } as const;
export const SammoAPI = APIPathGen(apiRealPath, (path: string[]) => { export const SammoAPI = APIPathGen(apiRealPath, (path: string[], tail: APITail, pathParam) => {
const method = extractHttpMethod(tail);
return (args?: RawArgType, returnError?: boolean) => { return (args?: RawArgType, returnError?: boolean) => {
if (returnError) { if (returnError) {
return callSammoAPI(path.join('/'), args, true); return callSammoAPI(method, path.join('/'), args, pathParam, true);
} }
return callSammoAPI(path.join('/'), args); return callSammoAPI(method, path.join('/'), args, pathParam);
}; };
}); });
+14 -10
View File
@@ -1,21 +1,25 @@
import type { InvalidResponse } from "./defs"; import type { AutoLoginFailed, AutoLoginNonceResponse, AutoLoginResponse } from "./defs/API/Login";
import { APIPathGen } from "./util/APIPathGen"; import { APIPathGen } from "./util/APIPathGen";
import { callSammoAPI, done, type ValidResponse } from "./util/callSammoAPI"; import { callSammoAPI, extractHttpMethod, GET, POST, type APICallT, type APITail, type InvalidResponse, type RawArgType, type ValidResponse } from "./util/callSammoAPI";
export type { ValidResponse, InvalidResponse }; export type { ValidResponse, InvalidResponse };
const apiRealPath = { const apiRealPath = {
Login: { Login: {
LoginByID: done, LoginByID: POST,
LoginByToken: done, LoginByToken: POST as APICallT<{
ReqNonce: done, hashedToken: string,
token_id: number,
}, AutoLoginResponse, AutoLoginFailed>,
ReqNonce: GET as APICallT<undefined, AutoLoginNonceResponse, AutoLoginFailed>
}, },
} as const; } as const;
export const SammoRootAPI = APIPathGen(apiRealPath, (path: string[]) => { export const SammoRootAPI = APIPathGen(apiRealPath, (path: string[], tail: APITail, pathParam) => {
return (args?: Record<string, unknown>, returnError?: boolean) => { const method = extractHttpMethod(tail);
return (args?: RawArgType, returnError?: boolean) => {
if (returnError) { if (returnError) {
return callSammoAPI(path.join('/'), args, true); return callSammoAPI(method, path.join('/'), args, pathParam, true);
} }
return callSammoAPI(path.join('/'), args); return callSammoAPI(method, path.join('/'), args, pathParam);
}; };
}) as typeof apiRealPath; });
+3 -13
View File
@@ -129,21 +129,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { BettingInfo, ToastType } from "@/defs"; import type { BettingDetailResponse, BettingInfo, ToastType } from "@/defs";
import { SammoAPI, type ValidResponse } from "@/SammoAPI"; import { SammoAPI } from "@/SammoAPI";
import { joinYearMonth } from "@/util/joinYearMonth"; import { joinYearMonth } from "@/util/joinYearMonth";
import { parseYearMonth } from "@/util/parseYearMonth"; import { parseYearMonth } from "@/util/parseYearMonth";
import { isString, range, sum } from "lodash"; import { isString, range, sum } from "lodash";
import { ref, type PropType, watch } from "vue"; import { ref, type PropType, watch } from "vue";
type BettingDetailResponse = ValidResponse & {
bettingInfo: BettingInfo;
bettingDetail: [string, number][];
myBetting: [string, number][];
remainPoint: number;
year: number;
month: number;
};
const props = defineProps({ const props = defineProps({
bettingID: { bettingID: {
@@ -287,9 +279,7 @@ function calcReward() {
async function loadBetting(bettingID: number) { async function loadBetting(bettingID: number) {
try { try {
const result = await SammoAPI.Betting.GetBettingDetail<BettingDetailResponse>({ const result = await SammoAPI.Betting.GetBettingDetail[bettingID]();
betting_id: bettingID,
});
year.value = result.year; year.value = result.year;
month.value = result.month; month.value = result.month;
yearMonth.value = joinYearMonth(result.year, result.month); yearMonth.value = joinYearMonth(result.year, result.month);
+40
View File
@@ -0,0 +1,40 @@
export type LoginResponse = {
result: true,
nextToken: [number, string] | undefined,
}
export type LoginFailed = {
result: false,
reqOTP: boolean,
reason: string,
}
export type LoginResponseWithKakao = LoginResponse | LoginFailed;
export type OTPResponse = {
result: true,
validUntil: string,
} | {
result: false,
reset: boolean,
reason: string,
}
export type AutoLoginNonceResponse = {
result: true,
loginNonce: string,
};
export type AutoLoginResponse = {
result: true,
nextToken: [number, string] | undefined,
}
export type AutoLoginFailed = {
result: false,
silent: boolean,
reason: string,
}
+13 -6
View File
@@ -1,10 +1,7 @@
import type { Args } from "./processing/args"; import type { Args } from "@/processing/args";
import type { ValidResponse, InvalidResponse } from "@/util/callSammoAPI";
export type InvalidResponse = {
result: false;
reason: string;
}
export type { ValidResponse, InvalidResponse };
export type BasicGeneralListResponse = { export type BasicGeneralListResponse = {
result: true, result: true,
nationID: number, nationID: number,
@@ -266,3 +263,13 @@ export type BettingInfo = {
candidates: SelectItem[]; candidates: SelectItem[];
winner?: number[]; winner?: number[];
} }
export type BettingDetailResponse = ValidResponse & {
bettingInfo: BettingInfo;
bettingDetail: [string, number][];
myBetting: [string, number][];
remainPoint: number;
year: number;
month: number;
};
+4 -43
View File
@@ -8,51 +8,12 @@ import { setAxiosXMLHttpRequest } from '@util/setAxiosXMLHttpRequest';
import { unwrap_any } from '@util/unwrap_any'; import { unwrap_any } from '@util/unwrap_any';
import { sha512 } from 'js-sha512'; import { sha512 } from 'js-sha512';
import { unwrap } from '@util/unwrap'; import { unwrap } from '@util/unwrap';
import type { InvalidResponse } from '@/defs';
import { delay } from '@util/delay'; import { delay } from '@util/delay';
import { Modal } from 'bootstrap'; import { Modal } from 'bootstrap';
import '@/gateway/common'; import '@/gateway/common';
import { isString } from 'lodash'; import { isString } from 'lodash';
import { SammoRootAPI } from '@/SammoRootAPI'; import { SammoRootAPI, type InvalidResponse } from '@/SammoRootAPI';
import type { LoginFailed, LoginResponse, LoginResponseWithKakao, OTPResponse } from '@/defs/API/Login';
type LoginResponse = {
result: true,
nextToken: [number, string] | undefined,
}
type LoginFailed = {
result: false,
reqOTP: boolean,
reason: string,
}
type LoginResponseWithKakao = LoginResponse | LoginFailed;
type OTPResponse = {
result: true,
validUntil: string,
} | {
result: false,
reset: boolean,
reason: string,
}
type AutoLoginNonceResponse = {
result: true,
loginNonce: string,
};
type AutoLoginResponse = {
result: true,
nextToken: [number, string] | undefined,
}
type AutoLoginFailed = {
result: false,
silent: boolean,
reason: string,
}
declare global { declare global {
interface Window { interface Window {
getOAuthToken: (mode: string, scope_list: string[]) => void; getOAuthToken: (mode: string, scope_list: string[]) => void;
@@ -101,7 +62,7 @@ async function tryAutoLogin() {
const [tokenID, token] = tokenInfo; const [tokenID, token] = tokenInfo;
const result = await SammoRootAPI.Login.ReqNonce<AutoLoginNonceResponse, AutoLoginFailed>({}, true); const result = await SammoRootAPI.Login.ReqNonce(undefined, true);
if (!result) { if (!result) {
//api 에러. //api 에러.
@@ -116,7 +77,7 @@ async function tryAutoLogin() {
const nonce = result.loginNonce; const nonce = result.loginNonce;
const hashedToken = sha512(token + nonce); const hashedToken = sha512(token + nonce);
const loginResult = await SammoRootAPI.Login.LoginByToken<AutoLoginResponse, AutoLoginFailed>({ const loginResult = await SammoRootAPI.Login.LoginByToken({
'hashedToken': hashedToken, 'hashedToken': hashedToken,
'token_id': tokenID, 'token_id': tokenID,
}, true); }, true);
+9 -5
View File
@@ -1,21 +1,25 @@
export function APIPathGen<T>(obj: T, callback: (path: string[])=>unknown): T; export function APIPathGen<T, V>(
obj: T,
callback: (path: string[], tail: V, pathParam?: Record<string, string | number>) => unknown,
pathParam?: Record<string, string | number>
): T;
export function StrVar<PathType extends string>(): <NextCall>(next: NextCall)=>{ export function StrVar<PathType extends string>(paramKey: string): <NextCall>(next: NextCall) => {
[v in PathType]: NextCall [v in PathType]: NextCall
}; };
export function NumVar<NextCall>(next: NextCall):{ export function NumVar<NextCall>(paramKey: string, next: NextCall): {
[v: number]: NextCall [v: number]: NextCall
}; };
/* /*
const apiPath = { const apiPath = {
SomePath: someFunc, SomePath: someFunc,
User: StrVar<'a'|'b'>()({ User: StrVar<'a'|'b'>('name')({
Update: someFunc, Update: someFunc,
Delete: someFunc, Delete: someFunc,
}), }),
NationInfo: NumVar({ NationInfo: NumVar('id', {
show: someFunc show: someFunc
}) })
} }
+17 -6
View File
@@ -1,4 +1,4 @@
export function APIPathGen(obj, callback, path) { export function APIPathGen(obj, callback, path, pathParams) {
return new Proxy(obj, { return new Proxy(obj, {
get(target, key) { get(target, key) {
let nextPath; let nextPath;
@@ -9,12 +9,21 @@ export function APIPathGen(obj, callback, path) {
nextPath = [...path, key.toString()]; nextPath = [...path, key.toString()];
} }
if (pathParams !== undefined) {
pathParams = { ...pathParams };
}
const varType = target.__nextVarType; const varType = target.__nextVarType;
const varKey = target.__nextVarKey;
let next; let next;
if (varType !== undefined) { if (varType !== undefined && varKey !== undefined) {
if (typeof key !== varType) { if (typeof key !== varType) {
throw `${key} is not ${varType}`; throw `${key} is not ${varType}`;
} }
if(pathParams === undefined){
pathParams = {}
}
pathParams[varKey] = key;
next = target.next; next = target.next;
} }
else if (key in target) { else if (key in target) {
@@ -25,26 +34,28 @@ export function APIPathGen(obj, callback, path) {
} }
if (typeof (next) === 'function') { if (typeof (next) === 'function') {
return callback(nextPath); return callback(nextPath, next, pathParams);
} }
return APIPathGen(next, callback, nextPath); return APIPathGen(next, callback, nextPath, pathParams);
} }
}) })
} }
//generic 인자로 '자동'을 주려면 생략해야하므로 2단 호출 //generic 인자로 '자동'을 주려면 생략해야하므로 2단 호출
export function StrVar() { export function StrVar(key) {
return (next) => { return (next) => {
return { return {
__nextVarType: 'string', __nextVarType: 'string',
__nextVarKey: key,
next next
} }
} }
} }
export function NumVar(next) { export function NumVar(key, next) {
return { return {
__nextVarType: 'number', __nextVarType: 'number',
__nextVarKey: key,
next next
} }
} }
+90 -22
View File
@@ -1,35 +1,64 @@
import axios from "axios"; import ky from 'ky';
import { isArray } from "lodash"; import { isArray, isEmpty } from "lodash";
import type { InvalidResponse } from '@/defs';
export type ValidResponse = { export type ValidResponse = {
result: true result: true
} }
export type RawArgType = Record<string, unknown>|Record<string, unknown>[]; export type InvalidResponse = {
result: false;
reason: string;
}
export interface CallbackT<ArgType extends RawArgType, ResultType extends ValidResponse = ValidResponse, ErrorType extends InvalidResponse = InvalidResponse>{
export type RawArgType = Record<string, unknown> | Record<string, unknown>[] | undefined;
export interface APICallT<ArgType extends RawArgType, ResultType extends ValidResponse = ValidResponse, ErrorType extends InvalidResponse = InvalidResponse> {
(args?: ArgType): Promise<ResultType>; (args?: ArgType): Promise<ResultType>;
(args: ArgType | undefined, returnError: false): Promise<ResultType>; (args: ArgType | undefined, returnError: false): Promise<ResultType>;
(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>; (args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
} }
export async function callSammoAPI<ResultType extends ValidResponse>(path: string | string[], args?: Record<string, unknown> | Record<string, unknown>[]): Promise<ResultType>; type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
export async function callSammoAPI<ResultType extends ValidResponse>(path: string | string[], args: Record<string, unknown> | Record<string, unknown>[] | undefined, returnError: false): Promise<ResultType>; export type APITail = typeof GET | typeof POST | typeof PUT | typeof PATCH | typeof HEAD | typeof DELETE;
export async function callSammoAPI<ResultType extends ValidResponse, ErrorType extends InvalidResponse>(path: string | string[], args: Record<string, unknown> | Record<string, unknown>[] | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function callSammoAPI<ResultType extends ValidResponse, ErrorType extends InvalidResponse>(path: string | string[], args?: Record<string, unknown> | Record<string, unknown>[], returnError = false): Promise<ResultType | ErrorType> { const httpMethodMap = new Map<APITail, HttpMethod>([
[GET, 'get'],
[POST, 'post'],
[PUT, 'put'],
[PATCH, 'patch'],
[HEAD, 'head'],
[DELETE, 'delete'],
]);
export function extractHttpMethod(tail: APITail): HttpMethod {
return httpMethodMap.get(tail) ?? 'post';
}
export async function callSammoAPI<ResultType extends ValidResponse>(method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record<string, string | number> | undefined): Promise<ResultType>;
export async function callSammoAPI<ResultType extends ValidResponse>(method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record<string, string | number> | undefined, returnError: false): Promise<ResultType>;
export async function callSammoAPI<ResultType extends ValidResponse, ErrorType extends InvalidResponse>(method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record<string, string | number> | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function callSammoAPI<ResultType extends ValidResponse, ErrorType extends InvalidResponse>(method: HttpMethod, path: string | string[], args: RawArgType, paramArgs: Record<string, string | number> | undefined, returnError = false): Promise<ResultType | ErrorType> {
if (isArray(path)) { if (isArray(path)) {
path = path.join('/'); path = path.join('/');
} }
const response = await axios({ if (args && isEmpty(args)) {
url: `api.php?path=${path}`, args = undefined;
method: "post", }
responseType: "json",
data: args const result = await ky('api.php', {
}); searchParams: {
const result: ErrorType | ResultType = response.data; ...paramArgs,
path,
},
method,
json: args,
headers: {
'content-type': 'application/json'
}
}).json() as ErrorType | ResultType;
if (!result.result) { if (!result.result) {
if (returnError) { if (returnError) {
return result; return result;
@@ -39,11 +68,50 @@ export async function callSammoAPI<ResultType extends ValidResponse, ErrorType e
return result; return result;
} }
export async function done<ResultType extends ValidResponse>(args?: RawArgType): Promise<ResultType>; export async function GET<ResultType extends ValidResponse, ArgType extends undefined = undefined>(args?: ArgType): Promise<ResultType>;
export async function done<ResultType extends ValidResponse>(args: RawArgType | undefined, returnError: false): Promise<ResultType>; export async function GET<ResultType extends ValidResponse, ArgType extends undefined = undefined>(args: ArgType | undefined, returnError: false): Promise<ResultType>;
export async function done<ResultType extends ValidResponse, ErrorType extends InvalidResponse>(args: RawArgType | undefined, returnError: true): Promise<ResultType | ErrorType>; export async function GET<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined>(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function GET<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined>(args?: ArgType, returnError = false): Promise<ResultType | ErrorType> {
console.error(`Can't directly call GET. ${args}, ${returnError}. Use auto-generated path API.`);
return callSammoAPI<ResultType, ErrorType>('get', [], args, undefined, true);
}
export async function done<ResultType extends ValidResponse, ErrorType extends InvalidResponse>(args?: RawArgType, returnError = false): Promise<ResultType | ErrorType> { export async function POST<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType): Promise<ResultType>;
console.error(`Can't directly call. ${args}, ${returnError}. Use auto-generated path API.`); export async function POST<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: false): Promise<ResultType>;
return callSammoAPI<ResultType, ErrorType>([], args, true); export async function POST<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function POST<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType, returnError = false): Promise<ResultType | ErrorType> {
console.error(`Can't directly call POST. ${args}, ${returnError}. Use auto-generated path API.`);
return callSammoAPI<ResultType, ErrorType>('post', [], args, undefined, true);
}
export async function PUT<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType): Promise<ResultType>;
export async function PUT<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: false): Promise<ResultType>;
export async function PUT<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function PUT<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType, returnError = false): Promise<ResultType | ErrorType> {
console.error(`Can't directly call PUT. ${args}, ${returnError}. Use auto-generated path API.`);
return callSammoAPI<ResultType, ErrorType>('put', [], args, undefined, true);
}
export async function PATCH<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType): Promise<ResultType>;
export async function PATCH<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: false): Promise<ResultType>;
export async function PATCH<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function PATCH<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType, returnError = false): Promise<ResultType | ErrorType> {
console.error(`Can't directly call PATCH. ${args}, ${returnError}. Use auto-generated path API.`);
return callSammoAPI<ResultType, ErrorType>('patch', [], args, undefined, true);
}
export async function HEAD<ResultType extends ValidResponse, ArgType extends undefined = undefined>(args?: ArgType): Promise<ResultType>;
export async function HEAD<ResultType extends ValidResponse, ArgType extends undefined = undefined>(args: ArgType | undefined, returnError: false): Promise<ResultType>;
export async function HEAD<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined>(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function HEAD<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends undefined = undefined>(args?: ArgType, returnError = false): Promise<ResultType | ErrorType> {
console.error(`Can't directly call HEAD. ${args}, ${returnError}. Use auto-generated path API.`);
return callSammoAPI<ResultType, ErrorType>('head', [], args, undefined, true);
}
export async function DELETE<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType): Promise<ResultType>;
export async function DELETE<ResultType extends ValidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: false): Promise<ResultType>;
export async function DELETE<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args: ArgType | undefined, returnError: true): Promise<ResultType | ErrorType>;
export async function DELETE<ResultType extends ValidResponse, ErrorType extends InvalidResponse, ArgType extends RawArgType = RawArgType>(args?: ArgType, returnError = false): Promise<ResultType | ErrorType> {
console.error(`Can't directly call DELETE. ${args}, ${returnError}. Use auto-generated path API.`);
return callSammoAPI<ResultType, ErrorType>('patch', [], args, undefined, true);
} }
+17
View File
@@ -63,6 +63,7 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"jquery": "^3.6.0", "jquery": "^3.6.0",
"js-sha512": "^0.8.0", "js-sha512": "^0.8.0",
"ky": "^0.30.0",
"linkifyjs": "^3.0", "linkifyjs": "^3.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
@@ -6417,6 +6418,17 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/ky": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/ky/-/ky-0.30.0.tgz",
"integrity": "sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sindresorhus/ky?sponsor=1"
}
},
"node_modules/levn": { "node_modules/levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -15122,6 +15134,11 @@
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
"integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ=="
}, },
"ky": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/ky/-/ky-0.30.0.tgz",
"integrity": "sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog=="
},
"levn": { "levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+1
View File
@@ -76,6 +76,7 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"jquery": "^3.6.0", "jquery": "^3.6.0",
"js-sha512": "^0.8.0", "js-sha512": "^0.8.0",
"ky": "^0.30.0",
"linkifyjs": "^3.0", "linkifyjs": "^3.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
+12 -20
View File
@@ -21,41 +21,33 @@ class APIHelper
die(); die();
} }
public static function launch(string $rootPath, ?string $actionPath = null) public static function launch(string $rootPath, string $actionPath, array $eParams = [], bool $loadRawInput = true)
{ {
//TODO: path를 php://input에서 받는게 아니라 api.php?~~~~~ 로 받아오는것이 etag 캐시 측면에서 훨씬 나을 듯! if($loadRawInput){
try { try {
$rawInput = file_get_contents('php://input'); $rawInput = file_get_contents('php://input');
$input = Json::decode($rawInput); $input = Json::decode($rawInput);
} catch (\Exception $e) { } catch (\Exception $e) {
Json::dieWithReason($e->getMessage()); Json::dieWithReason($e->getMessage());
$input = null;
}
}
else{
$input = null;
} }
if ($actionPath !== null) { if(!$actionPath){
Json::dieWithReason('path가 지정되지 않았습니다.');
}
if ($input && !is_array($input)) { if ($input && !is_array($input)) {
Json::dieWithReason('args가 array가 아닙니다.' . gettype($input)); Json::dieWithReason('args가 array가 아닙니다.' . gettype($input));
} }
if (!$input) { if (!$input) {
$input = []; $input = [];
} }
$actionArgs = $input;
} else {
if (!$input) {
Json::dieWithReason("input이 비어있습니다. {$rawInput}");
}
if (!key_exists('path', $input)) {
Json::dieWithReason('path가 지정되지 않았습니다.');
}
$actionPath = $input['path'];
if (key_exists('args', $input) && !is_array($input['args'])) {
Json::dieWithReason('args가 array가 아닙니다.' . gettype($input['args']));
}
$actionArgs = $input['args'] ?? null;
}
//NOTE: array_merge([], {})의 상황이 가능함.
$actionArgs = array_merge($input, $eParams);
try { try {
$obj = buildAPIExecutorClass($actionPath, $rootPath, $actionArgs); $obj = buildAPIExecutorClass($actionPath, $rootPath, $actionArgs);