moonflower 0.9.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/.eslintrc.js +26 -0
- package/.prettierrc.js +7 -0
- package/README.md +383 -0
- package/cli/cli.ts +59 -0
- package/cli/entry.cjs +3 -0
- package/cli/prettyprint.ts +16 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +79 -0
- package/dist/cli/prettyprint.d.ts +4 -0
- package/dist/cli/prettyprint.d.ts.map +1 -0
- package/dist/cli/prettyprint.js +18 -0
- package/dist/src/errors/BaseHttpError.d.ts +13 -0
- package/dist/src/errors/BaseHttpError.d.ts.map +1 -0
- package/dist/src/errors/BaseHttpError.js +13 -0
- package/dist/src/errors/HttpErrorHandler.d.ts +3 -0
- package/dist/src/errors/HttpErrorHandler.d.ts.map +1 -0
- package/dist/src/errors/HttpErrorHandler.js +23 -0
- package/dist/src/errors/UserFacingErrors.d.ts +11 -0
- package/dist/src/errors/UserFacingErrors.d.ts.map +1 -0
- package/dist/src/errors/UserFacingErrors.js +23 -0
- package/dist/src/hooks/authentication/useAuth.d.ts +3 -0
- package/dist/src/hooks/authentication/useAuth.d.ts.map +1 -0
- package/dist/src/hooks/authentication/useAuth.js +7 -0
- package/dist/src/hooks/authentication/useOptionalAuth.d.ts +3 -0
- package/dist/src/hooks/authentication/useOptionalAuth.d.ts.map +1 -0
- package/dist/src/hooks/authentication/useOptionalAuth.js +16 -0
- package/dist/src/hooks/useApiEndpoint.d.ts +8 -0
- package/dist/src/hooks/useApiEndpoint.d.ts.map +1 -0
- package/dist/src/hooks/useApiEndpoint.js +7 -0
- package/dist/src/hooks/useApiHeader/index.d.ts +2 -0
- package/dist/src/hooks/useApiHeader/index.d.ts.map +1 -0
- package/dist/src/hooks/useApiHeader/index.js +17 -0
- package/dist/src/hooks/useApiHeader/useApiHeader.d.ts +3 -0
- package/dist/src/hooks/useApiHeader/useApiHeader.d.ts.map +1 -0
- package/dist/src/hooks/useApiHeader/useApiHeader.js +6 -0
- package/dist/src/hooks/useApiHeader/useApiHeader.spec.data.d.ts +2 -0
- package/dist/src/hooks/useApiHeader/useApiHeader.spec.data.d.ts.map +1 -0
- package/dist/src/hooks/useApiHeader/useApiHeader.spec.data.js +22 -0
- package/dist/src/hooks/useCookieParams.d.ts +9 -0
- package/dist/src/hooks/useCookieParams.d.ts.map +1 -0
- package/dist/src/hooks/useCookieParams.js +50 -0
- package/dist/src/hooks/useExposeApiModel/index.d.ts +2 -0
- package/dist/src/hooks/useExposeApiModel/index.d.ts.map +1 -0
- package/dist/src/hooks/useExposeApiModel/index.js +17 -0
- package/dist/src/hooks/useExposeApiModel/useExposeApiModel.d.ts +3 -0
- package/dist/src/hooks/useExposeApiModel/useExposeApiModel.d.ts.map +1 -0
- package/dist/src/hooks/useExposeApiModel/useExposeApiModel.js +9 -0
- package/dist/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.d.ts +2 -0
- package/dist/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.d.ts.map +1 -0
- package/dist/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.js +16 -0
- package/dist/src/hooks/useHeaderParams.d.ts +12 -0
- package/dist/src/hooks/useHeaderParams.d.ts.map +1 -0
- package/dist/src/hooks/useHeaderParams.js +52 -0
- package/dist/src/hooks/usePathParams.d.ts +22 -0
- package/dist/src/hooks/usePathParams.d.ts.map +1 -0
- package/dist/src/hooks/usePathParams.js +46 -0
- package/dist/src/hooks/useQueryParams.d.ts +9 -0
- package/dist/src/hooks/useQueryParams.d.ts.map +1 -0
- package/dist/src/hooks/useQueryParams.js +50 -0
- package/dist/src/hooks/useRequestBody.d.ts +9 -0
- package/dist/src/hooks/useRequestBody.d.ts.map +1 -0
- package/dist/src/hooks/useRequestBody.js +59 -0
- package/dist/src/hooks/useRequestRawBody.d.ts +7 -0
- package/dist/src/hooks/useRequestRawBody.d.ts.map +1 -0
- package/dist/src/hooks/useRequestRawBody.js +34 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +33 -0
- package/dist/src/openapi/analyzerModule/analyzerModule.d.ts +18 -0
- package/dist/src/openapi/analyzerModule/analyzerModule.d.ts.map +1 -0
- package/dist/src/openapi/analyzerModule/analyzerModule.js +192 -0
- package/dist/src/openapi/analyzerModule/nodeParsers.d.ts +19 -0
- package/dist/src/openapi/analyzerModule/nodeParsers.d.ts.map +1 -0
- package/dist/src/openapi/analyzerModule/nodeParsers.js +521 -0
- package/dist/src/openapi/analyzerModule/parseEndpoint.d.ts +4 -0
- package/dist/src/openapi/analyzerModule/parseEndpoint.d.ts.map +1 -0
- package/dist/src/openapi/analyzerModule/parseEndpoint.js +246 -0
- package/dist/src/openapi/analyzerModule/parseExposedModels.d.ts +5 -0
- package/dist/src/openapi/analyzerModule/parseExposedModels.d.ts.map +1 -0
- package/dist/src/openapi/analyzerModule/parseExposedModels.js +32 -0
- package/dist/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.d.ts +2 -0
- package/dist/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.d.ts.map +1 -0
- package/dist/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.js +400 -0
- package/dist/src/openapi/analyzerModule/types.d.ts +53 -0
- package/dist/src/openapi/analyzerModule/types.d.ts.map +1 -0
- package/dist/src/openapi/analyzerModule/types.js +2 -0
- package/dist/src/openapi/discoveryModule/discoverImports/discoverImports.d.ts +8 -0
- package/dist/src/openapi/discoveryModule/discoverImports/discoverImports.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/discoverImports/discoverImports.js +33 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.d.ts +4 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.js +8 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.d.ts +4 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.js +8 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.d.ts +17 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.js +80 -0
- package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.d.ts +6 -0
- package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.js +31 -0
- package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.d.ts +5 -0
- package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.js +38 -0
- package/dist/src/openapi/discoveryModule/index.d.ts +3 -0
- package/dist/src/openapi/discoveryModule/index.d.ts.map +1 -0
- package/dist/src/openapi/discoveryModule/index.js +18 -0
- package/dist/src/openapi/generatorModule/generateComponentSchemas.d.ts +4 -0
- package/dist/src/openapi/generatorModule/generateComponentSchemas.d.ts.map +1 -0
- package/dist/src/openapi/generatorModule/generateComponentSchemas.js +12 -0
- package/dist/src/openapi/generatorModule/generatePaths.d.ts +10 -0
- package/dist/src/openapi/generatorModule/generatePaths.d.ts.map +1 -0
- package/dist/src/openapi/generatorModule/generatePaths.js +116 -0
- package/dist/src/openapi/generatorModule/generatorModule.d.ts +16 -0
- package/dist/src/openapi/generatorModule/generatorModule.d.ts.map +1 -0
- package/dist/src/openapi/generatorModule/generatorModule.js +18 -0
- package/dist/src/openapi/generatorModule/getSchema.d.ts +36 -0
- package/dist/src/openapi/generatorModule/getSchema.d.ts.map +1 -0
- package/dist/src/openapi/generatorModule/getSchema.js +133 -0
- package/dist/src/openapi/generatorModule/index.d.ts +5 -0
- package/dist/src/openapi/generatorModule/index.d.ts.map +1 -0
- package/dist/src/openapi/generatorModule/index.js +20 -0
- package/dist/src/openapi/generatorModule/test/openApiGenerator.spec.data.d.ts +515 -0
- package/dist/src/openapi/generatorModule/test/openApiGenerator.spec.data.d.ts.map +1 -0
- package/dist/src/openapi/generatorModule/test/openApiGenerator.spec.data.js +1119 -0
- package/dist/src/openapi/initOpenApiEngine.d.ts +4 -0
- package/dist/src/openapi/initOpenApiEngine.d.ts.map +1 -0
- package/dist/src/openapi/initOpenApiEngine.js +14 -0
- package/dist/src/openapi/manager/OpenApiManager.d.ts +67 -0
- package/dist/src/openapi/manager/OpenApiManager.d.ts.map +1 -0
- package/dist/src/openapi/manager/OpenApiManager.js +86 -0
- package/dist/src/openapi/router/OpenApiRouter.d.ts +4 -0
- package/dist/src/openapi/router/OpenApiRouter.d.ts.map +1 -0
- package/dist/src/openapi/router/OpenApiRouter.js +11 -0
- package/dist/src/openapi/types.d.ts +81 -0
- package/dist/src/openapi/types.d.ts.map +1 -0
- package/dist/src/openapi/types.js +2 -0
- package/dist/src/router/Router.d.ts +23 -0
- package/dist/src/router/Router.d.ts.map +1 -0
- package/dist/src/router/Router.js +81 -0
- package/dist/src/router/responseValueToJson.d.ts +2 -0
- package/dist/src/router/responseValueToJson.d.ts.map +1 -0
- package/dist/src/router/responseValueToJson.js +10 -0
- package/dist/src/setupTests.d.ts +1 -0
- package/dist/src/setupTests.d.ts.map +1 -0
- package/dist/src/setupTests.js +3 -0
- package/dist/src/test/TestAppRouter.d.ts +8 -0
- package/dist/src/test/TestAppRouter.d.ts.map +1 -0
- package/dist/src/test/TestAppRouter.js +58 -0
- package/dist/src/test/app.d.ts +3 -0
- package/dist/src/test/app.d.ts.map +1 -0
- package/dist/src/test/app.js +41 -0
- package/dist/src/utils/TypeUtils.d.ts +22 -0
- package/dist/src/utils/TypeUtils.d.ts.map +1 -0
- package/dist/src/utils/TypeUtils.js +2 -0
- package/dist/src/utils/fromZodSchema.d.ts +2 -0
- package/dist/src/utils/fromZodSchema.d.ts.map +1 -0
- package/dist/src/utils/fromZodSchema.js +6 -0
- package/dist/src/utils/loadTestData.d.ts +2 -0
- package/dist/src/utils/loadTestData.d.ts.map +1 -0
- package/dist/src/utils/loadTestData.js +39 -0
- package/dist/src/utils/mockContext.d.ts +20 -0
- package/dist/src/utils/mockContext.d.ts.map +1 -0
- package/dist/src/utils/mockContext.js +85 -0
- package/dist/src/utils/nameOf.d.ts +5 -0
- package/dist/src/utils/nameOf.d.ts.map +1 -0
- package/dist/src/utils/nameOf.js +7 -0
- package/dist/src/utils/object.d.ts +7 -0
- package/dist/src/utils/object.d.ts.map +1 -0
- package/dist/src/utils/object.js +21 -0
- package/dist/src/utils/printers.d.ts +6 -0
- package/dist/src/utils/printers.d.ts.map +1 -0
- package/dist/src/utils/printers.js +76 -0
- package/dist/src/utils/validationMessages.d.ts +18 -0
- package/dist/src/utils/validationMessages.d.ts.map +1 -0
- package/dist/src/utils/validationMessages.js +43 -0
- package/dist/src/validators/BuiltInValidators.d.ts +61 -0
- package/dist/src/validators/BuiltInValidators.d.ts.map +1 -0
- package/dist/src/validators/BuiltInValidators.js +66 -0
- package/dist/src/validators/InternalParamWrappers.d.ts +5 -0
- package/dist/src/validators/InternalParamWrappers.d.ts.map +1 -0
- package/dist/src/validators/InternalParamWrappers.js +5 -0
- package/dist/src/validators/ParamWrappers.d.ts +11 -0
- package/dist/src/validators/ParamWrappers.d.ts.map +1 -0
- package/dist/src/validators/ParamWrappers.js +9 -0
- package/dist/src/validators/types.d.ts +18 -0
- package/dist/src/validators/types.d.ts.map +1 -0
- package/dist/src/validators/types.js +2 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +59 -0
- package/src/errors/BaseHttpError.ts +16 -0
- package/src/errors/HttpErrorHandler.ts +20 -0
- package/src/errors/UserFacingErrors.ts +39 -0
- package/src/hooks/authentication/useAuth.ts +8 -0
- package/src/hooks/authentication/useOptionalAuth.ts +17 -0
- package/src/hooks/useApiEndpoint.spec.ts +11 -0
- package/src/hooks/useApiEndpoint.ts +10 -0
- package/src/hooks/useApiHeader/index.ts +1 -0
- package/src/hooks/useApiHeader/useApiHeader.spec.data.ts +22 -0
- package/src/hooks/useApiHeader/useApiHeader.spec.ts +34 -0
- package/src/hooks/useApiHeader/useApiHeader.ts +6 -0
- package/src/hooks/useCookieParams.spec.ts +174 -0
- package/src/hooks/useCookieParams.ts +73 -0
- package/src/hooks/useExposeApiModel/index.ts +1 -0
- package/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.ts +48 -0
- package/src/hooks/useExposeApiModel/useExposeApiModel.spec.ts +388 -0
- package/src/hooks/useExposeApiModel/useExposeApiModel.ts +9 -0
- package/src/hooks/useHeaderParams.spec.ts +186 -0
- package/src/hooks/useHeaderParams.ts +83 -0
- package/src/hooks/usePathParams.spec.ts +161 -0
- package/src/hooks/usePathParams.ts +89 -0
- package/src/hooks/useQueryParams.spec.ts +224 -0
- package/src/hooks/useQueryParams.ts +73 -0
- package/src/hooks/useRequestBody.spec.ts +215 -0
- package/src/hooks/useRequestBody.ts +94 -0
- package/src/hooks/useRequestRawBody.spec.ts +154 -0
- package/src/hooks/useRequestRawBody.ts +56 -0
- package/src/index.ts +17 -0
- package/src/openapi/analyzerModule/analyzerModule.ts +228 -0
- package/src/openapi/analyzerModule/nodeParsers.ts +648 -0
- package/src/openapi/analyzerModule/parseEndpoint.ts +305 -0
- package/src/openapi/analyzerModule/parseExposedModels.ts +34 -0
- package/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.ts +521 -0
- package/src/openapi/analyzerModule/test/openApiAnalyzer.spec.ts +1043 -0
- package/src/openapi/analyzerModule/types.ts +72 -0
- package/src/openapi/discoveryModule/discoverImports/discoverImports.ts +43 -0
- package/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.ts +7 -0
- package/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.ts +7 -0
- package/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.spec.ts +36 -0
- package/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.ts +80 -0
- package/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.ts +42 -0
- package/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.ts +18 -0
- package/src/openapi/discoveryModule/discoverRouters/discoverRouters.ts +39 -0
- package/src/openapi/discoveryModule/index.ts +2 -0
- package/src/openapi/generatorModule/generateComponentSchemas.ts +12 -0
- package/src/openapi/generatorModule/generatePaths.ts +138 -0
- package/src/openapi/generatorModule/generatorModule.ts +17 -0
- package/src/openapi/generatorModule/getSchema.ts +169 -0
- package/src/openapi/generatorModule/index.ts +4 -0
- package/src/openapi/generatorModule/test/openApiGenerator.spec.data.ts +1119 -0
- package/src/openapi/generatorModule/test/openApiGenerator.spec.ts +783 -0
- package/src/openapi/initOpenApiEngine.ts +20 -0
- package/src/openapi/manager/OpenApiManager.ts +153 -0
- package/src/openapi/router/OpenApiRouter.ts +11 -0
- package/src/openapi/types.ts +86 -0
- package/src/router/Router.ts +123 -0
- package/src/router/responseValueToJson.ts +6 -0
- package/src/setupTests.ts +3 -0
- package/src/test/TestAppRouter.ts +76 -0
- package/src/test/app.spec.ts +130 -0
- package/src/test/app.ts +43 -0
- package/src/utils/TypeUtils.ts +51 -0
- package/src/utils/loadTestData.ts +15 -0
- package/src/utils/mockContext.ts +86 -0
- package/src/utils/nameOf.ts +7 -0
- package/src/utils/object.spec.ts +27 -0
- package/src/utils/object.ts +17 -0
- package/src/utils/printers.spec.ts +103 -0
- package/src/utils/printers.ts +49 -0
- package/src/utils/validationMessages.ts +65 -0
- package/src/validators/BuiltInValidators.ts +64 -0
- package/src/validators/InternalParamWrappers.ts +14 -0
- package/src/validators/ParamWrappers.ts +22 -0
- package/src/validators/types.ts +35 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +29 -0
- package/vite.config.ts +16 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BooleanValidator,
|
|
3
|
+
NumberValidator,
|
|
4
|
+
OptionalParam,
|
|
5
|
+
RequiredParam,
|
|
6
|
+
StringValidator,
|
|
7
|
+
useRequestBody,
|
|
8
|
+
ValidationError,
|
|
9
|
+
} from '..'
|
|
10
|
+
import { mockContext, mockContextBody } from '../utils/mockContext'
|
|
11
|
+
|
|
12
|
+
describe('useRequestBody', () => {
|
|
13
|
+
it('parses params correctly', () => {
|
|
14
|
+
const ctx = mockContextBody(mockContext(), {
|
|
15
|
+
stringParam: 'test_string',
|
|
16
|
+
numberParam: '12',
|
|
17
|
+
booleanParam: 'true',
|
|
18
|
+
objectParam: {
|
|
19
|
+
foo: 'aaa',
|
|
20
|
+
bar: 'bbb',
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const params = useRequestBody(ctx, {
|
|
25
|
+
stringParam: StringValidator,
|
|
26
|
+
numberParam: NumberValidator,
|
|
27
|
+
booleanParam: BooleanValidator,
|
|
28
|
+
objectParam: RequiredParam<{ foo: string; bar: string }>({
|
|
29
|
+
parse: (v) => JSON.parse(String(v)),
|
|
30
|
+
}),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
expect(params.stringParam).toEqual('test_string')
|
|
34
|
+
expect(params.numberParam).toEqual(12)
|
|
35
|
+
expect(params.booleanParam).toEqual(true)
|
|
36
|
+
expect(params.objectParam).toEqual({ foo: 'aaa', bar: 'bbb' })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('passes validation on valid parameter', () => {
|
|
40
|
+
const ctx = mockContextBody(mockContext(), {
|
|
41
|
+
testParam: '12',
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const params = useRequestBody(ctx, {
|
|
45
|
+
testParam: NumberValidator,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
expect(params.testParam).toEqual(12)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('passes validation on numeric zero parameter', () => {
|
|
52
|
+
const ctx = mockContextBody(mockContext(), {
|
|
53
|
+
testParam: 0,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const params = useRequestBody(ctx, {
|
|
57
|
+
testParam: NumberValidator,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
expect(params.testParam).toEqual(0)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('fails validation on invalid parameter', () => {
|
|
64
|
+
const test = () => {
|
|
65
|
+
const ctx = mockContextBody(mockContext(), {
|
|
66
|
+
testParam: 'qwerty',
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
useRequestBody(ctx, {
|
|
70
|
+
testParam: NumberValidator,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
expect(test).toThrow(ValidationError)
|
|
75
|
+
expect(test).toThrow("Failed body param validation: 'testParam'")
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('passes validation for nullable parameter', () => {
|
|
79
|
+
const ctx = mockContextBody(mockContext(), {
|
|
80
|
+
testParam: null,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const params = useRequestBody(ctx, {
|
|
84
|
+
testParam: RequiredParam({
|
|
85
|
+
parse: (v) => v,
|
|
86
|
+
validate: (v) => v === null,
|
|
87
|
+
}),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expect(params.testParam).toEqual(null)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('passes validation when optional parameter is not provided', () => {
|
|
94
|
+
const ctx = mockContextBody(mockContext(), {})
|
|
95
|
+
|
|
96
|
+
const params = useRequestBody(ctx, {
|
|
97
|
+
testParam: OptionalParam(NumberValidator),
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
expect(params.testParam).toEqual(undefined)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('fails validation when required parameter is not provided', () => {
|
|
104
|
+
const test = () => {
|
|
105
|
+
const ctx = mockContextBody(mockContext(), {})
|
|
106
|
+
|
|
107
|
+
useRequestBody(ctx, {
|
|
108
|
+
testParam: NumberValidator,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
expect(test).toThrow(ValidationError)
|
|
113
|
+
expect(test).toThrow("Missing body params: 'testParam'")
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('fails validation when no body is provided at all', () => {
|
|
117
|
+
const test = () => {
|
|
118
|
+
useRequestBody(mockContext(), {
|
|
119
|
+
testParam: NumberValidator,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
expect(test).toThrow(ValidationError)
|
|
124
|
+
expect(test).toThrow("Missing body params: 'testParam'")
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('passes prevalidation on valid parameter', () => {
|
|
128
|
+
const ctx = mockContextBody(mockContext(), {
|
|
129
|
+
testParam: 'valid',
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
const params = useRequestBody(ctx, {
|
|
133
|
+
testParam: RequiredParam({
|
|
134
|
+
prevalidate: (v) => v === 'valid',
|
|
135
|
+
parse: (v) => String(v),
|
|
136
|
+
}),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
expect(params.testParam).toEqual('valid')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('fails prevalidation on invalid parameter', () => {
|
|
143
|
+
const test = () => {
|
|
144
|
+
const ctx = mockContextBody(mockContext(), {
|
|
145
|
+
testParam: 'invalid',
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
useRequestBody(ctx, {
|
|
149
|
+
testParam: RequiredParam({
|
|
150
|
+
prevalidate: (v) => v === 'valid',
|
|
151
|
+
parse: (v) => String(v),
|
|
152
|
+
}),
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
expect(test).toThrow(ValidationError)
|
|
157
|
+
expect(test).toThrow("Failed body param validation: 'testParam'")
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('fails prevalidation on parse error', () => {
|
|
161
|
+
const test = () => {
|
|
162
|
+
const ctx = mockContextBody(mockContext(), {
|
|
163
|
+
testParam: 'not a json',
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
useRequestBody(ctx, {
|
|
167
|
+
testParam: RequiredParam<{ foo: 'aaa' }>({
|
|
168
|
+
parse: (v) => JSON.parse(String(v)),
|
|
169
|
+
}),
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
expect(test).toThrow(ValidationError)
|
|
174
|
+
expect(test).toThrow("Failed body param validation: 'testParam'")
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('sends an error message when validation fails', () => {
|
|
178
|
+
const test = () => {
|
|
179
|
+
const ctx = mockContextBody(mockContext(), {
|
|
180
|
+
testParam: 'invalid',
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
useRequestBody(ctx, {
|
|
184
|
+
testParam: RequiredParam({
|
|
185
|
+
prevalidate: (v) => v === 'valid',
|
|
186
|
+
parse: (v) => String(v),
|
|
187
|
+
description: 'Description',
|
|
188
|
+
errorMessage: 'Error message',
|
|
189
|
+
}),
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
expect(test).toThrow(ValidationError)
|
|
194
|
+
expect(test).toThrow("Failed body param validation: 'testParam' (Error message)")
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('sends the description when validation fails with no error message provided', () => {
|
|
198
|
+
const test = () => {
|
|
199
|
+
const ctx = mockContextBody(mockContext(), {
|
|
200
|
+
testParam: 'invalid',
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
useRequestBody(ctx, {
|
|
204
|
+
testParam: RequiredParam({
|
|
205
|
+
prevalidate: (v) => v === 'valid',
|
|
206
|
+
parse: (v) => String(v),
|
|
207
|
+
description: 'Description',
|
|
208
|
+
}),
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
expect(test).toThrow(ValidationError)
|
|
213
|
+
expect(test).toThrow("Failed body param validation: 'testParam' (Description)")
|
|
214
|
+
})
|
|
215
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ParameterizedContext } from 'koa'
|
|
2
|
+
|
|
3
|
+
import { ValidationError } from '../errors/UserFacingErrors'
|
|
4
|
+
import { keysOf } from '../utils/object'
|
|
5
|
+
import { getMissingParamMessage, getValidationResultMessage } from '../utils/validationMessages'
|
|
6
|
+
import { Validator } from '../validators/types'
|
|
7
|
+
|
|
8
|
+
type CheckIfOptional<T, B extends boolean | undefined> = B extends false ? T : T
|
|
9
|
+
|
|
10
|
+
type ValidatedData<T extends Record<string, Validator<any>>> = {
|
|
11
|
+
[K in keyof T]: CheckIfOptional<ReturnType<T[K]['parse']>, T[K]['optional']>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hook to access request body data in JSON or form formats.
|
|
16
|
+
*
|
|
17
|
+
* Supported content types:
|
|
18
|
+
* - `application/json`
|
|
19
|
+
* - `application/x-www-form-urlencoded`
|
|
20
|
+
*
|
|
21
|
+
* @param ctx Koa context
|
|
22
|
+
* @param validators Validator definitions
|
|
23
|
+
* @returns Validated parameters
|
|
24
|
+
*/
|
|
25
|
+
export const useRequestBody = <ValidatorsT extends Record<string, Validator<any>>>(
|
|
26
|
+
ctx: ParameterizedContext,
|
|
27
|
+
validators: ValidatorsT
|
|
28
|
+
): ValidatedData<ValidatorsT> => {
|
|
29
|
+
const providedParams = (ctx.request.body || {}) as Record<string, string | number | boolean | object>
|
|
30
|
+
const params = keysOf(validators).map((name) => ({
|
|
31
|
+
name,
|
|
32
|
+
validator: validators[name],
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
const missingParams = params.filter(
|
|
36
|
+
(param) => providedParams[param.name] === undefined && !validators[param.name].optional
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if (missingParams.length > 0) {
|
|
40
|
+
throw new ValidationError(
|
|
41
|
+
`Missing body params: ${missingParams.map((param) => getMissingParamMessage(param)).join(', ')}`
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const validationResults = params.map((param) => {
|
|
46
|
+
const paramValue = providedParams[param.name]
|
|
47
|
+
|
|
48
|
+
// Param is optional and is not provided - skip validation
|
|
49
|
+
if (paramValue === undefined) {
|
|
50
|
+
return { param, validated: true }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const convertedValue = (() => {
|
|
55
|
+
if (paramValue === null) {
|
|
56
|
+
return null
|
|
57
|
+
} else if (typeof paramValue === 'object') {
|
|
58
|
+
return JSON.stringify(paramValue)
|
|
59
|
+
}
|
|
60
|
+
return String(paramValue)
|
|
61
|
+
})()
|
|
62
|
+
const validatorObject = param.validator
|
|
63
|
+
const prevalidatorSuccess = !validatorObject.prevalidate || validatorObject.prevalidate(convertedValue)
|
|
64
|
+
const parsedValue = validatorObject.parse(convertedValue)
|
|
65
|
+
const validatorSuccess = !validatorObject.validate || validatorObject.validate(parsedValue)
|
|
66
|
+
return {
|
|
67
|
+
param,
|
|
68
|
+
validated: prevalidatorSuccess && validatorSuccess,
|
|
69
|
+
parsedValue,
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return { param, validated: false }
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const failedValidations = validationResults.filter((result) => !result.validated)
|
|
77
|
+
|
|
78
|
+
if (failedValidations.length > 0) {
|
|
79
|
+
throw new ValidationError(
|
|
80
|
+
`Failed body param validation: ${failedValidations
|
|
81
|
+
.map((result) => getValidationResultMessage(result.param))
|
|
82
|
+
.join(', ')}`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const successfulValidations = validationResults.filter((result) => result.validated)
|
|
87
|
+
|
|
88
|
+
const returnValue: Record<string, unknown> = {}
|
|
89
|
+
successfulValidations.forEach((result) => {
|
|
90
|
+
returnValue[result.param.name] = result.parsedValue
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return returnValue as ValidatedData<ValidatorsT>
|
|
94
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { NumberValidator, OptionalParam, RequiredParam, useRequestRawBody, ValidationError } from '..'
|
|
2
|
+
import { mockContext, mockContextBody, mockContextRawBody } from '../utils/mockContext'
|
|
3
|
+
|
|
4
|
+
describe('useRequestRawBody', () => {
|
|
5
|
+
it('parses param correctly', () => {
|
|
6
|
+
const ctx = mockContextBody(mockContext(), {
|
|
7
|
+
foo: 'aaa',
|
|
8
|
+
bar: 'bbb',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const params = useRequestRawBody(
|
|
12
|
+
ctx,
|
|
13
|
+
RequiredParam<{ foo: string; bar: string }>({
|
|
14
|
+
parse: (v) => JSON.parse(String(v)),
|
|
15
|
+
})
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
expect(params.foo).toEqual('aaa')
|
|
19
|
+
expect(params.bar).toEqual('bbb')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('passes validation on valid parameter', () => {
|
|
23
|
+
const ctx = mockContextRawBody(mockContext(), '12')
|
|
24
|
+
|
|
25
|
+
const rawBody = useRequestRawBody(ctx, NumberValidator)
|
|
26
|
+
|
|
27
|
+
expect(rawBody).toEqual(12)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('fails validation on invalid parameter', () => {
|
|
31
|
+
const test = () => {
|
|
32
|
+
const ctx = mockContextRawBody(mockContext(), 'not a number')
|
|
33
|
+
|
|
34
|
+
useRequestRawBody(ctx, NumberValidator)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
expect(test).toThrow(ValidationError)
|
|
38
|
+
expect(test).toThrow('Failed request body validation (Must be a valid number).')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('passes validation when optional parameter is not provided', () => {
|
|
42
|
+
const params = useRequestRawBody(mockContext(), OptionalParam(NumberValidator))
|
|
43
|
+
|
|
44
|
+
expect(params).toEqual(undefined)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('fails validation when required parameter is not provided', () => {
|
|
48
|
+
const test = () => {
|
|
49
|
+
useRequestRawBody(mockContext(), NumberValidator)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
expect(test).toThrow(ValidationError)
|
|
53
|
+
expect(test).toThrow('Missing request body (Any numeric value).')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('fails validation for inline validator', () => {
|
|
57
|
+
const test = () => {
|
|
58
|
+
useRequestRawBody(
|
|
59
|
+
mockContext(),
|
|
60
|
+
RequiredParam({
|
|
61
|
+
parse: (v) => Number(v),
|
|
62
|
+
validate: (v) => !Number.isNaN(v),
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
expect(test).toThrow(ValidationError)
|
|
68
|
+
expect(test).toThrow('Missing request body.')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('passes prevalidation on valid parameter', () => {
|
|
72
|
+
const ctx = mockContextRawBody(mockContext(), 'valid')
|
|
73
|
+
|
|
74
|
+
const params = useRequestRawBody(
|
|
75
|
+
ctx,
|
|
76
|
+
RequiredParam({
|
|
77
|
+
prevalidate: (v) => v === 'valid',
|
|
78
|
+
parse: (v) => String(v),
|
|
79
|
+
})
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
expect(params).toEqual('valid')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('fails prevalidation on invalid parameter', () => {
|
|
86
|
+
const test = () => {
|
|
87
|
+
const ctx = mockContextRawBody(mockContext(), 'invalid')
|
|
88
|
+
|
|
89
|
+
useRequestRawBody(
|
|
90
|
+
ctx,
|
|
91
|
+
RequiredParam({
|
|
92
|
+
prevalidate: (v) => v === 'valid',
|
|
93
|
+
parse: (v) => String(v),
|
|
94
|
+
})
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
expect(test).toThrow(ValidationError)
|
|
99
|
+
expect(test).toThrow('Failed request body validation.')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('fails prevalidation on parse error', () => {
|
|
103
|
+
const test = () => {
|
|
104
|
+
const ctx = mockContextRawBody(mockContext(), 'not a valid json')
|
|
105
|
+
|
|
106
|
+
useRequestRawBody(
|
|
107
|
+
ctx,
|
|
108
|
+
RequiredParam<{ foo: 'aaa' }>({
|
|
109
|
+
parse: (v) => JSON.parse(String(v)),
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expect(test).toThrow(ValidationError)
|
|
115
|
+
expect(test).toThrow('Failed request body validation.')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('sends an error message when validation fails', () => {
|
|
119
|
+
const test = () => {
|
|
120
|
+
const ctx = mockContextRawBody(mockContext(), 'invalid')
|
|
121
|
+
|
|
122
|
+
useRequestRawBody(
|
|
123
|
+
ctx,
|
|
124
|
+
RequiredParam({
|
|
125
|
+
prevalidate: (v) => v === 'valid',
|
|
126
|
+
parse: (v) => String(v),
|
|
127
|
+
description: 'Description',
|
|
128
|
+
errorMessage: 'Error message',
|
|
129
|
+
})
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
expect(test).toThrow(ValidationError)
|
|
134
|
+
expect(test).toThrow('Failed request body validation (Error message).')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('sends the description when validation fails with no error message provided', () => {
|
|
138
|
+
const test = () => {
|
|
139
|
+
const ctx = mockContextRawBody(mockContext(), 'invalid')
|
|
140
|
+
|
|
141
|
+
useRequestRawBody(
|
|
142
|
+
ctx,
|
|
143
|
+
RequiredParam({
|
|
144
|
+
prevalidate: (v) => v === 'valid',
|
|
145
|
+
parse: (v) => String(v),
|
|
146
|
+
description: 'Description',
|
|
147
|
+
})
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
expect(test).toThrow(ValidationError)
|
|
152
|
+
expect(test).toThrow('Failed request body validation (Description).')
|
|
153
|
+
})
|
|
154
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ParameterizedContext } from 'koa'
|
|
2
|
+
|
|
3
|
+
import { ValidationError } from '../errors/UserFacingErrors'
|
|
4
|
+
import { getFailedRawBodyValidationMessage, getMissingRawBodyMessage } from '../utils/validationMessages'
|
|
5
|
+
import { Validator } from '../validators/types'
|
|
6
|
+
|
|
7
|
+
type CheckIfOptional<T, B extends boolean | undefined> = B extends false ? T : T | undefined
|
|
8
|
+
|
|
9
|
+
type ValidatedData<T extends Validator<any>> = CheckIfOptional<ReturnType<T['parse']>, T['optional']>
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hook to access request body data without parsing into an object.
|
|
13
|
+
*
|
|
14
|
+
* Supported content types:
|
|
15
|
+
* - `text/plain`
|
|
16
|
+
* - `application/json`
|
|
17
|
+
* - `application/x-www-form-urlencoded`
|
|
18
|
+
*
|
|
19
|
+
* @param ctx Koa context
|
|
20
|
+
* @param validators Validator definitions
|
|
21
|
+
* @returns Validated parameters
|
|
22
|
+
*/
|
|
23
|
+
export const useRequestRawBody = <ValidatorT extends Validator<any>>(
|
|
24
|
+
ctx: ParameterizedContext,
|
|
25
|
+
validator: ValidatorT
|
|
26
|
+
): ValidatedData<ValidatorT> => {
|
|
27
|
+
const providedBody = ctx.request.rawBody
|
|
28
|
+
const isOptional = validator.optional
|
|
29
|
+
|
|
30
|
+
if (!isOptional && !providedBody) {
|
|
31
|
+
throw new ValidationError(getMissingRawBodyMessage(validator))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (isOptional && !providedBody) {
|
|
35
|
+
return undefined as ValidatedData<ValidatorT>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const validationResult = (() => {
|
|
39
|
+
try {
|
|
40
|
+
const prevalidatorSuccess = !validator.prevalidate || validator.prevalidate(providedBody)
|
|
41
|
+
const parsedValue = validator.parse(providedBody)
|
|
42
|
+
const validatorSuccess = !validator.validate || validator.validate(parsedValue)
|
|
43
|
+
return {
|
|
44
|
+
validated: prevalidatorSuccess && validatorSuccess,
|
|
45
|
+
parsedValue,
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return { validated: false }
|
|
49
|
+
}
|
|
50
|
+
})()
|
|
51
|
+
|
|
52
|
+
if (!validationResult.validated) {
|
|
53
|
+
throw new ValidationError(getFailedRawBodyValidationMessage(validator))
|
|
54
|
+
}
|
|
55
|
+
return validationResult.parsedValue as ValidatedData<ValidatorT>
|
|
56
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './errors/HttpErrorHandler'
|
|
2
|
+
export * from './errors/UserFacingErrors'
|
|
3
|
+
export * from './hooks/authentication/useAuth'
|
|
4
|
+
export * from './hooks/authentication/useOptionalAuth'
|
|
5
|
+
export * from './hooks/useApiEndpoint'
|
|
6
|
+
export * from './hooks/useApiHeader/useApiHeader'
|
|
7
|
+
export * from './hooks/useCookieParams'
|
|
8
|
+
export * from './hooks/useExposeApiModel'
|
|
9
|
+
export * from './hooks/useHeaderParams'
|
|
10
|
+
export * from './hooks/usePathParams'
|
|
11
|
+
export * from './hooks/useQueryParams'
|
|
12
|
+
export * from './hooks/useRequestBody'
|
|
13
|
+
export * from './hooks/useRequestRawBody'
|
|
14
|
+
export * from './openapi/initOpenApiEngine'
|
|
15
|
+
export * from './router/Router'
|
|
16
|
+
export * from './validators/BuiltInValidators'
|
|
17
|
+
export * from './validators/ParamWrappers'
|