From d884ac9d27540860dd66d9f4ef7debe881caa0c4 Mon Sep 17 00:00:00 2001 From: krutoo Date: Fri, 29 Mar 2024 18:10:23 +0500 Subject: [PATCH] #38 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tokens: добавлены токены для express-приложения, промежуточных слоев и маршрутов (minor) - preset/node: добавлены компоненты express-приложения, промежуточных слоев и маршрутов (minor) - docs: доработки по утилитам для redux (patch) - examples: учтены новые компоненты пресета (patch) --- docs/docs/utils/redux.md | 10 ++++- examples/node/src/app/index.ts | 44 ++++++------------- examples/node/src/index.ts | 2 +- examples/node/src/tokens.ts | 3 +- src/preset/node/index.ts | 19 +++++++- src/preset/node/providers/main-express-app.ts | 31 +++++++++++++ ...ics-http-app.ts => metrics-express-app.ts} | 2 +- src/tokens.ts | 19 +++++++- 8 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 src/preset/node/providers/main-express-app.ts rename src/preset/node/providers/{metrics-http-app.ts => metrics-express-app.ts} (88%) diff --git a/docs/docs/utils/redux.md b/docs/docs/utils/redux.md index 91e4db9..e696520 100644 --- a/docs/docs/utils/redux.md +++ b/docs/docs/utils/redux.md @@ -4,7 +4,7 @@ ### Удаленные данные -Для работы с удаленными данными пакет пердоставляет набор утилит `RemoteData`. +Для работы с загружаемыми данными пакет предоставляет набор утилит `RemoteData`. Пример использования вместе с `createAction` и `createReducer` из пакета `@reduxjs/toolkit`: @@ -40,10 +40,16 @@ const reducer = createReducer(initialState, builder => { RemoteData.applyReducers(actions, builder); }); +const selectors = { + // определяем базовые селекторы (данных, статуса, кол-ва загрузок и тд) + ...RemoteData.createSelectors(rootState => rootState.slice), +}; + export const MyData = { initialState, actions, reducer, + selectors, } as const; ``` @@ -76,4 +82,6 @@ export const MyData = createSlice({ ...RemoteData.createHandlers(), }, }); + +// ...использование RemoteData.createSelectors по аналогии с предыдущим примером ``` diff --git a/examples/node/src/app/index.ts b/examples/node/src/app/index.ts index c116407..d59984a 100644 --- a/examples/node/src/app/index.ts +++ b/examples/node/src/app/index.ts @@ -4,17 +4,21 @@ import { createApplication, Resolve } from '@sima-land/isomorph/di'; import { PresetNode, HandlerProvider } from '@sima-land/isomorph/preset/node'; import { AuthorsPageApp } from '../pages/authors'; import { PostsPageApp } from '../pages/posts'; -import express from 'express'; +import { Handler } from 'express'; export function MainApp() { const app = createApplication(); // используем пресет "node" с базовыми компонентами, такими как logger и тд - app.preset(PresetNode()); + app.preset( + PresetNode(({ override }) => { + // переопределяем провайдеры пресета + override(TOKEN.Lib.Express.pageRoutes, providePageRoutes); + }), + ); // добавляем в приложение собственные компоненты app.bind(TOKEN.config).toProvider(provideAppConfig); - app.bind(TOKEN.server).toProvider(provideHttpServer); app.bind(TOKEN.Pages.posts).toProvider(HandlerProvider(PostsPageApp)); app.bind(TOKEN.Pages.authors).toProvider(HandlerProvider(AuthorsPageApp)); @@ -36,31 +40,11 @@ function provideAppConfig(resolve: Resolve): AppConfig { }; } -function provideHttpServer(resolve: Resolve): express.Application { - const postsHandler = resolve(TOKEN.Pages.posts); - const authorsHandler = resolve(TOKEN.Pages.authors); - const healthCheckHandler = resolve(TOKEN.Lib.Express.Handlers.healthCheck); - - // промежуточные слои (express) доступные из пресета PresetNode - const requestHandle = resolve(TOKEN.Lib.Express.Middleware.request); - const logging = resolve(TOKEN.Lib.Express.Middleware.log); - const metrics = resolve(TOKEN.Lib.Express.Middleware.metrics); - const tracing = resolve(TOKEN.Lib.Express.Middleware.tracing); - const errorHandle = resolve(TOKEN.Lib.Express.Middleware.error); - - const app = express(); - - // регистрируем промежуточные слои - app.use(['/', '/users', '/posts'], [requestHandle, logging, metrics, tracing]); - - // регистрируем роуты - app.get('/', postsHandler); - app.get('/posts', postsHandler); - app.get('/authors', authorsHandler); - app.get('/healthcheck', healthCheckHandler); - - // регистрируем промежуточный слой обработки ошибок - app.use(['/', '/users', '/posts'], [errorHandle]); - - return app; +function providePageRoutes(resolve: Resolve): Array<[string, Handler]> { + // определяем маршруты страниц + return [ + ['/', resolve(TOKEN.Pages.posts)], + ['/posts', resolve(TOKEN.Pages.posts)], + ['/authors', resolve(TOKEN.Pages.authors)], + ]; } diff --git a/examples/node/src/index.ts b/examples/node/src/index.ts index 06b519e..784b940 100644 --- a/examples/node/src/index.ts +++ b/examples/node/src/index.ts @@ -2,7 +2,7 @@ import { MainApp } from './app'; import { TOKEN } from './tokens'; MainApp().invoke( - [TOKEN.config, TOKEN.Lib.logger, TOKEN.server, TOKEN.Lib.Metrics.httpApp], + [TOKEN.config, TOKEN.Lib.logger, TOKEN.Lib.Express.app, TOKEN.Lib.Metrics.expressApp], (config, logger, mainServer, metricsServer) => { mainServer.listen(config.http.ports.main, () => { logger.info(`Server started on port ${config.http.ports.main}`); diff --git a/examples/node/src/tokens.ts b/examples/node/src/tokens.ts index fd19847..79e03cd 100644 --- a/examples/node/src/tokens.ts +++ b/examples/node/src/tokens.ts @@ -3,7 +3,7 @@ import { KnownToken } from '@sima-land/isomorph/tokens'; // чтобы токены можно было использовать как в браузере так и на сервере импорты должны содержать только типы import type { AppConfig } from './app/types'; -import type { Application, Handler } from 'express'; +import type { Handler } from 'express'; import type { AuthorApi } from './entities/author'; import type { PostApi } from './entities/post'; @@ -13,7 +13,6 @@ export const TOKEN = { // далее идут токены компонентов нашего приложения config: createToken('config'), - server: createToken('http/server'), Pages: { posts: createToken('pages/posts'), authors: createToken('pages/authors'), diff --git a/src/preset/node/index.ts b/src/preset/node/index.ts index 98e115a..2849b5e 100644 --- a/src/preset/node/index.ts +++ b/src/preset/node/index.ts @@ -14,12 +14,13 @@ import { provideExpressRequestMiddleware } from './providers/express-request-mid import { provideExpressTracingMiddleware } from './providers/express-tracing-middleware'; import { provideKnownHttpApiHosts } from '../server/providers/known-http-api-hosts'; import { provideLogger } from './providers/logger'; -import { provideMetricsHttpApp } from './providers/metrics-http-app'; +import { provideMetricsExpressApp } from './providers/metrics-express-app'; import { provideSpanExporter } from './providers/span-exporter'; import { provideSsrBridgeServerSide } from '../server/providers/ssr-bridge-server-side'; import { provideTracer } from './providers/tracer'; import { provideTracerProvider } from './providers/tracer-provider'; import { provideTracerProviderResource } from './providers/tracer-provider-resource'; +import { provideMainExpressApp } from './providers/main-express-app'; /** * Возвращает preset с зависимостями по умолчанию для frontend-микросервисов на Node.js. @@ -44,7 +45,7 @@ export function PresetNode(customize?: PresetTuner): Preset { preset.set(KnownToken.Tracing.tracerProviderResource, provideTracerProviderResource); // metrics - preset.set(KnownToken.Metrics.httpApp, provideMetricsHttpApp); + preset.set(KnownToken.Metrics.expressApp, provideMetricsExpressApp); // fetch preset.set(KnownToken.Http.fetch, provideFetch); @@ -55,6 +56,20 @@ export function PresetNode(customize?: PresetTuner): Preset { preset.set(KnownToken.Axios.middleware, () => []); // express + preset.set(KnownToken.Express.app, provideMainExpressApp); + preset.set(KnownToken.Express.pageRoutes, () => []); + preset.set(KnownToken.Express.serviceRoutes, resolve => [ + ['/healthcheck', resolve(KnownToken.Express.Handlers.healthCheck)], + ]); + preset.set(KnownToken.Express.middleware, resolve => [ + resolve(KnownToken.Express.Middleware.request), + resolve(KnownToken.Express.Middleware.log), + resolve(KnownToken.Express.Middleware.metrics), + resolve(KnownToken.Express.Middleware.tracing), + ]); + preset.set(KnownToken.Express.endMiddleware, resolve => [ + resolve(KnownToken.Express.Middleware.error), + ]); preset.set(KnownToken.Express.factory, provideExpressFactory); preset.set(KnownToken.Express.Handlers.healthCheck, healthCheck); preset.set(KnownToken.Express.Middleware.request, provideExpressRequestMiddleware); diff --git a/src/preset/node/providers/main-express-app.ts b/src/preset/node/providers/main-express-app.ts new file mode 100644 index 0000000..078f3b7 --- /dev/null +++ b/src/preset/node/providers/main-express-app.ts @@ -0,0 +1,31 @@ +import express from 'express'; +import type { Resolve } from '../../../di'; +import { KnownToken } from '../../../tokens'; + +/** + * Провайдер основного express-приложения. + * @param resolve Функция для получения зависимости по токену. + * @return Основное express-приложение. + */ +export function provideMainExpressApp(resolve: Resolve): express.Application { + const pageRoutes = resolve(KnownToken.Express.pageRoutes); + const serviceRoutes = resolve(KnownToken.Express.serviceRoutes); + const middleware = resolve(KnownToken.Express.middleware); + const endMiddleware = resolve(KnownToken.Express.endMiddleware); + + const app = express(); + + // маршруты страниц + for (const [routePath, routeHandler] of pageRoutes) { + app.use(routePath, middleware); + app.get(routePath, routeHandler); + app.use(routePath, endMiddleware); + } + + // служебные маршруты (к ним не применяются промежуточные слои) + for (const [routePath, routeHandler] of serviceRoutes) { + app.get(routePath, routeHandler); + } + + return app; +} diff --git a/src/preset/node/providers/metrics-http-app.ts b/src/preset/node/providers/metrics-express-app.ts similarity index 88% rename from src/preset/node/providers/metrics-http-app.ts rename to src/preset/node/providers/metrics-express-app.ts index d2de490..8854724 100644 --- a/src/preset/node/providers/metrics-http-app.ts +++ b/src/preset/node/providers/metrics-express-app.ts @@ -5,7 +5,7 @@ import express from 'express'; * Провайдер express-приложения метрик. * @return Пул известных http-хостов. */ -export function provideMetricsHttpApp(): express.Application { +export function provideMetricsExpressApp(): express.Application { PromClient.collectDefaultMetrics(); const app = express(); diff --git a/src/tokens.ts b/src/tokens.ts index e7784b7..b084a6d 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -59,7 +59,7 @@ export const KnownToken = { /** Токены компонентов сбора метрик. */ Metrics: { - httpApp: createToken('metrics/http-app'), + expressApp: createToken('metrics/express-app'), httpHandler: createToken('metrics/http-handler'), }, @@ -173,6 +173,23 @@ export const KnownToken = { /** Токены компонентов для работы с библиотекой express. */ Express: { + /** Токен основного express-приложения. */ + app: createToken('express/app'), + + /** Токен списка маршрутов страниц. */ + pageRoutes: createToken>('express/page-routes'), + + /** Токен списка служебных маршрутов. */ + serviceRoutes: createToken>('express/service-routes'), + + /** Токен списка промежуточных слоев для публичных маршрутов. */ + middleware: + createToken>('express/middleware'), + + /** Токен списка промежуточных слоев для публичных маршрутов, которые подключаются после обработчика. */ + endMiddleware: + createToken>('express/end-middleware'), + /** Токен фабрики express-приложений. */ factory: createToken<() => express.Application>('express/factory'),