Добавление функции враппера для api объектов, доработка http объекта (#36)
This commit is contained in:
@ -55,6 +55,7 @@
|
||||
"class-methods-use-this": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"react/jsx-fragments": 0,
|
||||
"react/jsx-key": "warn",
|
||||
"react/no-array-index-key": 0,
|
||||
"react/destructuring-assignment": 0,
|
||||
"no-console": [
|
||||
@ -95,6 +96,7 @@
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error"],
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"array-bracket-spacing": ["warn", "never"],
|
||||
"block-spacing": ["warn", "never"],
|
||||
"brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
import {makeStorageApi} from './StorageApi';
|
||||
|
||||
type Region = {
|
||||
name: string;
|
||||
subject_number: number;
|
||||
gibdd_codes: Array<number>;
|
||||
};
|
||||
|
||||
type ResponseRegions = {
|
||||
regions: Array<Region>;
|
||||
}
|
||||
|
||||
const api = makeStorageApi<ResponseRegions>({
|
||||
key: 'russian_regions',
|
||||
hook: '26502372-6bc4-4cdf-bbcc-41b3b71cb386',
|
||||
description: 'Регионы России',
|
||||
service_name: 'geo_services',
|
||||
});
|
||||
|
||||
export const regionsApi = {
|
||||
request: async (): Promise<Region[]> => {
|
||||
const {value: {regions}} = await api.request();
|
||||
return regions;
|
||||
},
|
||||
find: async (name: string): Promise<Undefinable<Region>> => {
|
||||
const regions = await regionsApi.request();
|
||||
return regions.find(region => region.name === name);
|
||||
},
|
||||
create: async (newRegion: Region): Promise<Region> => {
|
||||
const regions = await regionsApi.request();
|
||||
const findedRegion = regions.find(region => region.name === newRegion.name);
|
||||
if (findedRegion) {
|
||||
throw new Error(`Город с именем "${newRegion.name}" уже существует`);
|
||||
}
|
||||
await api.update({
|
||||
regions: [
|
||||
...regions,
|
||||
newRegion,
|
||||
],
|
||||
});
|
||||
return newRegion;
|
||||
},
|
||||
update: async (updatedRegion: Region): Promise<Region> => {
|
||||
const regions = await regionsApi.request();
|
||||
const findedIndex = regions.findIndex(region => region.name === updatedRegion.name);
|
||||
if (findedIndex === -1) {
|
||||
throw new Error(`Город с именем "${updatedRegion.name}" не найден`);
|
||||
}
|
||||
await api.update({
|
||||
regions: regions.map((region, index) => {
|
||||
if (findedIndex === index) {
|
||||
return updatedRegion;
|
||||
}
|
||||
return region;
|
||||
}),
|
||||
});
|
||||
return updatedRegion;
|
||||
},
|
||||
remove: async (name: string): Promise<string> => {
|
||||
const regions = await regionsApi.request();
|
||||
const findedIndex = regions.findIndex(region => region.name === name);
|
||||
if (findedIndex === -1) {
|
||||
throw new Error(`Город с именем "${name}" не найден`);
|
||||
}
|
||||
await api.update({
|
||||
regions: regions.filter(region => region.name === name),
|
||||
});
|
||||
return name;
|
||||
}
|
||||
};
|
||||
@ -1,44 +0,0 @@
|
||||
import {http} from '../infrastructure/Http';
|
||||
|
||||
type QueryRequest = {
|
||||
hook: string;
|
||||
}
|
||||
|
||||
type ResponseData<T> = {
|
||||
key: string;
|
||||
value: T;
|
||||
description: string;
|
||||
service_name: string;
|
||||
author: string;
|
||||
};
|
||||
|
||||
type RequestData<T> = Omit<ResponseData<T>, 'author'>;
|
||||
|
||||
type ApiConfig<T> = Omit<ResponseData<T>, 'value' | 'author'> & {
|
||||
hook: string;
|
||||
};
|
||||
|
||||
type Api<T> = {
|
||||
request: () => Promise<ResponseData<T>>;
|
||||
update: (updateValue: T) => Promise<ResponseData<T>>;
|
||||
};
|
||||
|
||||
const ROOT_URL = 'https://api.storage.vigdorov.ru/store';
|
||||
|
||||
export const makeStorageApi = <T>({key, hook, ...body}: ApiConfig<T>): Api<T> => {
|
||||
const config = {params: {hook}};
|
||||
return {
|
||||
request: async (): Promise<ResponseData<T>> => {
|
||||
const {data} = await http.get<QueryRequest, ResponseData<T>>(`${ROOT_URL}/${key}`, config);
|
||||
return data;
|
||||
},
|
||||
update: async (updateValue: T): Promise<ResponseData<T>> => {
|
||||
const {data} = await http.put<QueryRequest, RequestData<T>, ResponseData<T>>(ROOT_URL, {
|
||||
...body,
|
||||
key,
|
||||
value: updateValue,
|
||||
}, config);
|
||||
return data;
|
||||
},
|
||||
};
|
||||
};
|
||||
29
src/core/api/usersTestApi.ts
Normal file
29
src/core/api/usersTestApi.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import {http} from '../infrastructure/Http';
|
||||
import {makeApi} from '../utils/makeApi';
|
||||
|
||||
type User = {
|
||||
id: number;
|
||||
avatar: string;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
};
|
||||
|
||||
type UserReponse = {
|
||||
data: Array<User>;
|
||||
page: number;
|
||||
per_page: number;
|
||||
support: {
|
||||
text: string;
|
||||
url: string;
|
||||
}
|
||||
total: number;
|
||||
total_pages: number;
|
||||
};
|
||||
|
||||
export const usersApi = makeApi({
|
||||
request: async () => {
|
||||
const {data} = await http.get<void, UserReponse>('https://reqres.in/api/users');
|
||||
return data;
|
||||
},
|
||||
});
|
||||
@ -1,53 +1,51 @@
|
||||
import axios, {AxiosRequestConfig, AxiosResponse} from 'axios';
|
||||
import axios, {AxiosRequestConfig} from 'axios';
|
||||
|
||||
type RequestConfig<Q> = Omit<AxiosRequestConfig, 'params'> & {
|
||||
params: Q;
|
||||
enum Method {
|
||||
Get = 'get',
|
||||
Delete = 'delete',
|
||||
Head = 'head',
|
||||
Options = 'options',
|
||||
Post = 'post',
|
||||
Put = 'put',
|
||||
Patch = 'patch',
|
||||
}
|
||||
|
||||
type RequestConfig<Q, B> = Omit<AxiosRequestConfig, 'params' | 'data'> & {
|
||||
params?: Q;
|
||||
data?: B;
|
||||
};
|
||||
|
||||
const requestMiddleware = async <Q, B, R>(config: RequestConfig<Q, B>): Promise<R> => {
|
||||
const axiosResponse = await axios.request<R>(config);
|
||||
// Добавить обработку ошибок
|
||||
return axiosResponse.data;
|
||||
};
|
||||
|
||||
const request = <Q, B, R>(config: RequestConfig<Q, B>) => requestMiddleware<Q, B, R>(config);
|
||||
|
||||
const requestWithoutBody = (method: Method) => <Q, R>(url: string, query?: Q) => {
|
||||
return request<Q, never, R>({
|
||||
method,
|
||||
url,
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
const requestWithBody = (method: Method) => <Q, B, R>(url: string, query?: Q, body?: B) => {
|
||||
return request<Q, B, R>({
|
||||
method,
|
||||
url,
|
||||
params: query,
|
||||
data: body,
|
||||
});
|
||||
};
|
||||
|
||||
export const http = {
|
||||
get: <Query, Response>(
|
||||
url: string,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.get<Response>(url, config);
|
||||
},
|
||||
delete: <Query, Response>(
|
||||
url: string,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.delete<Response>(url, config);
|
||||
},
|
||||
head: <Query, Response>(
|
||||
url: string,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.head<Response>(url, config);
|
||||
},
|
||||
options: <Query, Response>(
|
||||
url: string,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.options<Response>(url, config);
|
||||
},
|
||||
post: <Query, Body, Response>(
|
||||
url: string,
|
||||
body: Body,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.post<Response>(url, body, config);
|
||||
},
|
||||
put: <Query, Body, Response>(
|
||||
url: string,
|
||||
body: Body,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.post<Response>(url, body, config);
|
||||
},
|
||||
patch: <Query, Body, Response>(
|
||||
url: string,
|
||||
body: Body,
|
||||
config: RequestConfig<Query>
|
||||
): Promise<AxiosResponse<Response>> => {
|
||||
return axios.post<Response>(url, body, config);
|
||||
},
|
||||
get: requestWithoutBody(Method.Get),
|
||||
delete: requestWithoutBody(Method.Delete),
|
||||
head: requestWithoutBody(Method.Head),
|
||||
options: requestWithoutBody(Method.Options),
|
||||
post: requestWithBody(Method.Post),
|
||||
put: requestWithBody(Method.Put),
|
||||
patch: requestWithBody(Method.Patch),
|
||||
};
|
||||
|
||||
20
src/core/utils/makeApi.ts
Normal file
20
src/core/utils/makeApi.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {fromPromise} from '@most/core';
|
||||
import {Stream} from '@most/types';
|
||||
import {objectEntries} from './objectEntries';
|
||||
|
||||
type PromiseApi = Record<string, (...args: any[]) => Promise<unknown>>;
|
||||
|
||||
type StreamApi<T extends PromiseApi> = {
|
||||
[K in keyof T]: (...params: Parameters<T[K]>) => (
|
||||
T[K] extends (...args: any[]) => Promise<infer R> ? Stream<R> : never
|
||||
);
|
||||
};
|
||||
|
||||
export const makeApi = <T extends PromiseApi>(apiObj: T) => {
|
||||
return objectEntries(apiObj).reduce((streamObj, [apiKey, apiMethod]) => {
|
||||
return {
|
||||
...streamObj,
|
||||
[apiKey]: (...args: Parameters<typeof apiMethod>) => fromPromise(apiMethod(...args)),
|
||||
};
|
||||
}, {} as StreamApi<T>);
|
||||
};
|
||||
@ -1,8 +1,18 @@
|
||||
import React, {memo} from 'react';
|
||||
import {usersApi} from '../../../../core/api/usersTestApi';
|
||||
import {useStream} from '../../../../core/utils/useStream';
|
||||
|
||||
const userList$ = usersApi.request();
|
||||
|
||||
const Page: React.FC = () => {
|
||||
const users = useStream(userList$, []);
|
||||
return (
|
||||
<div>tags</div>
|
||||
<div>
|
||||
tags
|
||||
{users.map(user => (
|
||||
<div key={user.id}>{user.first_name}, {user.last_name}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user