Add api module

This commit is contained in:
Vasili Karaev 2021-08-04 20:40:14 +03:00
parent b8e39699bb
commit 90e227c794
10 changed files with 174 additions and 0 deletions

88
api/ApiMethodFactory.ts Normal file
View File

@ -0,0 +1,88 @@
import Query from 'qs'
import * as R from 'ramda'
import { sprintf } from 'sprintf-js'
import request from './request'
import HttpMethod from './HttpMethod'
class ApiMethodFactory {
private readonly apiPrefix: string
constructor(apiPrefix: string) {
this.apiPrefix = apiPrefix
}
private makePath = <T>(data: T, pathKeys: string[]) => (template: string): string => {
const prefixedTemplate = `${this.apiPrefix}${template}`
if (R.isEmpty(pathKeys)) {
return prefixedTemplate
}
const pathData = R.pick(pathKeys, data)
if (R.isEmpty(pathData)) {
throw Error('api: empty path data')
}
return sprintf(prefixedTemplate, pathData)
}
private makeEndpoint = <T>(
template: string,
data: T,
pathKeys: string[],
queryKeys: string[],
): string => {
const make = R.compose(
this.addQuery(data, queryKeys),
this.makePath(data, pathKeys),
)
return make(template)
}
private addQuery = <T>(data: T, queryKeys: string[]) => (path: string): string => {
if (R.isEmpty(queryKeys)) {
return path
}
const queryData = R.pick(queryKeys, data)
if (R.isEmpty(queryData)) {
throw Error('api: empty query data')
}
const query = Query.stringify(queryData)
return `${path}?${query}`
}
make = <R, T = null>(
template: string,
method: HttpMethod = HttpMethod.GET, {
path: pathKeys = [],
query: queryKeys = [],
}: { path?: string[], query?: string[] } = {},
) => async (data: Nullable<T> = null): Promise<R> => {
const getBody = R.pipe(
R.ifElse(
R.isNil,
R.always(null),
R.omit(R.concat(pathKeys, queryKeys)),
),
R.when(R.isEmpty, R.always(null)),
R.unless(R.isNil, JSON.stringify),
)
const body = getBody(data)
const endpoint = this.makeEndpoint(template, data, pathKeys, queryKeys)
return await request({
method: method,
url: endpoint,
data: body,
})
}
}
export default ApiMethodFactory

9
api/HttpMethod.ts Normal file
View File

@ -0,0 +1,9 @@
enum HttpMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
DELETE = 'DELETE'
}
export default HttpMethod

4
api/error.ts Normal file
View File

@ -0,0 +1,4 @@
export interface ApiError {
errorCode: number
errorMessage: Nullable<string>
}

2
api/index.ts Normal file
View File

@ -0,0 +1,2 @@
export { default as HttpMethod } from './HttpMethod'
export { default as ApiMethodFactory } from './ApiMethodFactory'

28
api/request.ts Normal file
View File

@ -0,0 +1,28 @@
import axios, { AxiosRequestConfig } from 'axios'
import logger from 'lib/logger'
const retrieve = async (
props: AxiosRequestConfig,
hasRetriedAfterAuthentication = false,
): Promise<any> => {
try {
const { data } = await axios(props)
return data
} catch (err) {
if (err?.hasAuthenticated && !hasRetriedAfterAuthentication) {
return retrieve(props, true)
}
throw new Error(err)
}
}
const request = (props: AxiosRequestConfig, { throwOnError = true } = {}) => {
logger.debug(props, `throwOnError: ${throwOnError}`)
return retrieve(props)
}
export default request

1
fn/index.ts Normal file
View File

@ -0,0 +1 @@
export { thread } from './thread'

12
fn/thread.ts Normal file
View File

@ -0,0 +1,12 @@
import * as R from 'ramda'
type Transformer = (value: any) => any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pipe = R.pipe as unknown as (...fs: Transformer[]) => (value: any) => any
export const thread = (value: any, ...fs: Transformer[]) => {
const transform = pipe(...fs)
return transform(value)
}

3
global.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
type Nullable<T> = T | null | undefined
type UnknownRecord = Record<string, unknown>

13
logger/index.ts Normal file
View File

@ -0,0 +1,13 @@
import loglevel from 'loglevel'
const logger = loglevel.getLogger('default')
logger.setLevel(process.env.NODE_ENV === 'production' ? 'WARN' : 'DEBUG')
export const pipelog = (...args: unknown[]) => (value: unknown) => {
logger.debug(...args, value)
return value
}
export default logger

14
package.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "lib",
"version": "0.1.0",
"dependencies": {
"@types/qs": "^6.9.7",
"@types/ramda": "^0.27.44",
"@types/sprintf-js": "^1.1.2",
"axios": "^0.21.1",
"loglevel": "^1.7.1",
"qs": "^6.10.1",
"ramda": "^0.27.1",
"sprintf-js": "^1.1.2"
}
}