@prosopo/api-express-router 3.2.0 → 3.2.2
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/.turbo/turbo-build$colon$cjs.log +3 -6
- package/.turbo/turbo-build$colon$tsc.log +21 -24
- package/.turbo/turbo-build.log +3 -9
- package/CHANGELOG.md +30 -0
- package/LICENSE +201 -0
- package/package.json +62 -59
- package/src/apiExpressRouterFactory.ts +0 -68
- package/src/endpointAdapter/apiExpressDefaultEndpointAdapter.ts +0 -92
- package/src/endpointAdapter/apiExpressEndpointAdapter.ts +0 -28
- package/src/errorHandler.ts +0 -33
- package/src/index.ts +0 -38
- package/src/middlewares/authMiddleware.ts +0 -150
- package/src/middlewares/jwtReplayGuard.ts +0 -56
- package/src/middlewares/requestLoggerMiddleware.ts +0 -104
- package/src/tests/apiExpressRouter.test-d.ts +0 -196
- package/src/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.ts +0 -414
- package/src/tests/unit/apiExpressRouterFactory.unit.test.ts +0 -215
- package/src/tests/unit/errorHandler.unit.test.ts +0 -190
- package/src/tests/unit/index.unit.test.ts +0 -95
- package/src/tests/unit/middlewares/authMiddleware.unit.test.ts +0 -449
- package/src/tests/unit/middlewares/jwtReplayGuard.unit.test.ts +0 -53
- package/src/tests/unit/middlewares/requestLoggerMiddleware.unit.test.ts +0 -368
- package/src/tests/unit/testDoubles.ts +0 -39
- package/tsconfig.cjs.json +0 -37
- package/tsconfig.json +0 -44
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.types.json +0 -9
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import { ProsopoApiError, ProsopoEnvError } from "@prosopo/common";
|
|
16
|
-
import { loadI18next } from "@prosopo/locale";
|
|
17
|
-
import type { NextFunction, Request, Response } from "express";
|
|
18
|
-
import { describe, expect, it, vi } from "vitest";
|
|
19
|
-
import { ZodError } from "zod";
|
|
20
|
-
import { handleErrors } from "../../errorHandler.js";
|
|
21
|
-
|
|
22
|
-
describe("handleErrors", async () => {
|
|
23
|
-
const i18n = await loadI18next(true);
|
|
24
|
-
await i18n.changeLanguage("en");
|
|
25
|
-
|
|
26
|
-
it("should handle ProsopoApiError", async () => {
|
|
27
|
-
const mockRequest = { i18n } as unknown as Request;
|
|
28
|
-
const mockResponse = {
|
|
29
|
-
writeHead: vi.fn().mockReturnThis(),
|
|
30
|
-
set: vi.fn().mockReturnThis(),
|
|
31
|
-
status: vi.fn().mockReturnThis(),
|
|
32
|
-
send: vi.fn(),
|
|
33
|
-
end: vi.fn(),
|
|
34
|
-
} as unknown as Response;
|
|
35
|
-
const mockNext = vi.fn() as unknown as NextFunction;
|
|
36
|
-
|
|
37
|
-
const error = new ProsopoApiError("CONTRACT.INVALID_DATA_FORMAT", {
|
|
38
|
-
context: { code: 400 },
|
|
39
|
-
i18n,
|
|
40
|
-
});
|
|
41
|
-
console.log(error);
|
|
42
|
-
|
|
43
|
-
handleErrors(error, mockRequest, mockResponse, mockNext);
|
|
44
|
-
|
|
45
|
-
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
46
|
-
"content-type",
|
|
47
|
-
"application/json",
|
|
48
|
-
);
|
|
49
|
-
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
50
|
-
error: {
|
|
51
|
-
code: 400,
|
|
52
|
-
key: "CONTRACT.INVALID_DATA_FORMAT",
|
|
53
|
-
message: "Invalid data format",
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
57
|
-
expect(mockResponse.end).toHaveBeenCalled();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should not return SyntaxError", async () => {
|
|
61
|
-
const mockRequest = { i18n } as unknown as Request;
|
|
62
|
-
const mockResponse = {
|
|
63
|
-
writeHead: vi.fn().mockReturnThis(),
|
|
64
|
-
set: vi.fn().mockReturnThis(),
|
|
65
|
-
status: vi.fn().mockReturnThis(),
|
|
66
|
-
send: vi.fn(),
|
|
67
|
-
end: vi.fn(),
|
|
68
|
-
} as unknown as Response;
|
|
69
|
-
const mockNext = vi.fn() as unknown as NextFunction;
|
|
70
|
-
|
|
71
|
-
const message = "Unknown API error";
|
|
72
|
-
const error = new SyntaxError(message);
|
|
73
|
-
|
|
74
|
-
handleErrors(error, mockRequest, mockResponse, mockNext);
|
|
75
|
-
|
|
76
|
-
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
77
|
-
"content-type",
|
|
78
|
-
"application/json",
|
|
79
|
-
);
|
|
80
|
-
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
81
|
-
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
82
|
-
error: {
|
|
83
|
-
message,
|
|
84
|
-
key: "API.UNKNOWN",
|
|
85
|
-
code: 400,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
expect(mockResponse.end).toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("should handle ZodError", () => {
|
|
92
|
-
const mockRequest = { i18n } as unknown as Request;
|
|
93
|
-
const mockResponse = {
|
|
94
|
-
writeHead: vi.fn().mockReturnThis(),
|
|
95
|
-
set: vi.fn().mockReturnThis(),
|
|
96
|
-
status: vi.fn().mockReturnThis(),
|
|
97
|
-
send: vi.fn(),
|
|
98
|
-
end: vi.fn(),
|
|
99
|
-
} as unknown as Response;
|
|
100
|
-
const mockNext = vi.fn() as unknown as NextFunction;
|
|
101
|
-
|
|
102
|
-
const zodError = {
|
|
103
|
-
code: "custom" as const,
|
|
104
|
-
message: "Invalid input",
|
|
105
|
-
path: ["some", "variable"],
|
|
106
|
-
};
|
|
107
|
-
const error = new ZodError([zodError]);
|
|
108
|
-
|
|
109
|
-
handleErrors(error, mockRequest, mockResponse, mockNext);
|
|
110
|
-
|
|
111
|
-
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
112
|
-
"content-type",
|
|
113
|
-
"application/json",
|
|
114
|
-
);
|
|
115
|
-
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
116
|
-
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
117
|
-
error: { code: 400, key: "API.INVALID_BODY", message: [zodError] },
|
|
118
|
-
});
|
|
119
|
-
expect(mockResponse.end).toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("should unwrap nested ProsopoBaseError", async () => {
|
|
123
|
-
const mockRequest = { i18n } as unknown as Request;
|
|
124
|
-
const mockResponse = {
|
|
125
|
-
writeHead: vi.fn().mockReturnThis(),
|
|
126
|
-
set: vi.fn().mockReturnThis(),
|
|
127
|
-
status: vi.fn().mockReturnThis(),
|
|
128
|
-
send: vi.fn(),
|
|
129
|
-
end: vi.fn(),
|
|
130
|
-
} as unknown as Response;
|
|
131
|
-
const mockNext = vi.fn() as unknown as NextFunction;
|
|
132
|
-
|
|
133
|
-
const envError = new ProsopoEnvError("GENERAL.ENVIRONMENT_NOT_READY", {
|
|
134
|
-
i18n,
|
|
135
|
-
});
|
|
136
|
-
const apiError = new ProsopoApiError(envError);
|
|
137
|
-
|
|
138
|
-
handleErrors(apiError, mockRequest, mockResponse, mockNext);
|
|
139
|
-
|
|
140
|
-
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
141
|
-
"content-type",
|
|
142
|
-
"application/json",
|
|
143
|
-
);
|
|
144
|
-
expect(mockResponse.status).toHaveBeenCalledWith(500);
|
|
145
|
-
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
146
|
-
error: {
|
|
147
|
-
code: 500,
|
|
148
|
-
key: "GENERAL.ENVIRONMENT_NOT_READY",
|
|
149
|
-
message: "Environment not ready",
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
expect(mockResponse.end).toHaveBeenCalled();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("should unwrap nested ProsopoBaseErrors but not an Error that is nested inside them", async () => {
|
|
156
|
-
const mockRequest = { i18n } as unknown as Request;
|
|
157
|
-
const mockResponse = {
|
|
158
|
-
writeHead: vi.fn().mockReturnThis(),
|
|
159
|
-
set: vi.fn().mockReturnThis(),
|
|
160
|
-
status: vi.fn().mockReturnThis(),
|
|
161
|
-
send: vi.fn(),
|
|
162
|
-
end: vi.fn(),
|
|
163
|
-
} as unknown as Response;
|
|
164
|
-
const mockNext = vi.fn() as unknown as NextFunction;
|
|
165
|
-
const code = 400;
|
|
166
|
-
const key = "API.UNKNOWN";
|
|
167
|
-
|
|
168
|
-
const error = new Error("Some error");
|
|
169
|
-
const apiError = new ProsopoApiError(key, {
|
|
170
|
-
context: { code, error },
|
|
171
|
-
i18n,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
handleErrors(apiError, mockRequest, mockResponse, mockNext);
|
|
175
|
-
|
|
176
|
-
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
177
|
-
"content-type",
|
|
178
|
-
"application/json",
|
|
179
|
-
);
|
|
180
|
-
expect(mockResponse.status).toHaveBeenCalledWith(code);
|
|
181
|
-
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
182
|
-
error: {
|
|
183
|
-
code,
|
|
184
|
-
key,
|
|
185
|
-
message: "Unknown API error",
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
expect(mockResponse.end).toHaveBeenCalled();
|
|
189
|
-
});
|
|
190
|
-
});
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import type { ApiRoutes, ApiRoutesProvider } from "@prosopo/api-route";
|
|
16
|
-
import { LogLevel } from "@prosopo/logger";
|
|
17
|
-
import type { NextFunction, Request, Response } from "express";
|
|
18
|
-
import { describe, expect, test, vi } from "vitest";
|
|
19
|
-
import {
|
|
20
|
-
apiExpressRouterFactory,
|
|
21
|
-
authMiddleware,
|
|
22
|
-
createApiExpressDefaultEndpointAdapter,
|
|
23
|
-
handleErrors,
|
|
24
|
-
requestLoggerMiddleware,
|
|
25
|
-
} from "../../index.js";
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* The barrel is what every consuming package imports, so the shape of it is
|
|
29
|
-
* part of the contract — a missing re-export or a changed default breaks
|
|
30
|
-
* callers without touching any of the implementations.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
describe("createApiExpressDefaultEndpointAdapter", () => {
|
|
34
|
-
test("builds an adapter exposing handleRequest", () => {
|
|
35
|
-
const adapter = createApiExpressDefaultEndpointAdapter(LogLevel.enum.info);
|
|
36
|
-
expect(typeof adapter.handleRequest).toBe("function");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("builds a fresh adapter per call", () => {
|
|
40
|
-
expect(createApiExpressDefaultEndpointAdapter(LogLevel.enum.info)).not.toBe(
|
|
41
|
-
createApiExpressDefaultEndpointAdapter(LogLevel.enum.info),
|
|
42
|
-
);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("defaults the error status code so callers may omit it", () => {
|
|
46
|
-
expect(() =>
|
|
47
|
-
createApiExpressDefaultEndpointAdapter(LogLevel.enum.debug),
|
|
48
|
-
).not.toThrow();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("accepts an explicit error status code", () => {
|
|
52
|
-
expect(() =>
|
|
53
|
-
createApiExpressDefaultEndpointAdapter(LogLevel.enum.debug, 503),
|
|
54
|
-
).not.toThrow();
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe("apiExpressRouterFactory", () => {
|
|
59
|
-
test("is exported as a single shared instance", () => {
|
|
60
|
-
// It holds no per-router state, so one instance is enough — but callers
|
|
61
|
-
// import the value rather than the class, so it must stay a singleton.
|
|
62
|
-
expect(typeof apiExpressRouterFactory.createRouter).toBe("function");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("builds a router from a provider", () => {
|
|
66
|
-
const provider: ApiRoutesProvider = {
|
|
67
|
-
getRoutes: (): ApiRoutes => ({}),
|
|
68
|
-
};
|
|
69
|
-
const router = apiExpressRouterFactory.createRouter(provider, {
|
|
70
|
-
handleRequest: vi.fn<
|
|
71
|
-
(
|
|
72
|
-
endpoint: never,
|
|
73
|
-
request: Request,
|
|
74
|
-
response: Response,
|
|
75
|
-
next: NextFunction,
|
|
76
|
-
) => Promise<void>
|
|
77
|
-
>(async () => undefined),
|
|
78
|
-
});
|
|
79
|
-
expect(typeof router).toBe("function");
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe("the re-exported middleware", () => {
|
|
84
|
-
test("exposes the error handler", () => {
|
|
85
|
-
expect(typeof handleErrors).toBe("function");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("exposes the auth middleware", () => {
|
|
89
|
-
expect(typeof authMiddleware).toBe("function");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("exposes the request logger middleware", () => {
|
|
93
|
-
expect(typeof requestLoggerMiddleware).toBe("function");
|
|
94
|
-
});
|
|
95
|
-
});
|