From a0e5c45db3460c3bcca7a7a858ed7ad75d4ecfcd Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Thu, 21 Mar 2024 16:51:13 +0800 Subject: [PATCH] Fix stream errors Trying to customize request objects trigger typescript errors. --- examples/basic/data-source.ts | 8 ++-- examples/basic/server.ts | 2 + src/backend/extenders/method.ts | 2 +- src/backend/extenders/url.ts | 2 +- src/backend/handlers.ts | 12 ++--- src/backend/server.ts | 80 +++++++++------------------------ 6 files changed, 37 insertions(+), 69 deletions(-) diff --git a/examples/basic/data-source.ts b/examples/basic/data-source.ts index 9c5ac5f..c4a9e69 100644 --- a/examples/basic/data-source.ts +++ b/examples/basic/data-source.ts @@ -1,7 +1,9 @@ -import {DataSource, dataSources, Resource} from '../../src'; +import {dataSources, Resource} from '../../src'; +import {DataSource} from '../../src/backend/data-source'; +import {BaseDataSource} from '../../src/common/data-source'; -export const autoIncrement = async (dataSource: DataSource) => { - const data = await dataSource.getMultiple() as Record[]; +export const autoIncrement = async (dataSource: BaseDataSource) => { + const data = await (dataSource as DataSource).getMultiple() as Record[]; const highestId = data.reduce( (highestId, d) => (Number(d.id) > highestId ? Number(d.id) : highestId), diff --git a/examples/basic/server.ts b/examples/basic/server.ts index cd9faec..4657a2e 100644 --- a/examples/basic/server.ts +++ b/examples/basic/server.ts @@ -15,6 +15,7 @@ const Piano = resource(v.object( v.never() )) .name('Piano') + .route('pianos') .id('id', { generationStrategy: autoIncrement, serialize: (id) => id?.toString() ?? '0', @@ -45,6 +46,7 @@ const User = resource(v.object( schema: v.number(), }) .name('User') + .route('users') .fullText('bio'); const app = application({ diff --git a/src/backend/extenders/method.ts b/src/backend/extenders/method.ts index 3a89c14..f22d162 100644 --- a/src/backend/extenders/method.ts +++ b/src/backend/extenders/method.ts @@ -3,7 +3,7 @@ import http from 'http'; import {HttpMiddlewareError} from '../server'; interface RequestContext extends http.IncomingMessage { - method: string; + method?: string; } export const adjustMethod = (req: RequestContext) => { diff --git a/src/backend/extenders/url.ts b/src/backend/extenders/url.ts index eca7199..1558d7b 100644 --- a/src/backend/extenders/url.ts +++ b/src/backend/extenders/url.ts @@ -7,7 +7,7 @@ interface RequestContext extends http.IncomingMessage { query?: URLSearchParams; - rawUrl: string; + rawUrl?: string; } export const adjustUrl = (req: RequestContext) => { diff --git a/src/backend/handlers.ts b/src/backend/handlers.ts index 3d97d9b..149e087 100644 --- a/src/backend/handlers.ts +++ b/src/backend/handlers.ts @@ -6,10 +6,10 @@ export const handleGetRoot: Middleware = (req) => { const { backend, basePath } = req; const data = { - name: backend.app.name + name: backend!.app.name }; - const registeredResources = Array.from(backend.app.resources); + const registeredResources = Array.from(backend!.app.resources); const availableResources = registeredResources.filter((r) => ( r.state.canFetchCollection || r.state.canCreate @@ -43,7 +43,7 @@ export const handleGetCollection: Middleware = async (req) => { try { // TODO querying mechanism data = await resource.dataSource.getMultiple(query); // TODO paginated responses per resource - if (backend.showTotalItemCountOnGetCollection && typeof resource.dataSource.getTotalCount === 'function') { + if (backend!.showTotalItemCountOnGetCollection && typeof resource.dataSource.getTotalCount === 'function') { totalItemCount = await resource.dataSource.getTotalCount(query); } } catch (cause) { @@ -133,7 +133,7 @@ export const handleDeleteItem: Middleware = async (req) => { }); } - if (!existing && backend.throws404OnDeletingNotFound) { + if (!existing && backend!.throws404OnDeletingNotFound) { throw new HttpMiddlewareError('deleteNonExistingResource', { statusCode: constants.HTTP_STATUS_NOT_FOUND }); @@ -214,7 +214,7 @@ export const handleCreateItem: Middleware = async (req) => { let totalItemCount: number | undefined; try { newObject = await resource.dataSource.create(params); - if (backend.showTotalItemCountOnCreateItem && typeof resource.dataSource.getTotalCount === 'function') { + if (backend!.showTotalItemCountOnCreateItem && typeof resource.dataSource.getTotalCount === 'function') { totalItemCount = await resource.dataSource.getTotalCount(); } } catch (cause) { @@ -266,7 +266,7 @@ export const handleEmplaceItem: Middleware = async (req) => { const headers: Record = {}; let totalItemCount: number | undefined; - if (backend.showTotalItemCountOnCreateItem && typeof resource.dataSource.getTotalCount === 'function') { + if (backend!.showTotalItemCountOnCreateItem && typeof resource.dataSource.getTotalCount === 'function') { totalItemCount = await resource.dataSource.getTotalCount(); } if (isCreated) { diff --git a/src/backend/server.ts b/src/backend/server.ts index b1ff90d..ebabe7d 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -1,9 +1,6 @@ import http from 'http'; import {BackendState} from './common'; import {Language, Resource, Charset, MediaType, LanguageStatusMessageMap} from '../common'; -import * as applicationJson from '../common/media-types/application/json'; -import * as utf8 from '../common/charsets/utf-8'; -import * as en from '../common/languages/en'; import https from 'https'; import Negotiator from 'negotiator'; import {constants} from 'http2'; @@ -89,19 +86,19 @@ export interface CreateServerParams { } export interface RequestContext extends http.IncomingMessage { - backend: BackendState; + backend?: BackendState; - host: string; + host?: string; - scheme: string; + scheme?: string; - basePath: string; + basePath?: string; - method: string; + method?: string; - url: string; + url?: string; - rawUrl: string; + rawUrl?: string; cn: { language: Language; @@ -122,39 +119,6 @@ export interface Middleware { (req: Req): undefined | Response | Promise; } -class ServerYasumiRequest extends http.IncomingMessage implements RequestContext { - host = 'localhost'; - - scheme = 'http'; - - basePath = ''; - - backend = {} as BackendState; - - resource = undefined as unknown as BackendResource; - - resourceId?: string; - - query = new URLSearchParams(); - - body?: unknown; - - method = ''; - - url = ''; - - rawUrl = ''; - - readonly cn: { - language: Language; - mediaType: MediaType; - charset: Charset; - } = { - language: en, - mediaType: applicationJson, - charset: utf8, - }; -} const getAllowedMiddlewares = (resource: Resource, mainResourceId: string) => { const middlewares = [] as [string, Middleware, v.BaseSchema?][]; if (mainResourceId === '') { @@ -210,13 +174,13 @@ const getAllowedMiddlewares = (resource: Resource, ma const adjustRequestForContentNegotiation = (req: RequestContext, res: http.ServerResponse) => { const negotiator = new Negotiator(req); - const availableLanguages = Array.from(req.backend.app.languages); - const availableCharsets = Array.from(req.backend.app.charsets); - const availableMediaTypes = Array.from(req.backend.app.mediaTypes); + const availableLanguages = Array.from(req.backend!.app.languages); + const availableCharsets = Array.from(req.backend!.app.charsets); + const availableMediaTypes = Array.from(req.backend!.app.mediaTypes); - const languageCandidate = negotiator.language(availableLanguages.map((l) => l.name)) ?? req.backend.cn.language.name; - const charsetCandidate = negotiator.charset(availableCharsets.map((l) => l.name)) ?? req.backend.cn.charset.name; - const mediaTypeCandidate = negotiator.mediaType(availableMediaTypes.map((l) => l.name)) ?? req.backend.cn.mediaType.name; + const languageCandidate = negotiator.language(availableLanguages.map((l) => l.name)) ?? req.backend!.cn.language.name; + const charsetCandidate = negotiator.charset(availableCharsets.map((l) => l.name)) ?? req.backend!.cn.charset.name; + const mediaTypeCandidate = negotiator.mediaType(availableMediaTypes.map((l) => l.name)) ?? req.backend!.cn.mediaType.name; // TODO refactor const currentLanguage = availableLanguages.find((l) => l.name === languageCandidate); @@ -255,9 +219,9 @@ const adjustRequestForContentNegotiation = (req: RequestContext, res: http.Serve const responseBodyCharset = availableCharsets.find((l) => l.name === charsetCandidate); if (typeof responseBodyCharset === 'undefined') { - const data = req.backend?.cn.language.bodies.encodingNotAcceptable(); - const responseRaw = req.backend?.cn.mediaType.serialize(data); - const response = typeof responseRaw !== 'undefined' ? req.backend?.cn.charset.encode(responseRaw) : undefined; + const data = req.backend!.cn.language.bodies.encodingNotAcceptable(); + const responseRaw = req.backend!.cn.mediaType.serialize(data); + const response = typeof responseRaw !== 'undefined' ? req.backend!.cn.charset.encode(responseRaw) : undefined; res.writeHead(constants.HTTP_STATUS_NOT_ACCEPTABLE, { 'Content-Language': req.backend?.cn.language.name, 'Content-Type': [ @@ -283,18 +247,18 @@ export const createServer = (backendState: BackendState, serverParams = {} as Cr key: serverParams.key, cert: serverParams.cert, requestTimeout: serverParams.requestTimeout, - IncomingMessage: ServerYasumiRequest, }) : http.createServer({ requestTimeout: serverParams.requestTimeout, - IncomingMessage: ServerYasumiRequest, }); - server.on('request', async (req: RequestContext, res) => { + server.on('request', async (req: RequestContext, res: http.ServerResponse) => { req.backend = backendState; req.basePath = serverParams.basePath ?? ''; req.host = serverParams.host ?? 'localhost'; req.scheme = isHttps ? 'https' : 'http'; + req.cn = req.backend.cn; + adjustRequestForContentNegotiation(req, res); try { @@ -373,7 +337,7 @@ export const createServer = (backendState: BackendState, serverParams = {} as Cr return; } - const [, resourceRouteName, resourceId = ''] = req.url.split('/') ?? []; + const [, resourceRouteName, resourceId = ''] = req.url?.split('/') ?? []; const resource = Array.from(req.backend.app.resources).find((r) => r.state!.routeName === resourceRouteName); if (typeof resource === 'undefined') { res.statusCode = constants.HTTP_STATUS_NOT_FOUND; @@ -414,8 +378,8 @@ export const createServer = (backendState: BackendState, serverParams = {} as Cr } if (schema) { - const availableSerializers = Array.from(req.backend.app.mediaTypes); - const availableCharsets = Array.from(req.backend.app.charsets); + const availableSerializers = Array.from(req.backend!.app.mediaTypes); + const availableCharsets = Array.from(req.backend!.app.charsets); const contentTypeHeader = req.headers['content-type'] ?? 'application/octet-stream'; const fragments = contentTypeHeader.split(';'); const mediaType = fragments[0];