From 8d4071ec7b2e4425f074e61a254be97101aa5836 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 12 May 2024 11:45:08 +0200 Subject: [PATCH] Add a service container --- app/api/apiClientBuilder.server.ts | 20 +++++++++++++++++++ app/container/container.server.ts | 18 +++++++++++++++++ app/routes/server.$serverId.$.tsx | 5 +++-- .../server.$serverId.shlink-api.$method.ts | 10 +++++----- app/routes/server.$serverId.tags.colors.ts | 6 +++++- app/servers/ServersService.server.ts | 3 +-- app/settings/SettingsService.server.ts | 4 +--- app/tags/TagsService.server.ts | 4 +--- package-lock.json | 1 + package.json | 1 + 10 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 app/api/apiClientBuilder.server.ts create mode 100644 app/container/container.server.ts diff --git a/app/api/apiClientBuilder.server.ts b/app/api/apiClientBuilder.server.ts new file mode 100644 index 0000000..cb8073c --- /dev/null +++ b/app/api/apiClientBuilder.server.ts @@ -0,0 +1,20 @@ +import { ShlinkApiClient } from '@shlinkio/shlink-js-sdk'; +import { NodeHttpClient } from '@shlinkio/shlink-js-sdk/node'; +import type { Server } from '../entities/Server'; + +const apiClients = new Map(); + +export const apiClientBuilder = (server: Server) => { + const key = `${server.apiKey}_${server.baseUrl}`; + const existingApiClient = apiClients.get(key); + + if (existingApiClient) { + return existingApiClient; + } + + const apiClient = new ShlinkApiClient(new NodeHttpClient(), server); + apiClients.set(key, apiClient); + return apiClient; +}; + +export type ApiClientBuilder = typeof apiClientBuilder; diff --git a/app/container/container.server.ts b/app/container/container.server.ts new file mode 100644 index 0000000..5131fb7 --- /dev/null +++ b/app/container/container.server.ts @@ -0,0 +1,18 @@ +import Bottle from 'bottlejs'; +import { apiClientBuilder } from '../api/apiClientBuilder.server'; +import { appDataSource } from '../db/data-source.server'; +import { ServersService } from '../servers/ServersService.server'; +import { SettingsService } from '../settings/SettingsService.server'; +import { TagsService } from '../tags/TagsService.server'; + +const bottle = new Bottle(); + +bottle.serviceFactory('em', () => appDataSource.manager); + +bottle.service(TagsService.name, TagsService, 'em'); +bottle.service(ServersService.name, ServersService, 'em'); +bottle.service(SettingsService.name, SettingsService, 'em'); + +bottle.constant('apiClientBuilder', apiClientBuilder); + +export const { container: serverContainer } = bottle; diff --git a/app/routes/server.$serverId.$.tsx b/app/routes/server.$serverId.$.tsx index c1ea1e9..3732b3f 100644 --- a/app/routes/server.$serverId.$.tsx +++ b/app/routes/server.$serverId.$.tsx @@ -4,14 +4,15 @@ import type { Settings } from '@shlinkio/shlink-web-component'; import type { ReactNode } from 'react'; import { useEffect, useMemo, useState } from 'react'; import { ShlinkApiProxyClient } from '../api/ShlinkApiProxyClient.client'; +import { serverContainer } from '../container/container.server'; import { SettingsService } from '../settings/SettingsService.server'; import { TagsService } from '../tags/TagsService.server'; import { TagsStorage } from '../tags/TagsStorage.client'; export async function loader( { params }: LoaderFunctionArgs, - tagsService = new TagsService(), - settingsService = new SettingsService(), + tagsService: TagsService = serverContainer[TagsService.name], + settingsService: SettingsService = serverContainer[SettingsService.name], ): Promise<{ settings: Settings; tagColors: Record }> { const { serverId: serverPublicId } = params; const userId = 1; // FIXME Get from session diff --git a/app/routes/server.$serverId.shlink-api.$method.ts b/app/routes/server.$serverId.shlink-api.$method.ts index 0cf8cb1..a89697b 100644 --- a/app/routes/server.$serverId.shlink-api.$method.ts +++ b/app/routes/server.$serverId.shlink-api.$method.ts @@ -1,8 +1,8 @@ import type { ActionFunctionArgs } from '@remix-run/node'; import { json } from '@remix-run/node'; -import { ShlinkApiClient } from '@shlinkio/shlink-js-sdk'; -import { NodeHttpClient } from '@shlinkio/shlink-js-sdk/node'; -import type { Server } from '../entities/Server'; +import type { ShlinkApiClient } from '@shlinkio/shlink-js-sdk'; +import type { ApiClientBuilder } from '../api/apiClientBuilder.server'; +import { serverContainer } from '../container/container.server'; import { ServersService } from '../servers/ServersService.server'; type Callback = (...args: unknown[]) => unknown; @@ -21,8 +21,8 @@ function argsAreValidForAction(args: any[], callback: Callback): args is Paramet export async function action( { params, request }: ActionFunctionArgs, - serversService = new ServersService(), - createApiClient = (server: Server) => new ShlinkApiClient(new NodeHttpClient(), server), + serversService: ServersService = serverContainer[ServersService.name], + createApiClient: ApiClientBuilder = serverContainer.apiClientBuilder, ) { try { const { method, serverId = '' } = params; diff --git a/app/routes/server.$serverId.tags.colors.ts b/app/routes/server.$serverId.tags.colors.ts index 76d1862..6c1fd98 100644 --- a/app/routes/server.$serverId.tags.colors.ts +++ b/app/routes/server.$serverId.tags.colors.ts @@ -1,7 +1,11 @@ import type { ActionFunctionArgs } from '@remix-run/node'; +import { serverContainer } from '../container/container.server'; import { TagsService } from '../tags/TagsService.server'; -export async function action({ params, request }: ActionFunctionArgs, tagsService = new TagsService()) { +export async function action( + { params, request }: ActionFunctionArgs, + tagsService: TagsService = serverContainer[TagsService.name], +) { const { serverId: serverPublicId } = params; // TODO Get UserID from session const userId = 1; diff --git a/app/servers/ServersService.server.ts b/app/servers/ServersService.server.ts index 77d9ddb..fcea0b9 100644 --- a/app/servers/ServersService.server.ts +++ b/app/servers/ServersService.server.ts @@ -1,10 +1,9 @@ import type { EntityManager } from 'typeorm'; -import { appDataSource } from '../db/data-source.server'; import type { Server } from '../entities/Server'; import { ServerEntity } from '../entities/Server'; export class ServersService { - constructor(private readonly em: EntityManager = appDataSource.manager) {} + constructor(private readonly em: EntityManager) {} public async getByPublicId(publicId: string): Promise { const server = await this.em.findOneBy(ServerEntity, { publicId }); diff --git a/app/settings/SettingsService.server.ts b/app/settings/SettingsService.server.ts index 1403525..5392578 100644 --- a/app/settings/SettingsService.server.ts +++ b/app/settings/SettingsService.server.ts @@ -1,12 +1,10 @@ import type { Settings } from '@shlinkio/shlink-web-component'; import type { EntityManager } from 'typeorm'; -import { appDataSource } from '../db/data-source.server'; import { SettingsEntity } from '../entities/Settings'; import { UserEntity } from '../entities/User'; export class SettingsService { - constructor(private readonly em: EntityManager = appDataSource.manager) { - } + constructor(private readonly em: EntityManager) {} async userSettings(userId: number): Promise { const user = await this.em.findOneBy(UserEntity, { id: userId }); diff --git a/app/tags/TagsService.server.ts b/app/tags/TagsService.server.ts index 6940e56..5a3676f 100644 --- a/app/tags/TagsService.server.ts +++ b/app/tags/TagsService.server.ts @@ -1,5 +1,4 @@ import type { EntityManager } from 'typeorm'; -import { appDataSource } from '../db/data-source.server'; import type { Server } from '../entities/Server'; import { ServerEntity } from '../entities/Server'; import { TagEntity } from '../entities/Tag'; @@ -21,8 +20,7 @@ type ServerAndUserResult = { }; export class TagsService { - constructor(private readonly em: EntityManager = appDataSource.manager) { - } + constructor(private readonly em: EntityManager) {} async tagColors(param: FindTagsParam): Promise> { const { server, user } = await this.resolveServerAndUser(param); diff --git a/package-lock.json b/package-lock.json index 796be4d..583aaf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@shlinkio/shlink-js-sdk": "^1.1.0", "@shlinkio/shlink-web-component": "^0.6.2", "bootstrap": "5.2.3", + "bottlejs": "^2.0.1", "express": "^4.19.2", "isbot": "^5.1.6", "pg": "^8.11.5", diff --git a/package.json b/package.json index d5cdea9..70e6603 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@shlinkio/shlink-js-sdk": "^1.1.0", "@shlinkio/shlink-web-component": "^0.6.2", "bootstrap": "5.2.3", + "bottlejs": "^2.0.1", "express": "^4.19.2", "isbot": "^5.1.6", "pg": "^8.11.5",