Skip to content

Commit

Permalink
fix: migrate to shared eslint config and apply fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandermendes committed Sep 17, 2024
1 parent 273bf5f commit 02f7d00
Show file tree
Hide file tree
Showing 16 changed files with 435 additions and 950 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules/
coverage/
dist/
src/generated/
index.d.ts
6 changes: 4 additions & 2 deletions bin/build.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
const { ModuleKind } = require('typescript');
const {
getTypeScriptReader,
Expand All @@ -19,6 +20,7 @@ const { SRC_DIR, TEMPLATES_DIR } = require('./constants');
const { compileTs } = require('./compile');
const { getOapiSpecs } = require('./get-spec');

// eslint-disable-next-line import/no-dynamic-require
const pkg = require(`${appRoot.path}/package.json`);

const GENERATED_FILES_DIR = path.join(SRC_DIR, 'generated');
Expand Down Expand Up @@ -58,7 +60,7 @@ const formatJsonSchemaTitleAsType = (jsonSchemaTitle) => {
* Build up a reference to one of our types from a content schema.
*/
const getTypeReferenceFromJson = (content) => {
if (!content?.properties) {
if (!content || !content.properties) {
return 'undefined';
}

Expand Down Expand Up @@ -335,7 +337,7 @@ const validateOapiSpec = (oapiSpec, operations) => {
* Build an API client based on its OpenAPI spec.
*/
const buildClient = async (oapiSpec) => {
const { title } = oapiSpec?.info ?? {};
const { title } = (oapiSpec || {}).info || {};
const outDir = path.join(GENERATED_FILES_DIR, camelCase(title));
const types = await openapiTS(oapiSpec);
const jsonSchemaTypes = await convertTsToJsonSchema(types);
Expand Down
3 changes: 3 additions & 0 deletions bin/compile.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
const ts = require('typescript');

const compile = (fileNames, options) => {
Expand All @@ -14,10 +15,12 @@ const compile = (fileNames, options) => {
diagnostic.file,
diagnostic.start,
);

const message = ts.flattenDiagnosticMessageText(
diagnostic.messageText,
'\n',
);

console.log(
`${diagnostic.file.fileName} (${line + 1},${
character + 1
Expand Down
3 changes: 2 additions & 1 deletion bin/get-spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
const path = require('path');
const fse = require('fs-extra');
const yargs = require('yargs');
Expand Down Expand Up @@ -33,7 +34,7 @@ const fetchOapiSpec = async (url) => {
throw new Error(
`Failed to load API spec: ${
axios.isAxiosError(err)
? err.response?.status ?? err.code
? (err.response || {}).status || err.code
: 'Unknown error'
}`,
);
Expand Down
3 changes: 2 additions & 1 deletion bin/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env node
const { build } = require('./build.js');
const { build } = require('./build');

(async () => {
try {
await build();
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
}
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json .",
"lint": "eslint --ext .js,.ts .",
"test": "jest",
"typecheck": "tsc --noEmit",
"oac": "./bin/index.js"
Expand Down Expand Up @@ -55,13 +55,11 @@
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^29.0.0",
"@types/jsonwebtoken": "^8.5.8",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@typescript-eslint/parser": "^5.60.0",
"commitlint": "^8.3.5",
"eslint": "^8.57.0",
"husky": "^4.2.5",
"jest": "^29.7.0",
"jsonwebtoken": "^8.5.1",
"eslint": "^8.57.0",
"prettier": "^3.2.5",
"semantic-release": "^24.0.0",
"ts-jest": "^29.1.0",
Expand Down
4 changes: 2 additions & 2 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type TokenRetrieverFunction = () => AccessToken | Promise<AccessToken>;
*/
const getTokenProperty = (accessToken: string, key: keyof DecodedToken) => {
try {
return (decode(accessToken) as DecodedToken)[key];
return decode<DecodedToken>(accessToken)[key];
} catch (err) {
// Failed to decode token, ignore.
return null;
Expand Down Expand Up @@ -108,7 +108,7 @@ export const getAuthorizationHeader = async (
}

const noop = () => null;
let accessToken = await (getAccessToken || noop)();
let accessToken = await (getAccessToken ?? noop)();

if (!secure && (!accessToken || !isUserAdmin(accessToken))) {
return null;
Expand Down
4 changes: 2 additions & 2 deletions src/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import axios, { AxiosInstance, RawAxiosRequestHeaders } from 'axios';
import { noCase } from 'change-case';
import { createRequestFunction } from './request';
import {
createRefreshTokenInterceptor,
createEconnresetInterceptor,
createResponseDebugInterceptor,
createRefreshTokenInterceptor,
createRequestDebugInterceptor,
createResponseDebugInterceptor,
timeoutInterceptor,
} from './interceptors';
import { serializeQueryParams } from './query';
Expand Down
15 changes: 10 additions & 5 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
type ValidationError = {
export type ValidationError = {
property: string;
constraint: string;
message: string;
Expand Down Expand Up @@ -64,8 +64,10 @@ export class OpenApiClientError extends BaseOpenApiClientError {
/**
* Determines whether a value is (probably) an error thrown by the API client.
*/
export const isOpenApiClientError = (val: any): val is OpenApiClientError =>
!!val?.isOpenApiClientError;
export const isOpenApiClientError = (
value: unknown,
): value is OpenApiClientError =>
typeof value === 'object' && !!value && 'isOpenApiClientError' in value;

export class OpenApiClientTimeoutError extends BaseOpenApiClientError {
isOpenApiClientTimeoutError = true;
Expand All @@ -82,5 +84,8 @@ export class OpenApiClientTimeoutError extends BaseOpenApiClientError {
* Determines whether a value is (probably) a timeout error thrown by the API client.
*/
export const isOpenApiClientTimeoutError = (
val: any,
): val is OpenApiClientTimeoutError => !!val?.isOpenApiClientTimeoutError;
value: unknown,
): value is OpenApiClientTimeoutError =>
typeof value === 'object' &&
!!value &&
'isOpenApiClientTimeoutError' in value;
2 changes: 1 addition & 1 deletion src/interceptors/request-debug-interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ export const createRequestDebugInterceptor = (

return config;
},
error: (error: any) => Promise.reject(error),
error: (error: unknown) => Promise.reject(error),
});
44 changes: 29 additions & 15 deletions src/interceptors/response-debug-interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,53 @@
import { AxiosResponse } from 'axios';
import { OpenApiClientError, OpenApiClientTimeoutError } from '../errors';
import { AxiosError, AxiosResponse } from 'axios';
import {
OpenApiClientError,
OpenApiClientTimeoutError,
ValidationError,
} from '../errors';

type ResponseError = AxiosError<
{ message?: string; type?: string; errors?: ValidationError[] },
{ method: string; url: string; baseURL: string }
> & {
statusCode: number;
};

export const createResponseDebugInterceptor = (
onError?: (error: any) => void,
onClientError?: (error: any) => void,
onTimeoutError?: (error: any) => void,
onError?: (error: unknown) => void,
onClientError?: (error: unknown) => void,
onTimeoutError?: (error: unknown) => void,
) => ({
success: (res: AxiosResponse) => res,
error: (error: any) => {
error: (error: ResponseError) => {
const { config, response, message, statusCode, code } = error;

const { method } = config;
const { data, status } = response || {};
const { url, method, baseURL } = config ?? {};
const { data, status } = response ?? {};
const finalStatus = status ?? statusCode;

const isFiniteStatus = Number.isFinite(finalStatus);
const isServerError = isFiniteStatus && finalStatus >= 500;
const isClientError =
isFiniteStatus && finalStatus >= 400 && finalStatus < 500;

const isTimeoutError = code === 'ETIMEDOUT' || code === 'ECONNABORTED';

const endpoint = `/${config.url}`.replace(/\/\//g, '/');
const url = `${config.baseURL}${endpoint}`;
const endpoint = `/${url}`.replace(/\/\//g, '/');
const fullUrl = `${baseURL}${endpoint}`;
const msg = `${finalStatus} ${
data?.message || message
} <${method.toUpperCase()} ${url}>`;
data?.message ?? message
} <${method?.toUpperCase()} ${fullUrl}>`;

const openApiClientError = new OpenApiClientError(finalStatus, msg, {
type: data?.type,
errors: data?.errors,
});

const logError = onError || console.error;
const logClientError = onClientError || console.warn;
const logTimeoutError = onTimeoutError || console.warn;
/* eslint-disable no-console */
const logError = onError ?? console.error;
const logClientError = onClientError ?? console.warn;
const logTimeoutError = onTimeoutError ?? console.warn;
/* eslint-enable no-console */

if (isTimeoutError) {
// Axios returns the timeout as code NOT statusCode
Expand Down
2 changes: 1 addition & 1 deletion src/query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import qs from 'qs';

export const serializeQueryParams = (params: any) =>
export const serializeQueryParams = (params: unknown) =>
qs.stringify(params, {
encodeValuesOnly: true,
arrayFormat: 'brackets',
Expand Down
11 changes: 7 additions & 4 deletions src/request.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AxiosRequestConfig, AxiosInstance } from 'axios';
import { TokenRetrieverFunction, getAuthorizationHeader } from './auth';
import { AxiosInstance, AxiosRequestConfig } from 'axios';
import { getAuthorizationHeader, TokenRetrieverFunction } from './auth';
import { OperationConfig } from './operations';

type OperationOption = Record<string, unknown> | null;
Expand Down Expand Up @@ -57,7 +57,10 @@ export const createRequestFunction =
getAccessToken?: TokenRetrieverFunction,
refreshAccessToken?: TokenRetrieverFunction,
) =>
async (operationConfig: OperationConfig, options?: OperationOptions) => {
async <T>(
operationConfig: OperationConfig,
options?: OperationOptions,
): Promise<T> => {
const { endpoint, method, secure } = operationConfig;
const url = getFullPath(populateEndpoint(endpoint, options), basePath);
const axiosRequestConfig: AxiosRequestConfig = {
Expand Down Expand Up @@ -86,5 +89,5 @@ export const createRequestFunction =

const res = await axiosInstance.request(axiosRequestConfig);

return res.data;
return res.data as T;
};
4 changes: 2 additions & 2 deletions tests/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import axios from 'axios';
import { getRequestFunction } from '../src/create';
import { createRequestFunction } from '../src/request';
import {
createRefreshTokenInterceptor,
createEconnresetInterceptor,
createResponseDebugInterceptor,
createRefreshTokenInterceptor,
createRequestDebugInterceptor,
createResponseDebugInterceptor,
} from '../src/interceptors';
import { OpenApiClientOptions } from '../src/options';
import { serializeQueryParams } from '../src/query';
Expand Down
2 changes: 1 addition & 1 deletion tests/errors.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
isOpenApiClientError,
OpenApiClientError,
isOpenApiClientTimeoutError,
OpenApiClientError,
OpenApiClientTimeoutError,
OpenApiClientTimeoutErrorCode,
} from '../src/errors';
Expand Down
Loading

0 comments on commit 02f7d00

Please sign in to comment.