@furystack/rest-service 12.3.4 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/esm/actions/index.d.ts +0 -1
  3. package/esm/actions/index.d.ts.map +1 -1
  4. package/esm/actions/index.js +0 -1
  5. package/esm/actions/index.js.map +1 -1
  6. package/esm/api-manager.d.ts.map +1 -1
  7. package/esm/api-manager.js +1 -2
  8. package/esm/api-manager.js.map +1 -1
  9. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +0 -14
  10. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -1
  11. package/esm/endpoint-generators/create-get-openapi-document-action.js +0 -23
  12. package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -1
  13. package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +0 -2
  14. package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -1
  15. package/esm/openapi/generate-openapi-document.d.ts +0 -8
  16. package/esm/openapi/generate-openapi-document.d.ts.map +1 -1
  17. package/esm/openapi/generate-openapi-document.js +0 -2
  18. package/esm/openapi/generate-openapi-document.js.map +1 -1
  19. package/esm/rest-service.integration.spec.js +3 -2
  20. package/esm/rest-service.integration.spec.js.map +1 -1
  21. package/esm/validate.integration.spec.js +0 -55
  22. package/esm/validate.integration.spec.js.map +1 -1
  23. package/package.json +12 -12
  24. package/src/actions/index.ts +0 -1
  25. package/src/api-manager.ts +1 -5
  26. package/src/endpoint-generators/create-get-openapi-document-action.ts +0 -36
  27. package/src/endpoint-generators/with-schema-and-openapi-action.ts +0 -3
  28. package/src/openapi/generate-openapi-document.ts +0 -3
  29. package/src/rest-service.integration.spec.ts +3 -2
  30. package/src/validate.integration.spec.ts +0 -62
  31. package/esm/actions/login-action.spec.d.ts +0 -2
  32. package/esm/actions/login-action.spec.d.ts.map +0 -1
  33. package/esm/actions/login-action.spec.js +0 -34
  34. package/esm/actions/login-action.spec.js.map +0 -1
  35. package/esm/actions/login.d.ts +0 -19
  36. package/esm/actions/login.d.ts.map +0 -1
  37. package/esm/actions/login.js +0 -27
  38. package/esm/actions/login.js.map +0 -1
  39. package/esm/swagger/generate-swagger-json.spec.d.ts +0 -2
  40. package/esm/swagger/generate-swagger-json.spec.d.ts.map +0 -1
  41. package/esm/swagger/generate-swagger-json.spec.js +0 -215
  42. package/esm/swagger/generate-swagger-json.spec.js.map +0 -1
  43. package/src/actions/login-action.spec.ts +0 -42
  44. package/src/actions/login.ts +0 -32
  45. package/src/swagger/generate-swagger-json.spec.ts +0 -250
@@ -11,10 +11,7 @@ import { ErrorAction } from './actions/error-action.js'
11
11
  import { NotFoundAction } from './actions/not-found-action.js'
12
12
  import { addCorsHeaders } from './add-cors-header.js'
13
13
  import { CreateGetSchemaAction } from './endpoint-generators/create-get-schema-action.js'
14
- import {
15
- CreateGetOpenApiDocumentAction,
16
- CreateDeprecatedSwaggerRedirect,
17
- } from './endpoint-generators/create-get-openapi-document-action.js'
14
+ import { CreateGetOpenApiDocumentAction } from './endpoint-generators/create-get-openapi-document-action.js'
18
15
  import { HttpUserContext } from './http-user-context.js'
19
16
  import type { CorsOptions } from './models/cors-options.js'
20
17
  import { readPostBody } from './read-post-body.js'
@@ -147,7 +144,6 @@ export class ApiManager {
147
144
  ...api.GET,
148
145
  '/schema': CreateGetSchemaAction(api, name, description, version),
149
146
  '/openapi.json': CreateGetOpenApiDocumentAction(api, name, description, version),
150
- '/swagger.json': CreateDeprecatedSwaggerRedirect(api, name, description, version),
151
147
  },
152
148
  }
153
149
  : api
@@ -58,39 +58,3 @@ export const CreateGetOpenApiDocumentAction = <T extends RestApiImplementation<R
58
58
  return JsonResult(cachedDoc, 200)
59
59
  }
60
60
  }
61
-
62
- /**
63
- * Generates a backward-compatible `/swagger.json` endpoint that serves the same OpenAPI document
64
- * as `CreateGetOpenApiDocumentAction` but includes `Deprecation` and `Link` headers
65
- * directing clients to use `/openapi.json` instead.
66
- *
67
- * @deprecated Use CreateGetOpenApiDocumentAction and `/openapi.json` instead.
68
- */
69
- export const CreateDeprecatedSwaggerRedirect = <T extends RestApiImplementation<RestApi>>(
70
- api: T,
71
- name = 'FuryStack API',
72
- description = 'API documentation generated from FuryStack API schema',
73
- version = '1.0.0',
74
- ): RequestAction<{ result: OpenApiDocument }> => {
75
- const { endpoints } = getSchemaFromApi({ api, name, description, version })
76
- let cachedDoc: OpenApiDocument | undefined
77
- return async ({ injector }) => {
78
- if (!cachedDoc) {
79
- const securitySchemes = getSecuritySchemesFromInjector(injector)
80
- cachedDoc = buildOpenApiDoc(
81
- endpoints,
82
- name,
83
- description,
84
- version,
85
- securitySchemes ? { securitySchemes } : undefined,
86
- )
87
- }
88
- return JsonResult(cachedDoc, 200, {
89
- Deprecation: 'true',
90
- Link: '</openapi.json>; rel="successor-version"',
91
- })
92
- }
93
- }
94
-
95
- /** @deprecated Use CreateGetOpenApiDocumentAction instead */
96
- export const CreateGetSwaggerJsonAction = CreateGetOpenApiDocumentAction
@@ -9,6 +9,3 @@ export type WithSchemaAndOpenApiAction<T extends RestApi> = T & {
9
9
  '/openapi.json': { result: OpenApiDocument; headers: { accept: 'application/json' } }
10
10
  }
11
11
  }
12
-
13
- /** @deprecated Use WithSchemaAndOpenApiAction instead */
14
- export type WithSchemaAndSwaggerAction<T extends RestApi> = WithSchemaAndOpenApiAction<T>
@@ -193,6 +193,3 @@ export const generateOpenApiDocument = ({
193
193
 
194
194
  return doc
195
195
  }
196
-
197
- /** @deprecated Use generateOpenApiDocument instead */
198
- export const generateSwaggerJsonFromApiSchema = generateOpenApiDocument
@@ -6,9 +6,10 @@ import type { RestApi } from '@furystack/rest'
6
6
  import { serializeValue } from '@furystack/rest'
7
7
  import { PathHelper, usingAsync } from '@furystack/utils'
8
8
  import { describe, expect, it, vi } from 'vitest'
9
- import { GetCurrentUser, IsAuthenticated, LoginAction, LogoutAction } from './actions/index.js'
9
+ import { GetCurrentUser, IsAuthenticated, LogoutAction, createPasswordLoginAction } from './actions/index.js'
10
10
  import './helpers.js'
11
11
  import { useHttpAuthentication, useRestService } from './helpers.js'
12
+ import { createCookieLoginStrategy } from './login-response-strategy.js'
12
13
  import { DefaultSession } from './models/default-session.js'
13
14
  import { ServerManager } from './server-manager.js'
14
15
  import { JsonResult } from './request-action-implementation.js'
@@ -56,7 +57,7 @@ const createIntegrationApi = async () => {
56
57
  '/testUrlParams/:urlParam': async (options) => JsonResult({ urlParamValue: options.getUrlParams().urlParam }),
57
58
  },
58
59
  POST: {
59
- '/login': LoginAction,
60
+ '/login': createPasswordLoginAction(createCookieLoginStrategy(i)),
60
61
  '/logout': LogoutAction,
61
62
  '/testPostBody': async (options) => {
62
63
  const body = await options.getBody()
@@ -179,51 +179,6 @@ describe('Validation integration tests', () => {
179
179
  })
180
180
  })
181
181
 
182
- describe('swagger.json backward compatibility', () => {
183
- it('Should serve the same OpenAPI document at /swagger.json when enabled', async () => {
184
- await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
185
- const result = await (client as ReturnType<typeof createClient<any>>)({
186
- method: 'GET',
187
- action: '/swagger.json',
188
- })
189
-
190
- expect(result.response.status).toBe(200)
191
- expect(result.result).toBeDefined()
192
-
193
- const openApiDoc = result.result as OpenApiDocument
194
- expect(openApiDoc.openapi).toBe('3.1.0')
195
- expect(openApiDoc.info?.title).toBe(name)
196
- })
197
- })
198
-
199
- it('Should include Deprecation header on /swagger.json', async () => {
200
- await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
201
- const result = await (client as ReturnType<typeof createClient<any>>)({
202
- method: 'GET',
203
- action: '/swagger.json',
204
- })
205
-
206
- expect(result.response.headers.get('deprecation')).toBe('true')
207
- expect(result.response.headers.get('link')).toContain('/openapi.json')
208
- })
209
- })
210
-
211
- it('Should return a 404 for /swagger.json when not enabled', async () => {
212
- await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
213
- try {
214
- await (client as ReturnType<typeof createClient<any>>)({
215
- method: 'GET',
216
- action: '/swagger.json',
217
- })
218
- expect.fail('Expected response error but got success')
219
- } catch (error) {
220
- expect(error).toBeInstanceOf(ResponseError)
221
- expect((error as ResponseError).response.status).toBe(404)
222
- }
223
- })
224
- })
225
- })
226
-
227
182
  describe('Validation metadata', () => {
228
183
  it('Should return 404 when not enabled', async () => {
229
184
  expect.assertions(2)
@@ -577,22 +532,5 @@ describe('Validation integration tests', () => {
577
532
  })
578
533
  })
579
534
  })
580
-
581
- it('Should include detected security schemes in the deprecated /swagger.json endpoint', async () => {
582
- await usingAsync(await createApiWithAuth(), async ({ client }) => {
583
- const result = await (client as ReturnType<typeof createClient<any>>)({
584
- method: 'GET',
585
- action: '/swagger.json',
586
- })
587
-
588
- const openApiDoc = result.result as OpenApiDocument
589
- expect(openApiDoc.components?.securitySchemes?.basicAuth).toEqual({ type: 'http', scheme: 'basic' })
590
- expect(openApiDoc.components?.securitySchemes?.cookieAuth).toEqual({
591
- type: 'apiKey',
592
- in: 'cookie',
593
- name: 'session',
594
- })
595
- })
596
- })
597
535
  })
598
536
  })
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=login-action.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login-action.spec.d.ts","sourceRoot":"","sources":["../../src/actions/login-action.spec.ts"],"names":[],"mappings":""}
@@ -1,34 +0,0 @@
1
- import { Injector } from '@furystack/inject';
2
- import { usingAsync } from '@furystack/utils';
3
- import { describe, expect, it, vi } from 'vitest';
4
- import { HttpUserContext } from '../http-user-context.js';
5
- import { LoginAction } from './login.js';
6
- describe('LoginAction', () => {
7
- const request = { url: 'https://google.com' };
8
- const response = {};
9
- it('Returns the provided user with 200 on success', async () => {
10
- const testUser = { Name: 'Userke' };
11
- await usingAsync(new Injector(), async (i) => {
12
- i.setExplicitInstance({
13
- authenticateUser: vi.fn(async () => testUser),
14
- cookieLogin: vi.fn(async () => testUser),
15
- authentication: {},
16
- }, HttpUserContext);
17
- const result = await LoginAction({
18
- request,
19
- response,
20
- injector: i,
21
- getBody: async () => ({ username: 'testuser', password: 'alma' }),
22
- });
23
- expect(result.chunk).toEqual(testUser);
24
- expect(result.statusCode).toBe(200);
25
- });
26
- });
27
- it('Returns throw error with 400 on fail', async () => {
28
- await usingAsync(new Injector(), async (i) => {
29
- i.setExplicitInstance({ cookieLogin: async () => Promise.reject(new Error(':(')) }, HttpUserContext);
30
- await expect(LoginAction({ request, response, injector: i, getBody: async () => ({ username: '', password: '' }) })).rejects.toThrowError('Login Failed');
31
- });
32
- });
33
- });
34
- //# sourceMappingURL=login-action.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login-action.spec.js","sourceRoot":"","sources":["../../src/actions/login-action.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAExC,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAqB,CAAA;IAChE,MAAM,QAAQ,GAAG,EAAoB,CAAA;IAErC,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;QACnC,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,CAAC,CAAC,mBAAmB,CACnB;gBACE,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAC7C,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC;gBACxC,cAAc,EAAE,EAAE;aACnB,EACD,eAAe,CAChB,CAAA;YACD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;gBAC/B,OAAO;gBACP,QAAQ;gBACR,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;aAClE,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;YACtC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACrC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,CAAC,CAAC,mBAAmB,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC,CAAA;YACpG,MAAM,MAAM,CACV,WAAW,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CACvG,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1,19 +0,0 @@
1
- import type { User } from '@furystack/core';
2
- import type { RequestAction } from '../request-action-implementation.js';
3
- /**
4
- * Action that logs in the current user.
5
- * Should be called with a JSON POST body with `username` and `password` fields.
6
- * Returns the current user instance.
7
- *
8
- * @deprecated Use `createPasswordLoginAction(createCookieLoginStrategy(injector))` instead.
9
- * This static action resolves services from the request-scoped injector on
10
- * every call; the factory approach captures them once at setup time.
11
- */
12
- export declare const LoginAction: RequestAction<{
13
- result: User;
14
- body: {
15
- username: string;
16
- password: string;
17
- };
18
- }>;
19
- //# sourceMappingURL=login.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAK3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IACtC,MAAM,EAAE,IAAI,CAAA;IACZ,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC7C,CAWA,CAAA"}
@@ -1,27 +0,0 @@
1
- import { RequestError } from '@furystack/rest';
2
- import { sleepAsync } from '@furystack/utils';
3
- import { HttpUserContext } from '../http-user-context.js';
4
- import { JsonResult } from '../request-action-implementation.js';
5
- /**
6
- * Action that logs in the current user.
7
- * Should be called with a JSON POST body with `username` and `password` fields.
8
- * Returns the current user instance.
9
- *
10
- * @deprecated Use `createPasswordLoginAction(createCookieLoginStrategy(injector))` instead.
11
- * This static action resolves services from the request-scoped injector on
12
- * every call; the factory approach captures them once at setup time.
13
- */
14
- export const LoginAction = async ({ injector, getBody, response }) => {
15
- const userContext = injector.getInstance(HttpUserContext);
16
- const body = await getBody();
17
- try {
18
- const user = await userContext.authenticateUser(body.username, body.password);
19
- await userContext.cookieLogin(user, response);
20
- return JsonResult(user, 200);
21
- }
22
- catch {
23
- await sleepAsync(Math.random() * 1000);
24
- throw new RequestError('Login Failed', 400);
25
- }
26
- };
27
- //# sourceMappingURL=login.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAGnB,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAA;IACzD,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;IAC5B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC7E,MAAM,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC7C,OAAO,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;QACtC,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=generate-swagger-json.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate-swagger-json.spec.d.ts","sourceRoot":"","sources":["../../src/swagger/generate-swagger-json.spec.ts"],"names":[],"mappings":""}
@@ -1,215 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { generateSwaggerJsonFromApiSchema } from '../openapi/generate-openapi-document.js';
3
- describe('generateSwaggerJsonFromApiSchema', () => {
4
- it('Should generate a basic Swagger document with correct OpenAPI structure', () => {
5
- const api = {
6
- GET: {
7
- '/api/test': {
8
- path: '/api/test',
9
- isAuthenticated: false,
10
- schemaName: 'Test',
11
- schema: { type: 'object', properties: { id: { type: 'string' } } },
12
- },
13
- },
14
- };
15
- const result = generateSwaggerJsonFromApiSchema({ api });
16
- // Check basic structure
17
- expect(result.openapi).toBe('3.1.0');
18
- expect(result.info.title).toBe('FuryStack API');
19
- expect(result.info.version).toBe('1.0.0');
20
- // Check security scheme is defined correctly for cookie-based auth
21
- expect(result.components?.securitySchemes?.cookieAuth).toEqual({
22
- type: 'apiKey',
23
- in: 'cookie',
24
- name: 'session',
25
- });
26
- });
27
- it('Should convert API endpoints to OpenAPI paths correctly', () => {
28
- const api = {
29
- GET: {
30
- '/api/users': {
31
- path: '/api/users',
32
- isAuthenticated: true,
33
- schemaName: 'UserCollection',
34
- schema: { type: 'array', items: { type: 'object' } },
35
- },
36
- '/api/users/:id': {
37
- path: '/api/users/:id',
38
- isAuthenticated: true,
39
- schemaName: 'User',
40
- schema: { type: 'object', properties: { id: { type: 'string' } } },
41
- },
42
- },
43
- };
44
- const result = generateSwaggerJsonFromApiSchema({ api });
45
- // Check paths
46
- expect(result.paths?.['/api/users']).toBeDefined();
47
- expect(result.paths?.['/api/users/{id}']).toBeDefined();
48
- // Check methods
49
- expect(result.paths?.['/api/users']?.get).toBeDefined();
50
- expect(result.paths?.['/api/users/{id}']?.get).toBeDefined();
51
- // Check security - should use cookieAuth for authenticated endpoints
52
- expect(result.paths?.['/api/users']?.get?.security).toEqual([{ cookieAuth: [] }]);
53
- expect(result.paths?.['/api/users/{id}']?.get?.security).toEqual([{ cookieAuth: [] }]);
54
- });
55
- it('Should extract path parameters correctly', () => {
56
- const api = {
57
- GET: {
58
- '/api/users/:userId/posts/:postId': {
59
- path: '/api/users/:userId/posts/:postId',
60
- isAuthenticated: false,
61
- schemaName: 'Post',
62
- schema: { type: 'object' },
63
- },
64
- },
65
- };
66
- const result = generateSwaggerJsonFromApiSchema({ api });
67
- // Check path parameters
68
- const parameters = result.paths?.['/api/users/{userId}/posts/{postId}']?.get?.parameters;
69
- expect(parameters).toHaveLength(2);
70
- expect(parameters?.[0].name).toBe('userId');
71
- expect(parameters?.[0].in).toBe('path');
72
- expect(parameters?.[0].required).toBe(true);
73
- expect(parameters?.[1].name).toBe('postId');
74
- expect(parameters?.[1].in).toBe('path');
75
- expect(parameters?.[1].required).toBe(true);
76
- });
77
- it('Should handle different HTTP methods correctly', () => {
78
- const api = {
79
- GET: {
80
- '/api/resource': {
81
- path: '/api/resource',
82
- isAuthenticated: false,
83
- schemaName: 'Resource',
84
- schema: { type: 'object' },
85
- },
86
- },
87
- POST: {
88
- '/api/resource': {
89
- path: '/api/resource',
90
- isAuthenticated: true,
91
- schemaName: 'ResourceInput',
92
- schema: { type: 'object' },
93
- },
94
- },
95
- PUT: {
96
- '/api/resource/:id': {
97
- path: '/api/resource/:id',
98
- isAuthenticated: true,
99
- schemaName: 'ResourceUpdate',
100
- schema: { type: 'object' },
101
- },
102
- },
103
- DELETE: {
104
- '/api/resource/:id': {
105
- path: '/api/resource/:id',
106
- isAuthenticated: true,
107
- schemaName: 'ResourceDelete',
108
- schema: { type: 'object' },
109
- },
110
- },
111
- };
112
- const result = generateSwaggerJsonFromApiSchema({ api });
113
- // Check multiple methods on same path
114
- expect(result.paths?.['/api/resource'].get).toBeDefined();
115
- expect(result.paths?.['/api/resource'].post).toBeDefined();
116
- expect(result.paths?.['/api/resource/{id}'].put).toBeDefined();
117
- expect(result.paths?.['/api/resource/{id}'].delete).toBeDefined();
118
- // Verify security is applied correctly based on isAuthenticated
119
- expect(result.paths?.['/api/resource'].get?.security).toEqual([]);
120
- expect(result.paths?.['/api/resource'].post?.security).toEqual([{ cookieAuth: [] }]);
121
- });
122
- it('Should include schemas in components', () => {
123
- const testSchema = {
124
- type: 'object',
125
- properties: {
126
- id: { type: 'string' },
127
- name: { type: 'string' },
128
- age: { type: 'number' },
129
- },
130
- };
131
- const api = {
132
- GET: {
133
- '/api/test': {
134
- path: '/api/test',
135
- isAuthenticated: false,
136
- schemaName: 'TestModel',
137
- schema: testSchema,
138
- },
139
- },
140
- };
141
- const result = generateSwaggerJsonFromApiSchema({ api });
142
- // Check schema is included in components
143
- expect(result.components?.schemas?.TestModel).toEqual(testSchema);
144
- });
145
- it('Should handle responses with correct status codes and content types', () => {
146
- const api = {
147
- GET: {
148
- '/api/test': {
149
- path: '/api/test',
150
- isAuthenticated: false,
151
- schemaName: 'Test',
152
- schema: { type: 'object' },
153
- },
154
- },
155
- };
156
- const result = generateSwaggerJsonFromApiSchema({ api });
157
- // Check response structure
158
- const responses = result.paths?.['/api/test'].get?.responses;
159
- const response200 = responses?.['200'];
160
- expect(response200).toBeDefined();
161
- expect(response200.description).toBe('Successful operation');
162
- expect((response200.content?.['application/json']?.schema).$ref).toBe('#/components/schemas/Test');
163
- expect(responses?.['401']).toBeDefined();
164
- expect(responses?.['500']).toBeDefined();
165
- expect(responses?.['401'].description).toBe('Unauthorized');
166
- expect(responses?.['500'].description).toBe('Internal server error');
167
- });
168
- it('Should use empty path object if it does not exist - for code coverage', () => {
169
- const api = {
170
- GET: {
171
- '/api/test': {
172
- path: '/api/test',
173
- isAuthenticated: false,
174
- schemaName: 'Test',
175
- schema: { type: 'object' },
176
- },
177
- },
178
- };
179
- const result = generateSwaggerJsonFromApiSchema({ api });
180
- // This test is mainly for coverage, checking that the path initialization logic works
181
- expect(result.paths?.['/api/test']).toBeDefined();
182
- });
183
- it('Should handle endpoints without schemas', () => {
184
- const api = {
185
- GET: {
186
- '/api/no-schema': {
187
- path: '/api/no-schema',
188
- isAuthenticated: false,
189
- schemaName: 'EmptySchema',
190
- schema: null,
191
- },
192
- },
193
- };
194
- const result = generateSwaggerJsonFromApiSchema({ api });
195
- // Should still create the path without errors
196
- expect(result.paths?.['/api/no-schema']).toBeDefined();
197
- expect(result.paths?.['/api/no-schema'].get).toBeDefined();
198
- });
199
- it('Should use operationId based on method and path', () => {
200
- const api = {
201
- GET: {
202
- '/api/users/:id/profile': {
203
- path: '/api/users/:id/profile',
204
- isAuthenticated: false,
205
- schemaName: 'UserProfile',
206
- schema: { type: 'object' },
207
- },
208
- },
209
- };
210
- const result = generateSwaggerJsonFromApiSchema({ api });
211
- // Check operationId format
212
- expect(result.paths?.['/api/users/{id}/profile'].get?.operationId).toBe('get_api_users_id_profile');
213
- });
214
- });
215
- //# sourceMappingURL=generate-swagger-json.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate-swagger-json.spec.js","sourceRoot":"","sources":["../../src/swagger/generate-swagger-json.spec.ts"],"names":[],"mappings":"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"}
@@ -1,42 +0,0 @@
1
- import { Injector } from '@furystack/inject'
2
- import { usingAsync } from '@furystack/utils'
3
- import type { IncomingMessage, ServerResponse } from 'http'
4
- import { describe, expect, it, vi } from 'vitest'
5
- import { HttpUserContext } from '../http-user-context.js'
6
- import { LoginAction } from './login.js'
7
-
8
- describe('LoginAction', () => {
9
- const request = { url: 'https://google.com' } as IncomingMessage
10
- const response = {} as ServerResponse
11
-
12
- it('Returns the provided user with 200 on success', async () => {
13
- const testUser = { Name: 'Userke' }
14
- await usingAsync(new Injector(), async (i) => {
15
- i.setExplicitInstance(
16
- {
17
- authenticateUser: vi.fn(async () => testUser),
18
- cookieLogin: vi.fn(async () => testUser),
19
- authentication: {},
20
- },
21
- HttpUserContext,
22
- )
23
- const result = await LoginAction({
24
- request,
25
- response,
26
- injector: i,
27
- getBody: async () => ({ username: 'testuser', password: 'alma' }),
28
- })
29
- expect(result.chunk).toEqual(testUser)
30
- expect(result.statusCode).toBe(200)
31
- })
32
- })
33
-
34
- it('Returns throw error with 400 on fail', async () => {
35
- await usingAsync(new Injector(), async (i) => {
36
- i.setExplicitInstance({ cookieLogin: async () => Promise.reject(new Error(':(')) }, HttpUserContext)
37
- await expect(
38
- LoginAction({ request, response, injector: i, getBody: async () => ({ username: '', password: '' }) }),
39
- ).rejects.toThrowError('Login Failed')
40
- })
41
- })
42
- })
@@ -1,32 +0,0 @@
1
- import type { User } from '@furystack/core'
2
- import { RequestError } from '@furystack/rest'
3
- import { sleepAsync } from '@furystack/utils'
4
-
5
- import { HttpUserContext } from '../http-user-context.js'
6
- import type { RequestAction } from '../request-action-implementation.js'
7
- import { JsonResult } from '../request-action-implementation.js'
8
-
9
- /**
10
- * Action that logs in the current user.
11
- * Should be called with a JSON POST body with `username` and `password` fields.
12
- * Returns the current user instance.
13
- *
14
- * @deprecated Use `createPasswordLoginAction(createCookieLoginStrategy(injector))` instead.
15
- * This static action resolves services from the request-scoped injector on
16
- * every call; the factory approach captures them once at setup time.
17
- */
18
- export const LoginAction: RequestAction<{
19
- result: User
20
- body: { username: string; password: string }
21
- }> = async ({ injector, getBody, response }) => {
22
- const userContext = injector.getInstance(HttpUserContext)
23
- const body = await getBody()
24
- try {
25
- const user = await userContext.authenticateUser(body.username, body.password)
26
- await userContext.cookieLogin(user, response)
27
- return JsonResult(user, 200)
28
- } catch {
29
- await sleepAsync(Math.random() * 1000)
30
- throw new RequestError('Login Failed', 400)
31
- }
32
- }