Skip to content

Commit

Permalink
Merge pull request #108 from sima-land/38-examples-bun
Browse files Browse the repository at this point in the history
Шаг 61 #38
  • Loading branch information
krutoo committed Mar 7, 2024
2 parents 460d17c + 822d6da commit 32fba0c
Show file tree
Hide file tree
Showing 30 changed files with 693 additions and 646 deletions.
5 changes: 5 additions & 0 deletions src/preset/bun-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import { providePageRender } from '../node/handler/providers';
import { SpecificExtras } from '../node/handler/utils';
import { HandlerProviders } from './providers';

/**
* Возвращает preset с зависимостями для формирования обработчика входящего http-запроса.
* @param customize Получит функцию с помощью которой можно переопределить предустановленные провайдеры.
* @return Preset.
*/
export function PresetBunHandler(customize?: PresetTuner) {
const preset = createPreset();

Expand Down
3 changes: 2 additions & 1 deletion src/preset/bun-handler/providers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import {
getFetchExtraAborting,
getFetchLogging,
} from '../../isomorphic/utils';
import { getForwardedHeaders, getPageResponseFormat } from '../../server/utils';
import { PageAssets } from '../../isomorphic/types';
import { RESPONSE_EVENT_TYPE } from '../../isomorphic/constants';
import { getPageResponseFormat } from '../../server/utils/get-page-response-format';
import { getForwardedHeaders } from '../../server/utils/get-forwarded-headers';

export const HandlerProviders = {
handlerMain(resolve: Resolve) {
Expand Down
8 changes: 6 additions & 2 deletions src/preset/bun/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */
import { createPreset } from '../../di';
import { KnownToken } from '../../tokens';
import { PresetTuner } from '../isomorphic';
import { provideBaseConfig } from '../isomorphic/providers';
import { provideKnownHttpApiHosts, provideSsrBridgeServerSide } from '../node/node/providers';
import { BunProviders } from './providers';

// @todo возможно стоит переименовать в PresetServer (так как в теории это можно использовать не только в Bun но и в Deno, Node.js)
/**
* Возвращает preset с зависимостями для запуска приложения в Bun.
* @param customize Получит функцию с помощью которой можно переопределить предустановленные провайдеры.
* @return Preset.
*/
export function PresetBun(customize?: PresetTuner) {
// @todo возможно стоит переименовать в PresetServer (так как в теории это можно использовать не только в Bun но и в Deno, Node.js)
const preset = createPreset();

// config
Expand Down
16 changes: 7 additions & 9 deletions src/preset/bun/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import { getCurrentHub, init, runWithAsyncContext } from '@sentry/bun';
import { createSentryHandler } from '../../../log/handler/sentry';
import { provideFetch } from '../../isomorphic/providers';
import { ServerHandler, ServerMiddleware } from '../../server/types';
import {
applyServerMiddleware,
healthCheck,
getServeLogging,
getServeErrorLogging,
getServeMeasuring,
} from '../../server/utils';
import PromClient from 'prom-client';
import { statsHandler } from '../utils';
import { getHealthCheck } from '../../server/utils/get-health-check';
import { getServeLogging } from '../../server/utils/get-serve-logging';
import { getServeErrorLogging } from '../../server/utils/get-serve-error-logging';
import { getServeMeasuring } from '../../server/utils/get-serve-measuring';
import { applyServerMiddleware } from '../../server/utils/apply-server-middleware';
import PromClient from 'prom-client';

export const BunProviders = {
configSource(): ConfigSource {
Expand Down Expand Up @@ -88,7 +86,7 @@ export const BunProviders = {
serviceRoutes(): Array<[string, ServerHandler]> {
return [
// служебные маршруты (без промежуточных слоев)
['/healthcheck', healthCheck()],
['/healthcheck', getHealthCheck()],
['/stats', statsHandler()],
];
},
Expand Down
24 changes: 24 additions & 0 deletions src/preset/isomorphic/utils/__test__/hide-first-id.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { hideFirstId } from '../hide-first-id';

describe('hideFirstId', () => {
const cases = [
{
input: '/api/v2/something/123456/some-bff/123456',
output: ['/api/v2/something/{id}/some-bff/123456', 123456],
},
{
input: '/api/v2/something/222/some-bff/333',
output: ['/api/v2/something/{id}/some-bff/333', 222],
},
{
input: '/api/v2/something/45320/some-bff',
output: ['/api/v2/something/{id}/some-bff', 45320],
},
];

it('should replace first id only', () => {
cases.forEach(({ input, output }) => {
expect(hideFirstId(input)).toEqual(output);
});
});
});
14 changes: 14 additions & 0 deletions src/preset/isomorphic/utils/hide-first-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Преобразует строку вида:
* "/api/v2/something/123456/some-bff/123456"
* в строку вида:
* "/api/v2/something/{id}/some-bff/123456"
* и возвращает кортеж с этой строкой и вырезанным числом в случае если оно найдено.
* @param url Url.
* @return Кортеж со строкой и результатом поиска числа.
*/
export function hideFirstId(url: string): [string, number | undefined] {
const found = /\d{2,}/.exec(url);

return found ? [url.replace(found[0], '{id}'), Number(found[0])] : [url, undefined];
}
2 changes: 1 addition & 1 deletion src/preset/node/handler/providers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type { ConventionalJson } from '../../../isomorphic/types';
import { Fragment } from 'react';
import { HelmetContext, RegularHelmet, getPageResponseFormat } from '../utils';
import { renderToString } from 'react-dom/server';
import { getFetchTracing } from '../../../server/utils';
import { getFetchTracing } from '../../../server/utils/get-fetch-tracing';

/**
* Провайдер главной функции обработчика входящего http-запроса.
Expand Down
31 changes: 4 additions & 27 deletions src/preset/node/node/utils/http-client/__test__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Context } from '@opentelemetry/api';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { Span, Tracer } from '@opentelemetry/sdk-trace-base';
import { axiosTracingMiddleware, getRequestInfo, hideFirstId, getForwardedHeaders } from '..';
import { axiosTracingMiddleware, getRequestInfo, getForwardedHeaders } from '..';
import { BaseConfig } from '../../../../../../config';
import { Request } from 'express';
import express from 'express';

describe('axiosTracingMiddleware', () => {
it('should handle success response', async () => {
Expand Down Expand Up @@ -144,29 +144,6 @@ describe('getRequestInfo', () => {
});
});

describe('hideFirstId', () => {
const cases = [
{
input: '/api/v2/something/123456/some-bff/123456',
output: ['/api/v2/something/{id}/some-bff/123456', 123456],
},
{
input: '/api/v2/something/222/some-bff/333',
output: ['/api/v2/something/{id}/some-bff/333', 222],
},
{
input: '/api/v2/something/45320/some-bff',
output: ['/api/v2/something/{id}/some-bff', 45320],
},
];

it('should replace first id only', () => {
cases.forEach(({ input, output }) => {
expect(hideFirstId(input)).toEqual(output);
});
});
});

describe('getRequestHeaders', () => {
it('should return headers', () => {
const config: BaseConfig = {
Expand All @@ -175,7 +152,7 @@ describe('getRequestHeaders', () => {
env: 'test',
};

const request: Request = {
const request: express.Request = {
socket: {
remoteAddress: '127.0.0.1',
},
Expand Down Expand Up @@ -210,7 +187,7 @@ describe('getRequestHeaders', () => {
env: 'test',
};

const request: Request = {
const request: express.Request = {
socket: {
remoteAddress: undefined,
},
Expand Down
16 changes: 1 addition & 15 deletions src/preset/node/node/utils/http-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { BaseConfig } from '../../../../../config';
import { getClientIp } from '../http-server';
import { displayUrl } from '../../../../isomorphic/utils';
import { hideFirstId } from '../../../../isomorphic/utils/hide-first-id';

/**
* Возвращает новый middleware для трассировки исходящих запросов.
Expand Down Expand Up @@ -80,21 +81,6 @@ export function getRequestInfo(
};
}

/**
* Преобразует строку вида:
* "/api/v2/something/123456/some-bff/123456"
* в строку вида:
* "/api/v2/something/{id}/some-bff/123456"
* и возвращает кортеж с этой строкой и вырезанным числом в случае если оно найдено.
* @param url Url.
* @return Кортеж со строкой и результатом поиска числа.
*/
export function hideFirstId(url: string): [string, number | undefined] {
const found = /\d{2,}/.exec(url);

return found ? [url.replace(found[0], '{id}'), Number(found[0])] : [url, undefined];
}

/**
* Формирует заголовки для исходящих запросов с сервера по соглашению.
* @param config Конфиг.
Expand Down
5 changes: 4 additions & 1 deletion src/preset/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export type { ServerHandler, ServerMiddleware, ServerHandlerContext } from './types';
export { getClientIp, getForwardedHeaders, getPageResponseFormat, healthCheck } from './utils';
export { getClientIp } from './utils/get-client-ip';
export { getForwardedHeaders } from './utils/get-forwarded-headers';
export { getHealthCheck } from './utils/get-health-check';
export { getPageResponseFormat } from './utils/get-page-response-format';
49 changes: 49 additions & 0 deletions src/preset/server/utils/__test__/apply-server-middleware.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ServerMiddleware } from '../../types';
import { applyServerMiddleware } from '../apply-server-middleware';

describe('applyServerMiddleware', () => {
it('should compose middleware', async () => {
const log: string[] = [];

const foo: ServerMiddleware = async (request, next) => {
log.push('<foo>');
const result = await next(request);
log.push('</foo>');
return result;
};

const bar: ServerMiddleware = async (request, next) => {
log.push('<bar>');
const result = await next(request);
log.push('</bar>');
return result;
};

const baz: ServerMiddleware = async (request, next) => {
log.push('<baz>');
const result = await next(request);
log.push('</baz>');
return result;
};

const enhancer = applyServerMiddleware(foo, bar, baz);

const handler = enhancer(() => {
log.push('<handler />');
return new Response('Test');
});

await handler(new Request('http://test.com'), { events: new EventTarget() });

expect(log).toEqual([
// expected log
'<foo>',
'<bar>',
'<baz>',
'<handler />',
'</baz>',
'</bar>',
'</foo>',
]);
});
});
23 changes: 23 additions & 0 deletions src/preset/server/utils/__test__/get-client-ip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getClientIp } from '../get-client-ip';

describe('getClientIp', () => {
it('should handle "x-client-ip" header', () => {
const request = new Request('http://test.com', {
headers: {
'x-client-ip': '127.1.2.3',
},
});

expect(getClientIp(request)).toBe('127.1.2.3');
});

it('should handle "x-forwarded-for" header', () => {
const request = new Request('http://test.com', {
headers: {
'x-forwarded-for': '111.222.111.222',
},
});

expect(getClientIp(request)).toBe('111.222.111.222');
});
});
41 changes: 41 additions & 0 deletions src/preset/server/utils/__test__/get-fetch-tracing.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getFetchTracing } from '../get-fetch-tracing';

describe('getFetchTracing', () => {
it('should trace fetch stages', async () => {
const tracer: any = {
startSpan: jest.fn(() => ({
setAttributes: jest.fn(),
setStatus: jest.fn(),
end: jest.fn(),
})),
};
const context: any = {};
const middleware = getFetchTracing(tracer, context);

const request = new Request('http://test.com/api/v2/product/1005002');

expect(
await Promise.resolve(
middleware(request, () => Promise.resolve<Response>(new Response('OK'))),
).catch(() => 'catch!'),
).not.toBe('catch!');
});

it('should handle error', async () => {
const tracer: any = {
startSpan: jest.fn(() => ({
setAttributes: jest.fn(),
setStatus: jest.fn(),
end: jest.fn(),
})),
};
const context: any = {};
const middleware = getFetchTracing(tracer, context);

const request = new Request('http://test.com/api/v2/product/1005002');

expect(
await Promise.resolve(middleware(request, () => Promise.reject('FAKE ERROR'))).catch(e => e),
).toBe('FAKE ERROR');
});
});
38 changes: 38 additions & 0 deletions src/preset/server/utils/__test__/get-forwarded-headers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getForwardedHeaders } from '../get-forwarded-headers';

describe('getForwardedHeaders', () => {
it('should contain headers by convention', () => {
const config = { appName: 'app_name', appVersion: 'version', env: 'env' };
const request = new Request('http://test.com', {
headers: {
'x-client-ip': '127.0.0.89',
'simaland-foo': 'hello',
'simaland-bar': 'world',
'simaland-params': JSON.stringify({ testParam: 123 }),
},
});
const result = getForwardedHeaders(config, request);

expect(result.get('user-agent')).toBe('simaland-app_name/version');
expect(result.get('x-client-ip')).toBe('127.0.0.89');
expect(result.get('simaland-foo')).toBe('hello');
expect(result.get('simaland-bar')).toBe('world');
expect(result.get('simaland-params')).toBe(null);
});

it('should not contain x-client-ip when client ip is not defined', () => {
const config = { appName: 'app_name', appVersion: 'version', env: 'env' };
const request = new Request('http://test.com', {
headers: {
'simaland-foo': 'hello',
'simaland-bar': 'world',
},
});
const result = getForwardedHeaders(config, request);

expect(result.get('user-agent')).toBe('simaland-app_name/version');
expect(result.get('x-client-ip')).toBe(null);
expect(result.get('simaland-foo')).toBe('hello');
expect(result.get('simaland-bar')).toBe('world');
});
});
Loading

0 comments on commit 32fba0c

Please sign in to comment.