@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.
- package/CHANGELOG.md +44 -0
- package/esm/actions/index.d.ts +0 -1
- package/esm/actions/index.d.ts.map +1 -1
- package/esm/actions/index.js +0 -1
- package/esm/actions/index.js.map +1 -1
- package/esm/api-manager.d.ts.map +1 -1
- package/esm/api-manager.js +1 -2
- package/esm/api-manager.js.map +1 -1
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +0 -14
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -1
- package/esm/endpoint-generators/create-get-openapi-document-action.js +0 -23
- package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -1
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +0 -2
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -1
- package/esm/openapi/generate-openapi-document.d.ts +0 -8
- package/esm/openapi/generate-openapi-document.d.ts.map +1 -1
- package/esm/openapi/generate-openapi-document.js +0 -2
- package/esm/openapi/generate-openapi-document.js.map +1 -1
- package/esm/rest-service.integration.spec.js +3 -2
- package/esm/rest-service.integration.spec.js.map +1 -1
- package/esm/validate.integration.spec.js +0 -55
- package/esm/validate.integration.spec.js.map +1 -1
- package/package.json +12 -12
- package/src/actions/index.ts +0 -1
- package/src/api-manager.ts +1 -5
- package/src/endpoint-generators/create-get-openapi-document-action.ts +0 -36
- package/src/endpoint-generators/with-schema-and-openapi-action.ts +0 -3
- package/src/openapi/generate-openapi-document.ts +0 -3
- package/src/rest-service.integration.spec.ts +3 -2
- package/src/validate.integration.spec.ts +0 -62
- package/esm/actions/login-action.spec.d.ts +0 -2
- package/esm/actions/login-action.spec.d.ts.map +0 -1
- package/esm/actions/login-action.spec.js +0 -34
- package/esm/actions/login-action.spec.js.map +0 -1
- package/esm/actions/login.d.ts +0 -19
- package/esm/actions/login.d.ts.map +0 -1
- package/esm/actions/login.js +0 -27
- package/esm/actions/login.js.map +0 -1
- package/esm/swagger/generate-swagger-json.spec.d.ts +0 -2
- package/esm/swagger/generate-swagger-json.spec.d.ts.map +0 -1
- package/esm/swagger/generate-swagger-json.spec.js +0 -215
- package/esm/swagger/generate-swagger-json.spec.js.map +0 -1
- package/src/actions/login-action.spec.ts +0 -42
- package/src/actions/login.ts +0 -32
- package/src/swagger/generate-swagger-json.spec.ts +0 -250
package/src/api-manager.ts
CHANGED
|
@@ -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>
|
|
@@ -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,
|
|
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':
|
|
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 +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"}
|
package/esm/actions/login.d.ts
DELETED
|
@@ -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"}
|
package/esm/actions/login.js
DELETED
|
@@ -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
|
package/esm/actions/login.js.map
DELETED
|
@@ -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 +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
|
-
})
|
package/src/actions/login.ts
DELETED
|
@@ -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
|
-
}
|