diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5c93d0c7..ef0bac20 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -2,6 +2,7 @@ "packages/auth-bundler": "12.0.0", "packages/auth-core": "12.0.0", "packages/auth-openapi": "12.0.0", + "packages/token-openapi": "12.0.0", "packages/test-utils": "12.0.0", "apps/auth-cron": "12.0.0", "apps/auth-manager": "12.0.0", diff --git a/.vscode/project.code-workspace b/.vscode/project.code-workspace index 56f0f68e..4c68ac79 100644 --- a/.vscode/project.code-workspace +++ b/.vscode/project.code-workspace @@ -16,6 +16,10 @@ "name": "Auth OpenAPI", "path": "../packages/auth-openapi", }, + { + "name": "Token OpenAPI", + "path": "../packages/token-openapi", + }, { "name": "Test Utils", "path": "../packages/test-utils", diff --git a/apps/auth-manager/tsconfig.json b/apps/auth-manager/tsconfig.json index de593871..382204ac 100644 --- a/apps/auth-manager/tsconfig.json +++ b/apps/auth-manager/tsconfig.json @@ -11,8 +11,7 @@ "@client/*": ["./src/client/*"], "@domain/*": ["./src/domain/*"], "@key/*": ["./src/key/*"], - "@connection/*": ["./src/connection/*"], - "@openapi": ["src/openapi.js"] + "@connection/*": ["./src/connection/*"] } }, "include": ["src", "tests"], diff --git a/apps/kiosk-ui/package.json b/apps/kiosk-ui/package.json index 47e476e3..3a04676c 100644 --- a/apps/kiosk-ui/package.json +++ b/apps/kiosk-ui/package.json @@ -8,7 +8,6 @@ "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "generate:openapi-types": "openapi-typescript ../token-kiosk/openapi3.yaml -o ./src/types/openapi.d.ts", "knip": "knip --directory ../.. --workspace apps/kiosk-ui" }, "dependencies": { @@ -42,11 +41,11 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", - "openapi-typescript": "^7.8.0", "tailwindcss": "^4.1.11", "tw-animate-css": "^1.3.5", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", - "vite": "^7.0.3" + "vite": "^7.0.3", + "token-openapi": "workspace:^" } } diff --git a/apps/kiosk-ui/src/components/snake-game.tsx b/apps/kiosk-ui/src/components/snake-game.tsx index 3cae7067..c672696e 100644 --- a/apps/kiosk-ui/src/components/snake-game.tsx +++ b/apps/kiosk-ui/src/components/snake-game.tsx @@ -308,7 +308,7 @@ export function SnakeGame({ isVisible }: SnakeGameProps) { setDisplayIsPlaying(true); drawGame(0); - gameLoopRef.current = setInterval(gameLoop, GAME_SPEED); + gameLoopRef.current = window.setInterval(gameLoop, GAME_SPEED); }, [drawGame, gameLoop]); const handleKeyPress = useCallback((e: KeyboardEvent) => { diff --git a/apps/kiosk-ui/src/hooks/use-token-generation.ts b/apps/kiosk-ui/src/hooks/use-token-generation.ts index 8f0395de..522c77cf 100644 --- a/apps/kiosk-ui/src/hooks/use-token-generation.ts +++ b/apps/kiosk-ui/src/hooks/use-token-generation.ts @@ -33,7 +33,7 @@ export function useTokenGeneration() { setProgress(easedProgress); await new Promise((res) => { - timeoutId = setTimeout(res, stepDuration); + timeoutId = window.setTimeout(res, stepDuration); }); } diff --git a/apps/kiosk-ui/src/lib/http-client.ts b/apps/kiosk-ui/src/lib/http-client.ts index 13a7eec3..5ecda900 100644 --- a/apps/kiosk-ui/src/lib/http-client.ts +++ b/apps/kiosk-ui/src/lib/http-client.ts @@ -1,6 +1,6 @@ import createFetchClient from 'openapi-fetch'; import createClient from 'openapi-react-query'; -import type { paths } from '../types/openapi'; +import type { paths } from 'token-openapi'; // Create a global handler for 401 responses let authRedirectHandler: (() => void) | null = null; diff --git a/apps/kiosk-ui/src/types/openapi.d.ts b/apps/kiosk-ui/src/types/openapi.d.ts deleted file mode 100644 index 80ea86f4..00000000 --- a/apps/kiosk-ui/src/types/openapi.d.ts +++ /dev/null @@ -1,346 +0,0 @@ -/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths { - '/token': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** generated and returns a temporary access token for the MapColonies services. */ - get: operations['getToken']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/auth/me': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get current authenticated user information - * @description Returns information about the currently authenticated user - */ - get: operations['getCurrentUser']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/guides': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get available documentation guides - * @description Returns a list of guides to documentation and resources related to the Token Kiosk service - */ - get: operations['getGuides']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/files/{type}': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get QLR for QGIS or lyrx for ArcGIS Pro - * @description Returns a File that can be used to load specific MapColonies layers already configured and ready to use in QGIS or ArcGIS Pro. - */ - get: operations['getFile']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; -} -export type webhooks = Record; -export interface components { - schemas: { - error: { - message: string; - }; - tokenResponse: { - /** @description Temporary access token for MapColonies services */ - token: string; - /** - * Format: date-time - * @description Expiration date and time of the token - */ - expiration: string; - }; - userInfo: { - /** @description Unique user identifier (subject) */ - sub: string; - /** @description User display name */ - name?: string; - /** @description User first name */ - given_name?: string; - /** @description User last name */ - family_name?: string; - /** @description User middle name */ - middle_name?: string; - /** @description User nickname */ - nickname?: string; - /** @description User preferred username */ - preferred_username?: string; - /** - * Format: email - * @description User email address - */ - email?: string; - /** @description User gender */ - gender?: string; - /** @description User phone number */ - phone_number?: string; - }; - guidesResponse: { - /** @description Link to QGIS documentation */ - qgis: string; - /** @description Link to ArcGIS Pro documentation */ - arcgis: string; - /** @description Whether the guides are enabled */ - enabled: boolean; - }; - /** - * @description Type of file to retrieve (qlr for QGIS, lyrx for ArcGIS Pro) - * @enum {string} - */ - fileTypes: 'qlr' | 'lyrx'; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export interface operations { - getToken: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['tokenResponse']; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Unauthorized - No valid token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Forbidden - User is banned */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - }; - }; - getCurrentUser: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description User information retrieved successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['userInfo']; - }; - }; - /** @description Unauthorized - No valid token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - }; - }; - getGuides: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Guides retrieved successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['guidesResponse']; - }; - }; - /** @description Unauthorized - No valid token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Forbidden - User is banned */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - }; - }; - getFile: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Type of file to retrieve (qlr for QGIS, lyrx for ArcGIS Pro) */ - type: components['schemas']['fileTypes']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description QLR file retrieved successfully */ - 200: { - headers: { - /** @description Indicates that the content is a file attachment */ - 'Content-Disposition'?: string; - [name: string]: unknown; - }; - content: { - 'application/json': - | { - [key: string]: unknown; - } - | string - | Record; - 'application/xml': string | Record; - }; - }; - /** @description Unauthorized - No valid token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Forbidden - User is banned */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - }; - }; -} diff --git a/apps/token-kiosk/package.json b/apps/token-kiosk/package.json index 8f68348b..695624da 100644 --- a/apps/token-kiosk/package.json +++ b/apps/token-kiosk/package.json @@ -8,7 +8,6 @@ "test": "vitest run", "test:watch": "vitest watch", "test:ui": "vitest --ui", - "lint:openapi": "redocly lint openapi3.yaml", "lint": "eslint .", "lint:fix": "eslint --fix .", "prebuild": "npm run clean", @@ -17,8 +16,6 @@ "start:dev": "npm run build && cd dist && cross-env CONFIG_OFFLINE_MODE=true node --enable-source-maps --import ./instrumentation.mjs ./index.js", "assets:copy": "copyfiles -f ./config/* ./dist/config && copyfiles -f ./openapi3.yaml ./dist/ && copyfiles ./package.json dist && copyfiles -u 1 ./src/db/migrations/* ./dist/ && copyfiles -u 1 ./src/db/migrations/meta/* ./dist/", "clean": "rimraf dist", - "generate:openapi-types": "openapi-helpers generate types ./openapi3.yaml ./src/openapi.d.ts --format --add-typed-request-handler", - "generate:auth-manager-types": "openapi-helpers ../auth-manager/openapi3.yaml ./src/auth-manager.d.ts --format", "migration:create": "drizzle-kit generate --config drizzle.config.mts", "migration:run": "ts-node ./src/db/runMigrations.ts", "build:docker": "docker buildx build --build-arg APP_NAME=$npm_package_name -f ../../docker/backend.Dockerfile -t ${DOCKER_REGISTRY:-}${npm_package_name}:${DOCKER_TAG:-latest} ${DOCKER_FLAGS:-} ../..", @@ -61,9 +58,11 @@ "prom-client": "^15.1.3", "reflect-metadata": "^0.2.2", "tsyringe": "^4.8.0", - "zod": "^4.0.5" + "zod": "^4.0.5", + "token-openapi": "workspace:^" }, "devDependencies": { + "auth-openapi": "workspace:^", "@map-colonies/openapi-helpers": "catalog:", "@map-colonies/eslint-config": "catalog:", "@map-colonies/tsconfig": "catalog:", diff --git a/apps/token-kiosk/src/auth-manager.d.ts b/apps/token-kiosk/src/auth-manager.d.ts deleted file mode 100644 index 500042c1..00000000 --- a/apps/token-kiosk/src/auth-manager.d.ts +++ /dev/null @@ -1,1272 +0,0 @@ -/* eslint-disable */ -export type paths = { - '/client': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** gets clients by filtering */ - get: operations['getClients']; - put?: never; - /** creates a new client */ - post: operations['createClient']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/client/{clientName}': { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - }; - cookie?: never; - }; - /** get client by name */ - get: operations['getClient']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - /** update the client */ - patch: operations['updateClient']; - trace?: never; - }; - '/client/{clientName}/connection': { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - }; - cookie?: never; - }; - /** gets the connections for a specific client */ - get: operations['getClientConnections']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/client/{clientName}/connection/{environment}': { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - /** get the latest client connection for specific environment */ - get: operations['getClientEnvironmentConnections']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/client/{clientName}/connection/{environment}/{version}': { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - environment: components['parameters']['environmentPathParam']; - version: components['parameters']['versionParam']; - }; - cookie?: never; - }; - /** get a specfic client connection for specific environment */ - get: operations['getClientVersionedConnection']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/client/{clientName}/connection/{environment}/latest': { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - /** get the latest client connection for specific environment */ - get: operations['getClientLatestConnection']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/connection': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** get a connections based on filters */ - get: operations['getConnections']; - put?: never; - /** creates a new connection or updates it based on the version */ - post: operations['upsertConnection']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/key': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** get all latest keys */ - get: operations['getLastestKeys']; - put?: never; - /** creates a new key or updates it based on the version */ - post: operations['upsertKey']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/key/{environment}': { - parameters: { - query?: never; - header?: never; - path: { - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - /** get keys for specific environment */ - get: operations['getKeys']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/key/{environment}/{version}': { - parameters: { - query?: never; - header?: never; - path: { - environment: components['parameters']['environmentPathParam']; - version: components['parameters']['versionParam']; - }; - cookie?: never; - }; - /** gets a specific key */ - get: operations['getSpecificKey']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/key/{environment}/latest': { - parameters: { - query?: never; - header?: never; - path: { - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - /** gets the latest key for specific environment */ - get: operations['getLatestKey']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/asset': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** get assets by filters */ - get: operations['getAssets']; - put?: never; - /** creates a new asset or updates it based on the version */ - post: operations['upsertAsset']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/asset/{assetName}': { - parameters: { - query?: never; - header?: never; - path: { - assetName: components['parameters']['assetParam']; - }; - cookie?: never; - }; - /** get asset by name */ - get: operations['getAsset']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/asset/{assetName}/{version}': { - parameters: { - query?: never; - header?: never; - path: { - assetName: components['parameters']['assetParam']; - version: components['parameters']['versionParam']; - }; - cookie?: never; - }; - /** get asset by name and version */ - get: operations['getVersionedAsset']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/asset/{assetName}/latest': { - parameters: { - query?: never; - header?: never; - path: { - assetName: components['parameters']['assetParam']; - }; - cookie?: never; - }; - /** get latest asset by name */ - get: operations['getLatestAsset']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/bundle': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** get bundles by filter */ - get: operations['getBundles']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/bundle/{id}': { - parameters: { - query?: never; - header?: never; - path: { - id: number; - }; - cookie?: never; - }; - /** get a specific bundle */ - get: operations['getBundle']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/domain': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** get all the domains */ - get: operations['getDomains']; - put?: never; - /** create a new domain */ - post: operations['createDomain']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; -}; -export type webhooks = Record; -export type components = { - schemas: { - error: { - message: string; - }; - /** @enum {string} */ - environment: 'np' | 'stage' | 'prod'; - /** - * Format: int32 - * @default 1 - */ - version: number; - /** @enum {string} */ - assetType: 'TEST' | 'TEST_DATA' | 'POLICY' | 'DATA'; - name: string; - domain: { - name: components['schemas']['name']; - }; - pointOfContact: { - name: components['schemas']['name']; - /** Format: email */ - email: string; - /** Format: phone */ - phone: string; - }; - namelessClient: { - hebName: components['schemas']['name']; - description?: string; - branch?: string; - /** Format: date-time */ - readonly createdAt: string; - /** Format: date-time */ - readonly updatedAt: string; - techPointOfContact?: components['schemas']['pointOfContact']; - productPointOfContact?: components['schemas']['pointOfContact']; - tags?: string[]; - }; - client: components['schemas']['namelessClient'] & { - name: components['schemas']['name']; - }; - connection: { - name: components['schemas']['name']; - environment: components['schemas']['environment']; - version: components['schemas']['version']; - /** @default true */ - enabled: boolean; - token: string; - domains: string[]; - /** Format: date-time */ - readonly createdAt?: string; - allowNoBrowserConnection: boolean; - allowNoOriginConnection: boolean; - origins: string[]; - }; - publicJWK: { - kty: string; - n: string; - e: string; - alg: string; - kid: string; - }; - privateJWK: components['schemas']['publicJWK'] & { - d: string; - p: string; - q: string; - dp: string; - dq: string; - qi: string; - }; - key: { - publicKey: components['schemas']['publicJWK']; - privateKey: components['schemas']['privateJWK']; - version: components['schemas']['version']; - environment: components['schemas']['environment']; - }; - asset: { - environment: components['schemas']['environment'][]; - /** Format: date-time */ - readonly createdAt: string; - /** Format: byte */ - value: string; - uri: string; - type: components['schemas']['assetType']; - /** @default false */ - isTemplate: boolean; - version: components['schemas']['version']; - name: string; - }; - bundle: { - /** Format: int32 */ - id?: number; - hash?: string; - metadata?: { - [key: string]: unknown; - }; - assets?: { - name: string; - version: components['schemas']['version']; - }[]; - connections?: { - name: string; - version: components['schemas']['version']; - }[]; - environment?: components['schemas']['environment'] & unknown; - /** Format: date-time */ - readonly createdAt?: string; - keyVersion?: components['schemas']['version']; - /** @description OPA version used to generate the bundle */ - opaVersion?: string; - }; - paginationResponse: { - /** - * Format: int32 - * @description total number of items - */ - total: number; - }; - }; - responses: { - /** @description BadRequest */ - '400BadRequest': { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Not Found - If client does not exist */ - '404NotFound': { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description conflict */ - '409Conflict': { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Unsupported Media Type */ - '415UnsupportedMediaType': { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - /** @description Internal Server Error */ - '500InternalServerError': { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['error']; - }; - }; - }; - parameters: { - /** @description page number for pagination. The value is 1-based, meaning the first page is 1. - * If the value is greater than the total number of pages, an empty array will be returned. - * This simplifies the pagination logic on the client side. - * */ - page: number; - /** @description number of items per page */ - pageSize: number; - clientParam: string; - assetParam: string; - versionParam: components['schemas']['version']; - environmentQueryParam: components['schemas']['environment'][]; - environmentPathParam: components['schemas']['environment']; - }; - requestBodies: never; - headers: never; - pathItems: never; -}; -export type $defs = Record; -export interface operations { - getClients: { - parameters: { - query?: { - /** @description search by branch name */ - branch?: string; - /** @description filters all clients created before given date */ - createdBefore?: string; - /** @description filters all clients created after given date */ - createdAfter?: string; - /** @description filters all clients updated before given date */ - updatedBefore?: string; - /** @description filters all clients updated after given date */ - updatedAfter?: string; - /** @description filters based on tags */ - tags?: string[]; - /** - * @description Sorts the results based on the value of one or more properties. - * The value is a comma-separated list of property names and sort order. - * properties should be separated by a colon and sort order should be either asc or desc. For example: created-at:asc,name:asc - * The default sort order is ascending. If the sort order is not specified, the default sort order is used. Each property is only allowed to appear once in the list. - * The available properties are: - * - created-at - * - updated-at - * - name - * - heb-name - * - branch - * @example [ - * "created-at:asc", - * "name:asc" - * ] - */ - sort?: string[]; - /** @description page number for pagination. The value is 1-based, meaning the first page is 1. - * If the value is greater than the total number of pages, an empty array will be returned. - * This simplifies the pagination logic on the client side. - * */ - page?: components['parameters']['page']; - /** @description number of items per page */ - page_size?: components['parameters']['pageSize']; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['paginationResponse'] & { - /** @description list of clients */ - items: components['schemas']['client'][]; - }; - }; - }; - 400: components['responses']['400BadRequest']; - 500: components['responses']['500InternalServerError']; - }; - }; - createClient: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['client']; - }; - }; - responses: { - /** @description created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['client']; - }; - }; - 400: components['responses']['400BadRequest']; - 409: components['responses']['409Conflict']; - 500: components['responses']['500InternalServerError']; - }; - }; - getClient: { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['client']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - updateClient: { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - }; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['namelessClient']; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['client']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getClientConnections: { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['connection'][]; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getClientEnvironmentConnections: { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['connection'][]; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getClientVersionedConnection: { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - environment: components['parameters']['environmentPathParam']; - version: components['parameters']['versionParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['connection']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getClientLatestConnection: { - parameters: { - query?: never; - header?: never; - path: { - clientName: components['parameters']['clientParam']; - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['connection']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getConnections: { - parameters: { - query?: { - environment?: components['parameters']['environmentQueryParam']; - isEnabled?: boolean; - isNoBrowser?: boolean; - isNoOrigin?: boolean; - domains?: string[]; - /** - * @description Sorts the results based on the value of one or more properties. - * The value is a comma-separated list of property names and sort order. - * properties should be separated by a colon and sort order should be either asc or desc. For example: created-at:asc,name:asc - * The default sort order is ascending. If the sort order is not specified, the default sort order is used. Each property is only allowed to appear once in the list. - * The available properties are: - * - created-at - * - name - * - version - * - enabled - * - environment - * @example [ - * "created-at:asc", - * "name:asc" - * ] - */ - sort?: string[]; - /** @description page number for pagination. The value is 1-based, meaning the first page is 1. - * If the value is greater than the total number of pages, an empty array will be returned. - * This simplifies the pagination logic on the client side. - * */ - page?: components['parameters']['page']; - /** @description number of items per page */ - page_size?: components['parameters']['pageSize']; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['paginationResponse'] & { - /** @description list of clients */ - items: components['schemas']['connection'][]; - }; - }; - }; - 400: components['responses']['400BadRequest']; - 500: components['responses']['500InternalServerError']; - }; - }; - upsertConnection: { - parameters: { - query?: { - shouldIgnoreTokenErrors?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['connection']; - }; - }; - responses: { - /** @description updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['connection']; - }; - }; - /** @description created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['connection']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 409: components['responses']['409Conflict']; - 500: components['responses']['500InternalServerError']; - }; - }; - getLastestKeys: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['key'][]; - }; - }; - 415: components['responses']['415UnsupportedMediaType']; - 500: components['responses']['500InternalServerError']; - }; - }; - upsertKey: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['key']; - }; - }; - responses: { - /** @description updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['key']; - }; - }; - /** @description created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['key']; - }; - }; - 400: components['responses']['400BadRequest']; - 409: components['responses']['409Conflict']; - 500: components['responses']['500InternalServerError']; - }; - }; - getKeys: { - parameters: { - query?: never; - header?: never; - path: { - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['key'][]; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getSpecificKey: { - parameters: { - query?: never; - header?: never; - path: { - environment: components['parameters']['environmentPathParam']; - version: components['parameters']['versionParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['key']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getLatestKey: { - parameters: { - query?: never; - header?: never; - path: { - environment: components['parameters']['environmentPathParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['key']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getAssets: { - parameters: { - query?: { - environment?: components['parameters']['environmentQueryParam']; - type?: components['schemas']['assetType']; - isTemplate?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['asset'][]; - }; - }; - 400: components['responses']['400BadRequest']; - 500: components['responses']['500InternalServerError']; - }; - }; - upsertAsset: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['asset']; - }; - }; - responses: { - /** @description updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['asset']; - }; - }; - /** @description created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['asset']; - }; - }; - 400: components['responses']['400BadRequest']; - 409: components['responses']['409Conflict']; - 500: components['responses']['500InternalServerError']; - }; - }; - getAsset: { - parameters: { - query?: never; - header?: never; - path: { - assetName: components['parameters']['assetParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['asset'][]; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getVersionedAsset: { - parameters: { - query?: never; - header?: never; - path: { - assetName: components['parameters']['assetParam']; - version: components['parameters']['versionParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['asset']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getLatestAsset: { - parameters: { - query?: never; - header?: never; - path: { - assetName: components['parameters']['assetParam']; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['asset']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['404NotFound']; - 500: components['responses']['500InternalServerError']; - }; - }; - getBundles: { - parameters: { - query?: { - /** @description filters all clients created before given date */ - createdBefore?: string; - /** @description filters all clients created after given date */ - createdAfter?: string; - environment?: components['parameters']['environmentQueryParam']; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['bundle'][]; - }; - }; - 400: components['responses']['400BadRequest']; - 500: components['responses']['500InternalServerError']; - }; - }; - getBundle: { - parameters: { - query?: { - environment?: components['parameters']['environmentQueryParam']; - }; - header?: never; - path: { - id: number; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['bundle']; - }; - }; - 400: components['responses']['400BadRequest']; - 404: components['responses']['400BadRequest']; - 500: components['responses']['500InternalServerError']; - }; - }; - getDomains: { - parameters: { - query?: { - /** - * @description Sorts the results based on the value of one or more properties. - * The value is a comma-separated list of property names and sort order. - * properties should be separated by a colon and sort order should be either asc or desc. For example: domain:asc - * The default sort order is ascending. If the sort order is not specified, the default sort order is used. Each property is only allowed to appear once in the list. - * The available properties are: - * - domain - * @example [ - * "domain:asc" - * ] - */ - sort?: string[]; - /** @description page number for pagination. The value is 1-based, meaning the first page is 1. - * If the value is greater than the total number of pages, an empty array will be returned. - * This simplifies the pagination logic on the client side. - * */ - page?: components['parameters']['page']; - /** @description number of items per page */ - page_size?: components['parameters']['pageSize']; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['paginationResponse'] & { - /** @description list of clients */ - items: components['schemas']['domain'][]; - }; - }; - }; - 415: components['responses']['415UnsupportedMediaType']; - 500: components['responses']['500InternalServerError']; - }; - }; - createDomain: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['domain']; - }; - }; - responses: { - /** @description created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': components['schemas']['domain']; - }; - }; - 400: components['responses']['400BadRequest']; - 409: components['responses']['409Conflict']; - 500: components['responses']['500InternalServerError']; - }; - }; -} diff --git a/apps/token-kiosk/src/auth/controllers/authController.ts b/apps/token-kiosk/src/auth/controllers/authController.ts index f7e1bd9e..e67b131d 100644 --- a/apps/token-kiosk/src/auth/controllers/authController.ts +++ b/apps/token-kiosk/src/auth/controllers/authController.ts @@ -1,7 +1,7 @@ import type { Logger } from '@map-colonies/js-logger'; import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; -import type { TypedRequestHandlers } from '@openapi'; +import type { TypedRequestHandlers } from 'token-openapi'; import { SERVICES } from '@common/constants'; /** diff --git a/apps/token-kiosk/src/files/controllers/filesController.ts b/apps/token-kiosk/src/files/controllers/filesController.ts index fe0af681..dbcddbc9 100644 --- a/apps/token-kiosk/src/files/controllers/filesController.ts +++ b/apps/token-kiosk/src/files/controllers/filesController.ts @@ -2,7 +2,7 @@ import httpStatus from 'http-status-codes'; import { formatDate } from 'date-fns'; import { injectable, inject } from 'tsyringe'; import type { Logger } from '@map-colonies/js-logger'; -import { TypedRequestHandlers } from '@src/openapi'; +import type { TypedRequestHandlers } from 'token-openapi'; import { SERVICES } from '@common/constants'; import { type ConfigType } from '@src/common/config'; import { AuthManager } from '@src/auth/model/authManager'; diff --git a/apps/token-kiosk/src/files/models/fileManager.ts b/apps/token-kiosk/src/files/models/fileManager.ts index bd7bef73..7a4f1b83 100644 --- a/apps/token-kiosk/src/files/models/fileManager.ts +++ b/apps/token-kiosk/src/files/models/fileManager.ts @@ -2,7 +2,7 @@ import { injectable, inject } from 'tsyringe'; import { Cache, createCache } from 'async-cache-dedupe'; import { hoursToSeconds } from 'date-fns'; import { renderString } from 'nunjucks'; -import type { components } from '@openapi'; +import type { components } from 'token-openapi'; import { SERVICES } from '@common/constants'; import { type ConfigType } from '@src/common/config'; import { TokenManager } from '@src/tokens/models/tokenManager'; diff --git a/apps/token-kiosk/src/guides/controllers/guidesController.ts b/apps/token-kiosk/src/guides/controllers/guidesController.ts index 1d5ddb6b..78637809 100644 --- a/apps/token-kiosk/src/guides/controllers/guidesController.ts +++ b/apps/token-kiosk/src/guides/controllers/guidesController.ts @@ -1,6 +1,6 @@ import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; -import type { TypedRequestHandlers } from '@openapi'; +import type { TypedRequestHandlers } from 'token-openapi'; import { SERVICES } from '@common/constants'; import { type ConfigType } from '@src/common/config'; diff --git a/apps/token-kiosk/src/serverBuilder.ts b/apps/token-kiosk/src/serverBuilder.ts index b3a0ca46..3f04b534 100644 --- a/apps/token-kiosk/src/serverBuilder.ts +++ b/apps/token-kiosk/src/serverBuilder.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import express, { Router, static as expressStatic } from 'express'; import bodyParser from 'body-parser'; import compression from 'compression'; @@ -17,6 +18,8 @@ import { openidAuthMiddlewareFactory } from './auth/middlewares/openid'; import { GUIDES_ROUTER_SYMBOL } from './guides/routes/guidesRouter'; import { FILES_ROUTER_SYMBOL } from './files/routes/filesRouter'; +const OPENAPI_PATH = path.join(path.dirname(require.resolve('token-openapi')), 'openapi3.yaml'); + @injectable() export class ServerBuilder { private readonly serverInstance: express.Application; @@ -45,7 +48,7 @@ export class ServerBuilder { private buildDocsRoutes(): void { const openapiRouter = new OpenapiViewerRouter({ ...this.config.get('openapiConfig'), - filePathOrSpec: this.config.get('openapiConfig.filePath'), + filePathOrSpec: OPENAPI_PATH, }); openapiRouter.setup(); this.serverInstance.use(this.config.get('openapiConfig.basePath'), openapiRouter.getRouter()); @@ -78,10 +81,9 @@ export class ServerBuilder { this.serverInstance.use(this.authMiddleware); const ignorePathRegex = new RegExp(`^${this.config.get('openapiConfig.basePath')}/.*`, 'i'); - const apiSpecPath = this.config.get('openapiConfig.filePath'); this.serverInstance.use( - OpenApiMiddleware({ apiSpec: apiSpecPath, validateRequests: true, ignorePaths: ignorePathRegex, validateSecurity: false }) + OpenApiMiddleware({ apiSpec: OPENAPI_PATH, validateRequests: true, ignorePaths: ignorePathRegex, validateSecurity: false }) ); } diff --git a/apps/token-kiosk/src/tokens/controllers/tokenController.ts b/apps/token-kiosk/src/tokens/controllers/tokenController.ts index 4ebe7211..4793004a 100644 --- a/apps/token-kiosk/src/tokens/controllers/tokenController.ts +++ b/apps/token-kiosk/src/tokens/controllers/tokenController.ts @@ -1,7 +1,7 @@ import type { Logger } from '@map-colonies/js-logger'; import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; -import type { TypedRequestHandlers } from '@openapi'; +import type { TypedRequestHandlers } from 'token-openapi'; import { SERVICES } from '@common/constants'; import { UserIsBannedError } from '@src/users/errors'; import { AuthManager } from '@src/auth/model/authManager'; diff --git a/apps/token-kiosk/src/tokens/models/authManagerClient.ts b/apps/token-kiosk/src/tokens/models/authManagerClient.ts index 69223124..b45cb8c2 100644 --- a/apps/token-kiosk/src/tokens/models/authManagerClient.ts +++ b/apps/token-kiosk/src/tokens/models/authManagerClient.ts @@ -1,7 +1,7 @@ import type { Client } from 'openapi-fetch'; import createClient from 'openapi-fetch'; import type { DependencyContainer } from 'tsyringe'; -import type { paths } from '@src/auth-manager'; +import type { paths } from 'auth-openapi'; import type { ConfigType } from '@src/common/config'; import { SERVICES } from '@src/common/constants'; diff --git a/apps/token-kiosk/src/tokens/models/tokenManager.ts b/apps/token-kiosk/src/tokens/models/tokenManager.ts index 6c3f09d6..a8789da3 100644 --- a/apps/token-kiosk/src/tokens/models/tokenManager.ts +++ b/apps/token-kiosk/src/tokens/models/tokenManager.ts @@ -4,13 +4,13 @@ import parseDuration from 'parse-duration'; import { SignJWT } from 'jose'; import { inject, injectable } from 'tsyringe'; import { Cache, createCache } from 'async-cache-dedupe'; -import type { components } from '@openapi'; +import type { components as authManagerComponents } from 'auth-openapi'; +import type { components } from 'token-openapi'; import type { ConfigType } from '@src/common/config'; import { AuthManager } from '@src/auth/model/authManager'; import { UserManager } from '@src/users/userManager'; import { UserIsBannedError } from '@src/users/errors'; import { SERVICES } from '@common/constants'; -import type { components as authManagerComponents } from '@src/auth-manager'; import type { AuthManagerClient } from './authManagerClient'; export type TokenResponse = components['schemas']['tokenResponse']; diff --git a/apps/token-kiosk/tests/configurations/vitest.setup.ts b/apps/token-kiosk/tests/configurations/vitest.setup.ts index 62dbbf01..cb5c23d3 100644 --- a/apps/token-kiosk/tests/configurations/vitest.setup.ts +++ b/apps/token-kiosk/tests/configurations/vitest.setup.ts @@ -1,5 +1,5 @@ import 'reflect-metadata'; -import path from 'node:path'; import { setupOpenapi } from '@map-colonies/vitest-utils'; +import { OPENAPI_PATH } from '@tests/utils/paths.mjs'; -setupOpenapi(path.join(process.cwd(), 'openapi3.yaml')); +setupOpenapi(OPENAPI_PATH); diff --git a/apps/token-kiosk/tests/files/files.spec.ts b/apps/token-kiosk/tests/files/files.spec.ts index 90f4479c..f6d48924 100644 --- a/apps/token-kiosk/tests/files/files.spec.ts +++ b/apps/token-kiosk/tests/files/files.spec.ts @@ -8,11 +8,12 @@ import type { RequestSender } from '@map-colonies/openapi-helpers/requestSender' import { createRequestSender } from '@map-colonies/openapi-helpers/requestSender'; import type { RequestContext } from 'express-openid-connect'; import type { RequestHandler } from 'express'; -import type { paths, operations } from '@openapi'; +import type { paths, operations } from 'token-openapi'; import { getApp } from '@src/app'; import type { Drizzle } from '@src/db/createConnection'; import { users } from '@src/users/user'; import { SERVICES } from '@common/constants'; +import { OPENAPI_PATH } from '@tests/utils/paths.mjs'; import { initConfig } from '@src/common/config'; import privateKey from '../data/key'; import mockUser from '../data/user'; @@ -47,7 +48,7 @@ describe('guides', function () { useChild: true, }); - requestSender = await createRequestSender('openapi3.yaml', app, { baseUrl: '/api' }); + requestSender = await createRequestSender(OPENAPI_PATH, app, { baseUrl: '/api' }); drizzle = container.resolve(SERVICES.DRIZZLE); nock('http://localhost:8082').get('/key/prod/latest').reply(httpStatusCodes.OK, privateKey); diff --git a/apps/token-kiosk/tests/guides/guides.spec.ts b/apps/token-kiosk/tests/guides/guides.spec.ts index bb10d1b0..6f18536a 100644 --- a/apps/token-kiosk/tests/guides/guides.spec.ts +++ b/apps/token-kiosk/tests/guides/guides.spec.ts @@ -6,8 +6,9 @@ import type { RequestSender } from '@map-colonies/openapi-helpers/requestSender' import { createRequestSender } from '@map-colonies/openapi-helpers/requestSender'; import type { RequestContext } from 'express-openid-connect'; import type { RequestHandler } from 'express'; -import type { paths, operations } from '@openapi'; +import type { paths, operations } from 'token-openapi'; import { getApp } from '@src/app'; +import { OPENAPI_PATH } from '@tests/utils/paths.mjs'; import { SERVICES } from '@common/constants'; import { initConfig } from '@src/common/config'; import mockUser from '../data/user'; @@ -40,7 +41,7 @@ describe('guides', function () { useChild: true, }); - requestSender = await createRequestSender('openapi3.yaml', app, { baseUrl: '/api' }); + requestSender = await createRequestSender(OPENAPI_PATH, app, { baseUrl: '/api' }); }); afterEach(function () { diff --git a/apps/token-kiosk/tests/token/token.spec.ts b/apps/token-kiosk/tests/token/token.spec.ts index 69a15848..f35e5921 100644 --- a/apps/token-kiosk/tests/token/token.spec.ts +++ b/apps/token-kiosk/tests/token/token.spec.ts @@ -10,12 +10,13 @@ import type { RequestContext } from 'express-openid-connect'; import type { RequestHandler } from 'express'; import { eq } from 'drizzle-orm'; import { subWeeks } from 'date-fns'; -import type { paths, operations } from '@openapi'; +import type { paths, operations } from 'token-openapi'; import { getApp } from '@src/app'; import type { Drizzle } from '@src/db/createConnection'; import { users } from '@src/users/user'; import { SERVICES } from '@common/constants'; import { initConfig } from '@src/common/config'; +import { OPENAPI_PATH } from '@tests/utils/paths.mjs'; import privateKey from '../data/key'; import mockUser from '../data/user'; @@ -54,7 +55,7 @@ describe('token', function () { useChild: true, }); - requestSender = await createRequestSender('openapi3.yaml', app, { baseUrl: '/api' }); + requestSender = await createRequestSender(OPENAPI_PATH, app, { baseUrl: '/api' }); drizzle = container.resolve(SERVICES.DRIZZLE); }); diff --git a/apps/token-kiosk/tests/utils/paths.mts b/apps/token-kiosk/tests/utils/paths.mts new file mode 100644 index 00000000..fc764b6f --- /dev/null +++ b/apps/token-kiosk/tests/utils/paths.mts @@ -0,0 +1,6 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const tokenOpenapiPath = fileURLToPath(new URL('.', import.meta.resolve('token-openapi'))); + +export const OPENAPI_PATH = path.join(tokenOpenapiPath, 'openapi3.yaml'); diff --git a/apps/token-kiosk/tsconfig.json b/apps/token-kiosk/tsconfig.json index 03f8ab6f..75790492 100644 --- a/apps/token-kiosk/tsconfig.json +++ b/apps/token-kiosk/tsconfig.json @@ -6,8 +6,7 @@ "paths": { "@src/*": ["./src/*"], "@tests/*": ["./tests/*"], - "@common/*": ["./src/common/*"], - "@openapi": ["./src/openapi"] + "@common/*": ["./src/common/*"] }, "types": ["jest-openapi", "lodash"] }, diff --git a/packages/auth-openapi/package.json b/packages/auth-openapi/package.json index f873e972..75593291 100644 --- a/packages/auth-openapi/package.json +++ b/packages/auth-openapi/package.json @@ -31,7 +31,8 @@ "scripts": { "check-dist": "publint && attw --profile node16 --pack .", "knip": "knip --directory ../.. --workspace packages/auth-openapi", - "lint:openapi": "redocly lint --config ../../redocly.yaml openapi3.yaml" + "lint:openapi": "redocly lint --config ../../redocly.yaml openapi3.yaml", + "generate:openapi-types": "openapi-helpers generate types ./openapi3.yaml ./index.d.ts --format --add-typed-request-handler" }, "bugs": { "url": "https://github.com/MapColonies/opa-la/issues" diff --git a/packages/token-openapi/eslint.config.mjs b/packages/token-openapi/eslint.config.mjs new file mode 100644 index 00000000..cdfdebf9 --- /dev/null +++ b/packages/token-openapi/eslint.config.mjs @@ -0,0 +1,4 @@ +import tsBaseConfig from '@map-colonies/eslint-config/ts-base'; +import { defineConfig, globalIgnores } from 'eslint/config'; + +export default defineConfig(tsBaseConfig, globalIgnores(['vitest.config.mts', 'index.d.ts'])); diff --git a/apps/token-kiosk/src/openapi.d.ts b/packages/token-openapi/index.d.ts similarity index 100% rename from apps/token-kiosk/src/openapi.d.ts rename to packages/token-openapi/index.d.ts diff --git a/packages/token-openapi/index.js b/packages/token-openapi/index.js new file mode 100644 index 00000000..1a32b123 --- /dev/null +++ b/packages/token-openapi/index.js @@ -0,0 +1 @@ +// LOL diff --git a/apps/token-kiosk/openapi3.yaml b/packages/token-openapi/openapi3.yaml similarity index 100% rename from apps/token-kiosk/openapi3.yaml rename to packages/token-openapi/openapi3.yaml diff --git a/packages/token-openapi/package.json b/packages/token-openapi/package.json new file mode 100644 index 00000000..d8d36eef --- /dev/null +++ b/packages/token-openapi/package.json @@ -0,0 +1,47 @@ +{ + "name": "token-openapi", + "version": "12.0.0", + "description": "OpenAPI specification for the token service of OPA LA", + "author": "MapColonies", + "homepage": "https://github.com/MapColonies/opa-la#readme", + "license": "MIT", + "private": true, + "type": "commonjs", + "files": [ + "openapi3.yaml", + "index.d.ts", + "index.js" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/MapColonies/opa-la.git" + }, + "exports": { + ".": { + "import": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "require": { + "types": "./index.d.ts", + "default": "./index.js" + } + } + }, + "scripts": { + "check-dist": "publint && attw --profile node16 --pack .", + "knip": "knip --directory ../.. --workspace packages/token-openapi", + "lint:openapi": "redocly lint --config ../../redocly.yaml openapi3.yaml", + "generate:openapi-types": "openapi-helpers generate types ./openapi3.yaml ./index.d.ts --format --add-typed-request-handler" + }, + "bugs": { + "url": "https://github.com/MapColonies/opa-la/issues" + }, + "dependencies": {}, + "peerDependencies": {}, + "devDependencies": { + "@map-colonies/eslint-config": "catalog:", + "@map-colonies/openapi-helpers": "catalog:", + "eslint": "catalog:" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5230e075..565e3ec0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -600,12 +600,12 @@ importers: globals: specifier: ^16.3.0 version: 16.5.0 - openapi-typescript: - specifier: ^7.8.0 - version: 7.13.0(typescript@5.8.3) tailwindcss: specifier: ^4.1.11 version: 4.2.2 + token-openapi: + specifier: workspace:^ + version: link:../../packages/token-openapi tw-animate-css: specifier: ^1.3.5 version: 1.4.0 @@ -708,6 +708,9 @@ importers: reflect-metadata: specifier: ^0.2.2 version: 0.2.2 + token-openapi: + specifier: workspace:^ + version: link:../../packages/token-openapi tsyringe: specifier: ^4.8.0 version: 4.10.0 @@ -757,6 +760,9 @@ importers: '@vitest/ui': specifier: 'catalog:' version: 4.1.4(vitest@4.1.4) + auth-openapi: + specifier: workspace:^ + version: link:../../packages/auth-openapi cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -950,6 +956,18 @@ importers: specifier: 'catalog:' version: 5.9.3 + packages/token-openapi: + devDependencies: + '@map-colonies/eslint-config': + specifier: 'catalog:' + version: 8.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(@vitest/eslint-plugin@1.6.15(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.4))(eslint-plugin-jest@28.14.0(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@24.12.0))(typescript@5.9.3))(eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(globals@15.15.0)(typescript@5.9.3) + '@map-colonies/openapi-helpers': + specifier: 'catalog:' + version: 5.1.0(@types/express@4.17.25)(@types/json-schema@7.0.15)(encoding@0.1.13)(openapi-typescript@7.13.0(typescript@5.9.3))(prettier@3.8.1)(supertest@7.2.2)(typescript@5.9.3) + eslint: + specifier: 'catalog:' + version: 9.39.4(jiti@2.6.1) + packages: '@andrewbranch/untar.js@1.0.3': @@ -17620,16 +17638,6 @@ snapshots: openapi-typescript-helpers@0.1.0: {} - openapi-typescript@7.13.0(typescript@5.8.3): - dependencies: - '@redocly/openapi-core': 1.34.11(supports-color@10.2.2) - ansi-colors: 4.1.3 - change-case: 5.4.4 - parse-json: 8.3.0 - supports-color: 10.2.2 - typescript: 5.8.3 - yargs-parser: 21.1.1 - openapi-typescript@7.13.0(typescript@5.9.3): dependencies: '@redocly/openapi-core': 1.34.11(supports-color@10.2.2) diff --git a/release-please-config.json b/release-please-config.json index 8ba48f79..b30b5b6d 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -84,6 +84,11 @@ "component": "auth-openapi", "skip-changelog": true }, + "packages/token-openapi": { + "release-type": "node", + "component": "token-openapi", + "skip-changelog": true + }, "packages/test-utils": { "release-type": "node", "component": "test-utils",