@sentzunhat/zacatl 0.0.6 โ 0.0.8
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/README.md +113 -27
- package/package.json +33 -2
- package/src/error/custom.ts +3 -1
- package/src/micro-service/architecture/application/entry-points/rest/common/handler.ts +1 -2
- package/src/micro-service/architecture/application/entry-points/rest/common/request.ts +2 -3
- package/src/micro-service/architecture/application/entry-points/rest/route-handlers/abstract.ts +16 -2
- package/src/micro-service/architecture/application/entry-points/rest/route-handlers/get-route-handler.ts +3 -5
- package/src/micro-service/architecture/application/entry-points/rest/route-handlers/post-route-handler.ts +4 -6
- package/src/micro-service/architecture/application/entry-points/rest/route-handlers/route-handler.ts +1 -2
- package/src/micro-service/architecture/infrastructure/repositories/abstract.ts +28 -14
- package/src/micro-service/architecture/platform/service/service.ts +4 -0
- package/test/unit/micro-service/architecture/application/application.test.ts +19 -2
- package/test/unit/micro-service/architecture/application/entry-points/rest/route-handlers/abstract.test.ts +30 -10
- package/test/unit/micro-service/architecture/application/entry-points/rest/route-handlers/post-route-handler.test.ts +14 -9
- package/test/unit/micro-service/architecture/platform/service/service.test.ts +3 -3
package/README.md
CHANGED
|
@@ -1,42 +1,128 @@
|
|
|
1
1
|
# Zacatl Microservice Framework
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@sentzunhat/zacatl)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
A modular, high-performance TypeScript microservice framework for Node.js, featuring layered architecture, dependency injection, and robust validation for building scalable APIs and distributed systems.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Table of Contents
|
|
8
10
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- [Overview](#overview)
|
|
12
|
+
- [Key Features](#key-features)
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Quick Start](#quick-start)
|
|
15
|
+
- [Project Structure](#project-structure)
|
|
16
|
+
- [Core Concepts](#core-concepts)
|
|
17
|
+
- [Configuration](#configuration)
|
|
18
|
+
- [Testing](#testing)
|
|
19
|
+
- [Contributing](#contributing)
|
|
20
|
+
- [Documentation](#documentation)
|
|
21
|
+
- [License](#license)
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
## Overview
|
|
24
|
+
|
|
25
|
+
Zacatl is a TypeScript-first microservice framework that enforces clean, layered (hexagonal) architecture with strict separation of concerns. It's designed for rapid development of robust APIs and distributed systems, with built-in support for dependency injection, validation, and seamless collaboration between human and AI contributors.
|
|
26
|
+
|
|
27
|
+
## Key Features
|
|
28
|
+
|
|
29
|
+
- **๐๏ธ Layered Architecture**: Clean separation with Application, Domain, Infrastructure, and Platform layers
|
|
30
|
+
- **๐ Dependency Injection**: Built-in DI container using `tsyringe` for better modularity
|
|
31
|
+
- **โก High Performance**: Built on Fastify for maximum speed and efficiency
|
|
32
|
+
- **๐ Type Safety**: Full TypeScript support with strict typing and generics
|
|
33
|
+
- **โ
Validation**: Request/response validation using Zod schemas
|
|
34
|
+
- **๐งช Testing**: Comprehensive testing with Vitest and clear test structure
|
|
35
|
+
- **๐ MongoDB Support**: Built-in repository pattern with Mongoose integration
|
|
36
|
+
- **๐ Internationalization**: Multi-language support with i18n
|
|
37
|
+
- **๐ Production Ready**: Error handling, logging, and monitoring built-in
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Install Zacatl using npm:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @sentzunhat/zacatl
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or with yarn:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
yarn add @sentzunhat/zacatl
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
Here's a minimal example to get you started:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import Fastify from "fastify";
|
|
59
|
+
import { MicroService } from "@sentzunhat/zacatl";
|
|
60
|
+
|
|
61
|
+
const fastifyApp = Fastify();
|
|
62
|
+
|
|
63
|
+
const microservice = new MicroService({
|
|
64
|
+
architecture: {
|
|
65
|
+
application: {
|
|
66
|
+
entryPoints: {
|
|
67
|
+
rest: {
|
|
68
|
+
hookHandlers: [], // Add hook handler classes
|
|
69
|
+
routeHandlers: [], // Add route handler classes
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
domain: { providers: [] }, // Add domain provider classes
|
|
74
|
+
infrastructure: { repositories: [] }, // Add repository classes
|
|
75
|
+
service: {
|
|
76
|
+
name: "my-service",
|
|
77
|
+
server: {
|
|
78
|
+
type: "SERVER",
|
|
79
|
+
vendor: "FASTIFY",
|
|
80
|
+
instance: fastifyApp,
|
|
81
|
+
},
|
|
82
|
+
databases: [
|
|
83
|
+
// Example for MongoDB:
|
|
84
|
+
// {
|
|
85
|
+
// vendor: "MONGOOSE",
|
|
86
|
+
// instance: new Mongoose(),
|
|
87
|
+
// connectionString: "mongodb://localhost/mydb",
|
|
88
|
+
// }
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
16
93
|
|
|
17
|
-
|
|
94
|
+
await microservice.start({ port: 9000 });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For a complete example with route handlers, see our [examples](./examples) directory.
|
|
98
|
+
|
|
99
|
+
## Project Structure
|
|
18
100
|
|
|
19
101
|
```
|
|
20
102
|
src/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
103
|
+
โโโ configuration.ts # App configuration and environment settings
|
|
104
|
+
โโโ logs.ts # Structured logging with Pino
|
|
105
|
+
โโโ optionals.ts # Optional utility types and helpers
|
|
106
|
+
โโโ error/ # Custom error classes (BadRequest, NotFound, etc.)
|
|
107
|
+
โโโ locales/ # Internationalization files (en.json, fr.json)
|
|
108
|
+
โโโ micro-service/
|
|
109
|
+
โ โโโ architecture/
|
|
110
|
+
โ โโโ application/ # HTTP entry points, validation, route handlers
|
|
111
|
+
โ โโโ domain/ # Business logic, models, services (pure)
|
|
112
|
+
โ โโโ infrastructure/ # Database repositories, external adapters
|
|
113
|
+
โ โโโ platform/ # Service orchestration, DI setup, startup
|
|
114
|
+
โโโ utils/ # Utility functions (base64, hmac, etc.)
|
|
115
|
+
|
|
116
|
+
test/
|
|
117
|
+
โโโ unit/ # Unit tests mirroring src/ structure
|
|
34
118
|
```
|
|
35
119
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- **
|
|
39
|
-
- **
|
|
120
|
+
### Layer Responsibilities
|
|
121
|
+
|
|
122
|
+
- **Application Layer**: Handles HTTP requests, validation, and delegates to domain logic
|
|
123
|
+
- **Domain Layer**: Contains pure business logic, models, and services (no infrastructure dependencies)
|
|
124
|
+
- **Infrastructure Layer**: Manages persistence (MongoDB repositories) and external integrations
|
|
125
|
+
- **Platform Layer**: Bootstraps the application, configures DI container, and starts the server
|
|
40
126
|
|
|
41
127
|
---
|
|
42
128
|
|
package/package.json
CHANGED
|
@@ -2,12 +2,43 @@
|
|
|
2
2
|
"name": "@sentzunhat/zacatl",
|
|
3
3
|
"main": "src/index.ts",
|
|
4
4
|
"module": "src/index.ts",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.8",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/sentzunhat/zacatl.git"
|
|
9
9
|
},
|
|
10
|
-
"description": "",
|
|
10
|
+
"description": "A modular, high-performance TypeScript microservice framework for Node.js, featuring layered architecture, dependency injection, and robust validation for building scalable APIs and distributed systems.",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"typescript",
|
|
13
|
+
"microservice",
|
|
14
|
+
"microservices",
|
|
15
|
+
"framework",
|
|
16
|
+
"nodejs",
|
|
17
|
+
"dependency-injection",
|
|
18
|
+
"hexagonal-architecture",
|
|
19
|
+
"layered-architecture",
|
|
20
|
+
"clean-architecture",
|
|
21
|
+
"fastify",
|
|
22
|
+
"mongodb",
|
|
23
|
+
"mongoose",
|
|
24
|
+
"zod",
|
|
25
|
+
"validation",
|
|
26
|
+
"vitest",
|
|
27
|
+
"testing",
|
|
28
|
+
"api",
|
|
29
|
+
"rest-api",
|
|
30
|
+
"backend",
|
|
31
|
+
"server",
|
|
32
|
+
"enterprise",
|
|
33
|
+
"scalable",
|
|
34
|
+
"modular",
|
|
35
|
+
"tsyringe",
|
|
36
|
+
"pino",
|
|
37
|
+
"logging",
|
|
38
|
+
"i18n",
|
|
39
|
+
"internationalization",
|
|
40
|
+
"artisan"
|
|
41
|
+
],
|
|
11
42
|
"author": "Diego Beltran <beltrd@gmail.com> (https://sentzunhat.com)",
|
|
12
43
|
"contributors": [
|
|
13
44
|
{
|
package/src/error/custom.ts
CHANGED
|
@@ -14,7 +14,9 @@ export type HttpStatusCode =
|
|
|
14
14
|
| 502 // Bad Gateway
|
|
15
15
|
| 503; // Service Unavailable;
|
|
16
16
|
|
|
17
|
-
export type
|
|
17
|
+
export type StatusCodeString = "invalid";
|
|
18
|
+
|
|
19
|
+
export type ErrorCode = Optional<HttpStatusCode | StatusCodeString>;
|
|
18
20
|
|
|
19
21
|
export interface CustomErrorsArgs {
|
|
20
22
|
message: string;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { FastifyReply } from "fastify";
|
|
3
2
|
|
|
4
3
|
import { Request } from "./request";
|
|
5
4
|
|
|
6
5
|
export type Handler<
|
|
7
6
|
TBody,
|
|
8
|
-
TQuerystring =
|
|
7
|
+
TQuerystring = Record<string, string>,
|
|
9
8
|
TParams = void
|
|
10
9
|
> = (
|
|
11
10
|
request: Request<TBody, TQuerystring, TParams>,
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { IncomingMessage } from "http";
|
|
3
2
|
import { FastifySchema, FastifyRequest, RawServerBase } from "fastify";
|
|
4
3
|
|
|
5
4
|
export type Request<
|
|
6
5
|
TBody,
|
|
7
|
-
TQuerystring =
|
|
6
|
+
TQuerystring = Record<string, string>,
|
|
8
7
|
TParams = void
|
|
9
8
|
> = FastifyRequest<
|
|
10
9
|
{
|
|
11
10
|
Body: TBody;
|
|
12
11
|
Querystring: TQuerystring;
|
|
13
12
|
Params: TParams;
|
|
14
|
-
Headers:
|
|
13
|
+
Headers: Record<string, string>;
|
|
15
14
|
},
|
|
16
15
|
RawServerBase,
|
|
17
16
|
IncomingMessage,
|
package/src/micro-service/architecture/application/entry-points/rest/route-handlers/abstract.ts
CHANGED
|
@@ -5,6 +5,20 @@ import { HTTPMethods, FastifySchema, FastifyReply } from "fastify";
|
|
|
5
5
|
import { RouteHandler } from "./route-handler";
|
|
6
6
|
import { Request } from "../common/request";
|
|
7
7
|
|
|
8
|
+
export type RouteSchema<
|
|
9
|
+
TBody = void,
|
|
10
|
+
TQuerystring = Record<string, string>,
|
|
11
|
+
TParams = Record<string, string>,
|
|
12
|
+
THeaders = Record<string, string>,
|
|
13
|
+
TResponse = void
|
|
14
|
+
> = {
|
|
15
|
+
body?: z.ZodSchema<TBody>;
|
|
16
|
+
querystring?: z.ZodSchema<TQuerystring>;
|
|
17
|
+
params?: z.ZodSchema<TParams>;
|
|
18
|
+
headers?: z.ZodSchema<THeaders>;
|
|
19
|
+
response?: z.ZodSchema<TResponse>;
|
|
20
|
+
};
|
|
21
|
+
|
|
8
22
|
export type AbstractRouteHandlerConstructor = {
|
|
9
23
|
url: string;
|
|
10
24
|
method: HTTPMethods;
|
|
@@ -15,9 +29,9 @@ export type HandlerOutput<TResponse> = TResponse;
|
|
|
15
29
|
|
|
16
30
|
export abstract class AbstractRouteHandler<
|
|
17
31
|
TBody = void,
|
|
18
|
-
TQuerystring =
|
|
32
|
+
TQuerystring = Record<string, string>,
|
|
19
33
|
TResponse = void,
|
|
20
|
-
TParams =
|
|
34
|
+
TParams = Record<string, string>
|
|
21
35
|
> implements RouteHandler<TBody, TQuerystring, TParams>
|
|
22
36
|
{
|
|
23
37
|
public url: string;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { FastifySchema } from "fastify";
|
|
3
|
-
|
|
4
2
|
import { AbstractRouteHandler } from "./abstract";
|
|
5
3
|
|
|
6
4
|
export type GetRouteHandlerConstructor = {
|
|
@@ -9,9 +7,9 @@ export type GetRouteHandlerConstructor = {
|
|
|
9
7
|
};
|
|
10
8
|
|
|
11
9
|
export abstract class GetRouteHandler<
|
|
12
|
-
TBody =
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
TBody = void,
|
|
11
|
+
TQuerystring = Record<string, string>,
|
|
12
|
+
TResponse = void,
|
|
15
13
|
TParams = void
|
|
16
14
|
> extends AbstractRouteHandler<TBody, TQuerystring, TResponse, TParams> {
|
|
17
15
|
constructor(args: GetRouteHandlerConstructor) {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { FastifySchema } from "fastify";
|
|
3
|
-
|
|
4
2
|
import { AbstractRouteHandler } from "./abstract";
|
|
5
3
|
|
|
6
4
|
export type PostRouteHandlerConstructor = {
|
|
@@ -9,10 +7,10 @@ export type PostRouteHandlerConstructor = {
|
|
|
9
7
|
};
|
|
10
8
|
|
|
11
9
|
export abstract class PostRouteHandler<
|
|
12
|
-
TBody =
|
|
13
|
-
TResponse =
|
|
14
|
-
TQuerystring =
|
|
15
|
-
TParams =
|
|
10
|
+
TBody = unknown,
|
|
11
|
+
TResponse = unknown,
|
|
12
|
+
TQuerystring = Record<string, string>,
|
|
13
|
+
TParams = Record<string, string>
|
|
16
14
|
> extends AbstractRouteHandler<TBody, TQuerystring, TResponse, TParams> {
|
|
17
15
|
constructor(args: PostRouteHandlerConstructor) {
|
|
18
16
|
super({
|
package/src/micro-service/architecture/application/entry-points/rest/route-handlers/route-handler.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { FastifySchema, HTTPMethods } from "fastify";
|
|
3
2
|
|
|
4
3
|
import { Handler } from "../common/handler";
|
|
5
4
|
|
|
6
5
|
export type RouteHandler<
|
|
7
6
|
TBody = void,
|
|
8
|
-
TQuerystring =
|
|
7
|
+
TQuerystring = Record<string, string>,
|
|
9
8
|
TParams = void
|
|
10
9
|
> = {
|
|
11
10
|
url: string;
|
|
@@ -7,6 +7,7 @@ import importedMongoose, {
|
|
|
7
7
|
Schema,
|
|
8
8
|
Default__v,
|
|
9
9
|
Require_id,
|
|
10
|
+
ObjectId,
|
|
10
11
|
} from "mongoose";
|
|
11
12
|
import { v4 as uuidv4 } from "uuid";
|
|
12
13
|
import { container } from "tsyringe";
|
|
@@ -18,6 +19,14 @@ export type BaseRepositoryConfig<D> = {
|
|
|
18
19
|
|
|
19
20
|
export type WithMongooseMeta<T> = Default__v<Require_id<T>>;
|
|
20
21
|
|
|
22
|
+
export type MongooseDocument<Db> = Document<
|
|
23
|
+
ObjectId,
|
|
24
|
+
{},
|
|
25
|
+
Db,
|
|
26
|
+
Record<string, string>
|
|
27
|
+
> &
|
|
28
|
+
WithMongooseMeta<Db>;
|
|
29
|
+
|
|
21
30
|
export type LeanWithMeta<T> = WithMongooseMeta<T> & {
|
|
22
31
|
id: string;
|
|
23
32
|
createdAt: Date;
|
|
@@ -31,9 +40,9 @@ export type LeanDocument<T> = T & {
|
|
|
31
40
|
};
|
|
32
41
|
|
|
33
42
|
export type MongooseDoc<Db> = IfAny<
|
|
34
|
-
Db
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
MongooseDocument<Db>,
|
|
44
|
+
MongooseDocument<Db>,
|
|
45
|
+
MongooseDocument<Db>
|
|
37
46
|
>;
|
|
38
47
|
|
|
39
48
|
export type ToLeanInput<D, T> =
|
|
@@ -47,6 +56,7 @@ export type ToLeanInput<D, T> =
|
|
|
47
56
|
export type Repository<D, T> = {
|
|
48
57
|
model: Model<D>;
|
|
49
58
|
|
|
59
|
+
toLean(input: ToLeanInput<D, T>): LeanDocument<T> | null;
|
|
50
60
|
findById(id: string): Promise<LeanDocument<T> | null>;
|
|
51
61
|
create(entity: D): Promise<LeanDocument<T>>;
|
|
52
62
|
update(id: string, update: Partial<D>): Promise<LeanDocument<T> | null>;
|
|
@@ -62,12 +72,15 @@ export abstract class BaseRepository<D, T> implements Repository<D, T> {
|
|
|
62
72
|
const mongoose = connection.db?.databaseName
|
|
63
73
|
? importedMongoose
|
|
64
74
|
: container.resolve<Mongoose>(Mongoose);
|
|
75
|
+
|
|
65
76
|
const { name, schema } = this.config;
|
|
77
|
+
|
|
66
78
|
if (name) {
|
|
67
79
|
this.model = mongoose.model<D>(name, schema);
|
|
68
80
|
} else {
|
|
69
81
|
this.model = mongoose.model<D>(uuidv4(), schema);
|
|
70
82
|
}
|
|
83
|
+
|
|
71
84
|
this.model.createCollection();
|
|
72
85
|
this.model.createIndexes();
|
|
73
86
|
this.model.init();
|
|
@@ -77,20 +90,17 @@ export abstract class BaseRepository<D, T> implements Repository<D, T> {
|
|
|
77
90
|
* Ensures the returned document has the correct id, createdAt, and updatedAt fields.
|
|
78
91
|
* Accepts a Mongoose document, a plain object, or a result from .lean().
|
|
79
92
|
*/
|
|
80
|
-
|
|
93
|
+
public toLean(input: ToLeanInput<D, T>): LeanDocument<T> | null {
|
|
81
94
|
if (!input) {
|
|
82
95
|
return null;
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
let base: LeanWithMeta<T>;
|
|
86
99
|
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
base = (
|
|
92
|
-
input as Document<unknown, {}, D, {}> & WithMongooseMeta<D>
|
|
93
|
-
).toObject<LeanDocument<T>>({ virtuals: true });
|
|
100
|
+
if (typeof (input as MongooseDoc<D>).toObject === "function") {
|
|
101
|
+
base = (input as MongooseDoc<D>).toObject<LeanDocument<T>>({
|
|
102
|
+
virtuals: true,
|
|
103
|
+
});
|
|
94
104
|
} else {
|
|
95
105
|
base = input as LeanWithMeta<T>;
|
|
96
106
|
}
|
|
@@ -132,11 +142,15 @@ export abstract class BaseRepository<D, T> implements Repository<D, T> {
|
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
async create(entity: D): Promise<LeanDocument<T>> {
|
|
135
|
-
const
|
|
145
|
+
const document = await this.model.create<D>(entity);
|
|
136
146
|
|
|
137
|
-
|
|
147
|
+
const leanDocument = this.toLean(document as MongooseDoc<D>);
|
|
148
|
+
|
|
149
|
+
if (!leanDocument) {
|
|
150
|
+
throw new Error("failed to create document");
|
|
151
|
+
}
|
|
138
152
|
|
|
139
|
-
return
|
|
153
|
+
return leanDocument;
|
|
140
154
|
}
|
|
141
155
|
|
|
142
156
|
async update(
|
|
@@ -206,6 +206,10 @@ export class Service implements ServicePort {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
public async configureDatabases(): Promise<void> {
|
|
209
|
+
if (!this.databases || this.databases.length === 0) {
|
|
210
|
+
return; // No databases to configure
|
|
211
|
+
}
|
|
212
|
+
|
|
209
213
|
for (const database of this.databases) {
|
|
210
214
|
const strategy = strategiesForDatabaseVendor[database.vendor];
|
|
211
215
|
|
|
@@ -16,7 +16,14 @@ class DummyHookHandler implements HookHandler {
|
|
|
16
16
|
async execute(_: FastifyRequest): Promise<void> {}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
import { FastifyReply } from "fastify";
|
|
20
|
+
|
|
21
|
+
class DummyRouteHandler extends AbstractRouteHandler<
|
|
22
|
+
void, // Body
|
|
23
|
+
Record<string, string>, // Querystring
|
|
24
|
+
void, // Params
|
|
25
|
+
void // Response
|
|
26
|
+
> {
|
|
20
27
|
constructor() {
|
|
21
28
|
super({
|
|
22
29
|
url: "/",
|
|
@@ -25,7 +32,17 @@ class DummyRouteHandler extends AbstractRouteHandler {
|
|
|
25
32
|
});
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
handler(
|
|
35
|
+
handler(
|
|
36
|
+
_: Request<
|
|
37
|
+
void, // Body
|
|
38
|
+
Record<string, string>, // Querystring
|
|
39
|
+
void // Params
|
|
40
|
+
>,
|
|
41
|
+
__: FastifyReply
|
|
42
|
+
): void | Promise<void> {
|
|
43
|
+
// Dummy implementation
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
const fakeConfig: ConfigApplication = {
|
|
@@ -10,9 +10,29 @@ import {
|
|
|
10
10
|
Request,
|
|
11
11
|
} from "../../../../../../../../src/micro-service/architecture/application";
|
|
12
12
|
|
|
13
|
-
class TestRouteHandler extends AbstractRouteHandler<
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
class TestRouteHandler extends AbstractRouteHandler<
|
|
14
|
+
void, // Body
|
|
15
|
+
Record<string, string>, // Querystring
|
|
16
|
+
void, // Params
|
|
17
|
+
void // Response
|
|
18
|
+
> {
|
|
19
|
+
constructor() {
|
|
20
|
+
super({
|
|
21
|
+
url: "/",
|
|
22
|
+
schema: {},
|
|
23
|
+
method: "GET",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
handler(
|
|
28
|
+
_: Request<
|
|
29
|
+
void, // Body
|
|
30
|
+
Record<string, string>, // Querystring
|
|
31
|
+
void // Params
|
|
32
|
+
>
|
|
33
|
+
): void | Promise<void> {
|
|
34
|
+
// Dummy implementation
|
|
35
|
+
return;
|
|
16
36
|
}
|
|
17
37
|
}
|
|
18
38
|
|
|
@@ -20,15 +40,15 @@ describe("AbstractRouteHandler", () => {
|
|
|
20
40
|
it("executes the handler and sends the proper response", async () => {
|
|
21
41
|
vi.spyOn(i18n, "__").mockReturnValue("Default success");
|
|
22
42
|
|
|
23
|
-
const fakeRequest = createFakeFastifyRequest() as Request<
|
|
43
|
+
const fakeRequest = createFakeFastifyRequest() as Request<
|
|
44
|
+
void, // Body
|
|
45
|
+
Record<string, string>, // Querystring
|
|
46
|
+
void // Params
|
|
47
|
+
>;
|
|
24
48
|
|
|
25
49
|
const fakeReply: any = createFakeFastifyReply();
|
|
26
50
|
|
|
27
|
-
const testHandler = new TestRouteHandler(
|
|
28
|
-
url: "/test",
|
|
29
|
-
method: "GET",
|
|
30
|
-
schema: {},
|
|
31
|
-
});
|
|
51
|
+
const testHandler = new TestRouteHandler();
|
|
32
52
|
|
|
33
53
|
await testHandler.execute(fakeRequest, fakeReply);
|
|
34
54
|
|
|
@@ -36,7 +56,7 @@ describe("AbstractRouteHandler", () => {
|
|
|
36
56
|
expect(fakeReply.send).toHaveBeenCalledWith({
|
|
37
57
|
ok: true,
|
|
38
58
|
message: "Default success",
|
|
39
|
-
data:
|
|
59
|
+
data: undefined,
|
|
40
60
|
});
|
|
41
61
|
});
|
|
42
62
|
});
|
|
@@ -10,9 +10,17 @@ import {
|
|
|
10
10
|
createFakeFastifyRequest,
|
|
11
11
|
} from "../../../../../../helpers/common/common";
|
|
12
12
|
|
|
13
|
-
class TestPostRouteHandler extends PostRouteHandler<
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
class TestPostRouteHandler extends PostRouteHandler<{}, {}, {}, {}> {
|
|
14
|
+
constructor() {
|
|
15
|
+
super({
|
|
16
|
+
url: "/post-test",
|
|
17
|
+
schema: {}, // Use an empty schema for test purposes.
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
handler(_: Request<{}, {}, {}>): {} | Promise<{}> {
|
|
22
|
+
// Dummy implementation
|
|
23
|
+
return {};
|
|
16
24
|
}
|
|
17
25
|
}
|
|
18
26
|
|
|
@@ -20,12 +28,9 @@ describe("PostRouteHandler", () => {
|
|
|
20
28
|
it("executes POST handler and sends proper response", async () => {
|
|
21
29
|
vi.spyOn(i18n, "__").mockReturnValue("Default success POST");
|
|
22
30
|
|
|
23
|
-
const testHandler = new TestPostRouteHandler(
|
|
24
|
-
url: "/post-test",
|
|
25
|
-
schema: {}, // Use an empty schema for test purposes.
|
|
26
|
-
});
|
|
31
|
+
const testHandler = new TestPostRouteHandler();
|
|
27
32
|
|
|
28
|
-
const fakeRequest = createFakeFastifyRequest() as Request<
|
|
33
|
+
const fakeRequest = createFakeFastifyRequest() as Request<{}, {}, {}>;
|
|
29
34
|
const fakeReply = createFakeFastifyReply();
|
|
30
35
|
|
|
31
36
|
await testHandler.execute(fakeRequest, fakeReply);
|
|
@@ -34,7 +39,7 @@ describe("PostRouteHandler", () => {
|
|
|
34
39
|
expect(fakeReply.send).toHaveBeenCalledWith({
|
|
35
40
|
ok: true,
|
|
36
41
|
message: "Default success POST",
|
|
37
|
-
data:
|
|
42
|
+
data: {},
|
|
38
43
|
});
|
|
39
44
|
});
|
|
40
45
|
});
|
|
@@ -146,6 +146,8 @@ describe("Service", () => {
|
|
|
146
146
|
const port = 3000;
|
|
147
147
|
const service = new Service(config);
|
|
148
148
|
|
|
149
|
+
await service.configureDatabases();
|
|
150
|
+
|
|
149
151
|
await service.start({ port });
|
|
150
152
|
|
|
151
153
|
expect(
|
|
@@ -184,15 +186,13 @@ describe("Service", () => {
|
|
|
184
186
|
});
|
|
185
187
|
|
|
186
188
|
it("should throw an error if database configuration fails", async () => {
|
|
187
|
-
const port = 3000;
|
|
188
|
-
|
|
189
189
|
strategiesForDatabaseVendor[DatabaseVendor.MONGOOSE] = originalDbStrategy;
|
|
190
190
|
|
|
191
191
|
fakeMongoose.connect.mockRejectedValue(new Error("db connect failed"));
|
|
192
192
|
|
|
193
193
|
const service = new Service(config);
|
|
194
194
|
|
|
195
|
-
await expect(service.
|
|
195
|
+
await expect(service.configureDatabases()).rejects.toThrow(
|
|
196
196
|
"failed to configure database for service"
|
|
197
197
|
);
|
|
198
198
|
});
|