feat: инициализация API CodeGen
CLI утилита для генерации TypeScript API клиента из OpenAPI спецификации. - Поддержка локальных файлов и URL для спецификаций - Кастомизация имени выходного файла через флаг --name - Генерация типизированного клиента с SWR хуками - Минимальный вывод логов для лучшего UX
This commit is contained in:
140
src/templates/procedure-call.ejs
Normal file
140
src/templates/procedure-call.ejs
Normal file
@@ -0,0 +1,140 @@
|
||||
<%
|
||||
const { utils, route, config } = it;
|
||||
const { requestBodyInfo, responseBodyInfo, specificArgNameResolver } = route;
|
||||
const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils;
|
||||
const { parameters, path, method, payload, query, formData, security, requestParams } = route.request;
|
||||
const { type, errorType, contentTypes } = route.response;
|
||||
const { HTTP_CLIENT, RESERVED_REQ_PARAMS_ARG_NAMES } = config.constants;
|
||||
const routeDocs = includeFile("./route-docs", { config, route, utils });
|
||||
const queryName = (query && query.name) || "query";
|
||||
const pathParams = _.values(parameters);
|
||||
const pathParamsNames = _.map(pathParams, "name");
|
||||
|
||||
const isFetchTemplate = config.httpClientType === HTTP_CLIENT.FETCH;
|
||||
|
||||
const requestConfigParam = {
|
||||
name: specificArgNameResolver.resolve(RESERVED_REQ_PARAMS_ARG_NAMES),
|
||||
optional: true,
|
||||
type: "RequestParams",
|
||||
defaultValue: "{}",
|
||||
}
|
||||
|
||||
const argToTmpl = ({ name, optional, type, defaultValue }) => `${name}${!defaultValue && optional ? '?' : ''}: ${type}${defaultValue ? ` = ${defaultValue}` : ''}`;
|
||||
|
||||
const rawWrapperArgs = config.extractRequestParams ?
|
||||
_.compact([
|
||||
requestParams && {
|
||||
name: pathParams.length ? `{ ${_.join(pathParamsNames, ", ")}, ...${queryName} }` : queryName,
|
||||
optional: false,
|
||||
type: getInlineParseContent(requestParams),
|
||||
},
|
||||
...(!requestParams ? pathParams : []),
|
||||
payload,
|
||||
requestConfigParam,
|
||||
]) :
|
||||
_.compact([
|
||||
...pathParams,
|
||||
query,
|
||||
payload,
|
||||
requestConfigParam,
|
||||
])
|
||||
|
||||
const wrapperArgs = _
|
||||
// Sort by optionality
|
||||
.sortBy(rawWrapperArgs, [o => o.optional])
|
||||
.map(argToTmpl)
|
||||
.join(', ')
|
||||
|
||||
// RequestParams["type"]
|
||||
const requestContentKind = {
|
||||
"JSON": "ContentType.Json",
|
||||
"JSON_API": "ContentType.JsonApi",
|
||||
"URL_ENCODED": "ContentType.UrlEncoded",
|
||||
"FORM_DATA": "ContentType.FormData",
|
||||
"TEXT": "ContentType.Text",
|
||||
}
|
||||
// RequestParams["format"]
|
||||
const responseContentKind = {
|
||||
"JSON": '"json"',
|
||||
"IMAGE": '"blob"',
|
||||
"FORM_DATA": isFetchTemplate ? '"formData"' : '"document"'
|
||||
}
|
||||
|
||||
const bodyTmpl = _.get(payload, "name") || null;
|
||||
const queryTmpl = (query != null && queryName) || null;
|
||||
const bodyContentKindTmpl = requestContentKind[requestBodyInfo.contentKind] || null;
|
||||
const responseFormatTmpl = responseContentKind[responseBodyInfo.success && responseBodyInfo.success.schema && responseBodyInfo.success.schema.contentKind] || null;
|
||||
const securityTmpl = security ? 'true' : null;
|
||||
|
||||
const describeReturnType = () => {
|
||||
if (!config.toJS) return "";
|
||||
|
||||
switch(config.httpClientType) {
|
||||
case HTTP_CLIENT.AXIOS: {
|
||||
return `Promise<AxiosResponse<${type}>>`
|
||||
}
|
||||
default: {
|
||||
return `Promise<HttpResponse<${type}, ${errorType}>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isValidIdentifier = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
||||
|
||||
%>
|
||||
/**
|
||||
<%~ routeDocs.description %>
|
||||
|
||||
*<% /* Here you can add some other JSDoc tags */ %>
|
||||
|
||||
<%~ routeDocs.lines %>
|
||||
|
||||
*/
|
||||
<% if (isValidIdentifier(route.routeName.usage)) { %><%~ route.routeName.usage %><%~ route.namespace ? ': ' : ' = ' %><% } else { %>"<%~ route.routeName.usage %>"<%~ route.namespace ? ': ' : ' = ' %><% } %>(<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> =>
|
||||
<%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({
|
||||
path: `<%~ path %>`,
|
||||
method: '<%~ _.upperCase(method) %>',
|
||||
<%~ queryTmpl ? `query: ${queryTmpl},` : '' %>
|
||||
<%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %>
|
||||
<%~ securityTmpl ? `secure: ${securityTmpl},` : '' %>
|
||||
<%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %>
|
||||
<%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %>
|
||||
...<%~ _.get(requestConfigParam, "name") %>,
|
||||
})<%~ route.namespace ? ',' : '' %>
|
||||
<%
|
||||
// Генерируем use* функцию для GET запросов
|
||||
const isGetRequest = _.upperCase(method) === 'GET';
|
||||
if (isGetRequest) {
|
||||
const useMethodName = 'use' + _.upperFirst(route.routeName.usage);
|
||||
const argsWithoutParams = rawWrapperArgs.filter(arg => arg.name !== requestConfigParam.name);
|
||||
const useWrapperArgs = _
|
||||
.sortBy(argsWithoutParams, [o => o.optional])
|
||||
.map(argToTmpl)
|
||||
.join(', ');
|
||||
|
||||
// Определяем обязательные параметры для проверки
|
||||
const requiredArgs = argsWithoutParams.filter(arg => !arg.optional);
|
||||
const requiredArgsNames = requiredArgs.map(arg => {
|
||||
// Извлекаем имя из деструктуризации типа "{ id, ...query }"
|
||||
const match = arg.name.match(/^\{\s*([^,}]+)/);
|
||||
return match ? match[1].trim() : arg.name;
|
||||
});
|
||||
|
||||
// Генерируем условие для проверки всех обязательных параметров
|
||||
const hasRequiredArgs = requiredArgsNames.length > 0;
|
||||
const conditionCheck = hasRequiredArgs
|
||||
? requiredArgsNames.join(' && ')
|
||||
: 'true';
|
||||
%>
|
||||
|
||||
/**
|
||||
* SWR hook для <%~ route.routeName.usage %>
|
||||
<%~ routeDocs.lines %>
|
||||
*/
|
||||
<% if (isValidIdentifier(useMethodName)) { %><%~ useMethodName %><%~ route.namespace ? ': ' : ' = ' %><% } else { %>"<%~ useMethodName %>"<%~ route.namespace ? ': ' : ' = ' %><% } %>(<%~ useWrapperArgs %>) => {
|
||||
return useSWR<<%~ type %>>(
|
||||
<%~ conditionCheck %> ? `<%~ path %>` : null,
|
||||
fetcher
|
||||
);
|
||||
}<%~ route.namespace ? ',' : '' %>
|
||||
<% } %>
|
||||
Reference in New Issue
Block a user