@forklaunch/core 0.1.2 → 0.1.3

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.
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config = {
4
+ verbose: true,
5
+ preset: 'ts-jest',
6
+ testEnvironment: 'node',
7
+ testPathIgnorePatterns: ['dist/', 'node_modules/']
8
+ };
9
+ exports.default = config;
10
+ //# sourceMappingURL=jest.config.js.map
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "name": "@forklaunch/core",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "forklaunch-js core package. Contains useful building blocks.",
5
- "main": "index.js",
5
+ "files": [
6
+ "dist"
7
+ ],
6
8
  "scripts": {
7
9
  "test": "jest",
8
10
  "build": "tsc",
package/.prettierignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules/
2
- dist/
package/.prettierrc DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "semi": true,
3
- "trailingComma": "none",
4
- "singleQuote": true,
5
- "printWidth": 80
6
- }
@@ -1,2 +0,0 @@
1
- export * from './models/requestEntityMapper.model';
2
- export * from './models/responseEntityMapper.model';
@@ -1,17 +0,0 @@
1
- import { AnySchemaValidator } from '@forklaunch/validator';
2
-
3
- /**
4
- * Interface representing a constructor for an entity mapper.
5
- *
6
- * @template T - The type of the entity mapper.
7
- * @interface EntityMapperConstructor
8
- */
9
- export interface EntityMapperConstructor<T, SV extends AnySchemaValidator> {
10
- /**
11
- * Creates a new instance of the entity mapper.
12
- *
13
- * @param {AnySchemaValidator} schemaValidator - The arguments to pass to the constructor.
14
- * @returns {T} - A new instance of the entity mapper.
15
- */
16
- new (schemaValidator: SV): T;
17
- }
@@ -1,110 +0,0 @@
1
- import {
2
- AnySchemaValidator,
3
- Schema,
4
- SchemaValidator
5
- } from '@forklaunch/validator';
6
- import { EntityMapperConstructor } from '../interfaces/entityMapper.interface';
7
- import { EntityMapperSchemaValidatorObject } from '../types/entityMapper.types';
8
-
9
- /**
10
- * Constructs an instance of a T.
11
- *
12
- * @template T - A type that extends BaseEntityMapper.
13
- * @param {EntityMapperConstructor<T>} self - The constructor of the T.
14
- * @param {...any[]} args - The arguments to pass to the constructor.
15
- * @returns {T} - An instance of the T.
16
- */
17
- export function construct<T, SV extends AnySchemaValidator>(
18
- self: EntityMapperConstructor<T, SV>,
19
- schemaValidator?: SV
20
- ): T {
21
- return new self(schemaValidator || ({} as SV));
22
- }
23
-
24
- /**
25
- * Abstract class representing a base entityMapper.
26
- *
27
- * @template SV - A type that extends SchemaValidator.
28
- */
29
- export abstract class BaseEntityMapper<SV extends AnySchemaValidator> {
30
- /**
31
- * The schema validator exact type.
32
- * @type {SV}
33
- * @protected
34
- */
35
- _SV!: SV;
36
-
37
- /**
38
- * The schema validator as a general type.
39
- * @type {SchemaValidator}
40
- * @protected
41
- */
42
- protected schemaValidator: SchemaValidator;
43
-
44
- /**
45
- * The schema definition.
46
- * @type {EntityMapperSchemaValidatorObject<SV>}
47
- * @abstract
48
- */
49
- abstract schema: EntityMapperSchemaValidatorObject<SV>;
50
-
51
- /**
52
- * The Data Transfer Object (DTO).
53
- * @type {Schema<this['schema'], SV>}
54
- *
55
- */
56
- _dto: Schema<this['schema'], SV> = {} as unknown as Schema<
57
- this['schema'],
58
- SV
59
- >;
60
-
61
- /**
62
- * Creates an instance of BaseEntityMapper.
63
- *
64
- * @param {SV} schemaValidator - The schema provider.
65
- */
66
- constructor(schemaValidator: SV) {
67
- this.schemaValidator = schemaValidator as unknown as SchemaValidator;
68
- }
69
-
70
- /**
71
- * Validates and sets the Data Transfer Object (DTO).
72
- *
73
- * @param {this['_dto']} dto - The Data Transfer Object (DTO).
74
- * @throws {Error} - Throws an error if the DTO is invalid.
75
- */
76
- set dto(_dto: this['_dto']) {
77
- if (
78
- !this.schemaValidator.validate(
79
- this.schemaValidator.schemify(this.schema),
80
- _dto
81
- )
82
- ) {
83
- throw new Error('Invalid DTO');
84
- }
85
- this._dto = _dto as unknown as Schema<this['schema'], SV>;
86
- }
87
-
88
- /**
89
- * Validates and gets the Data Transfer Object (DTO).
90
- *
91
- * @returns {this['_dto']} - The Data Transfer Object (DTO).
92
- * @throws {Error} - Throws an error if the DTO is invalid.
93
- */
94
- get dto(): this['_dto'] {
95
- return this._dto as unknown as this['_dto'];
96
- }
97
-
98
- /**
99
- * Gets the schema of a T.
100
- *
101
- * @template T - A type that extends BaseEntityMapper.
102
- * @param {EntityMapperConstructor<T>} this - The constructor of the T.
103
- * @returns {T['schema']} - The schema of the T.
104
- */
105
- static schema<T extends BaseEntityMapper<SV>, SV extends AnySchemaValidator>(
106
- this: EntityMapperConstructor<T, SV>
107
- ): T['schema'] {
108
- return construct(this).schema;
109
- }
110
- }
@@ -1,111 +0,0 @@
1
- import { AnySchemaValidator } from '@forklaunch/validator';
2
- import { BaseEntity } from '../../database/mikro/models/entities/base.entity';
3
- import { EntityMapperConstructor } from '../interfaces/entityMapper.interface';
4
- import { BaseEntityMapper, construct } from './baseEntityMapper.model';
5
-
6
- /**
7
- * Abstract class representing a request entityMapper.
8
- *
9
- * @template Entity - A type that extends BaseEntity.
10
- * @template SV - A type that extends SchemaValidator.
11
- * @extends {BaseEntityMapper<SV>}
12
- */
13
- export abstract class RequestEntityMapper<
14
- Entity extends BaseEntity,
15
- SV extends AnySchemaValidator
16
- > extends BaseEntityMapper<SV> {
17
- /**
18
- * The entity.
19
- * @type {Entity}
20
- * @protected
21
- */
22
- _Entity!: Entity;
23
-
24
- /**
25
- * Converts the underlying DTO to an entity.
26
- *
27
- * @abstract
28
- * @param {...unknown[]} additionalArgs - Additional arguments.
29
- * @returns {Entity} - The entity.
30
- */
31
- abstract toEntity(...additionalArgs: unknown[]): Entity;
32
-
33
- /**
34
- * Populates the DTO with data from a JSON object.
35
- *
36
- * @param {this['_dto']} json - The JSON object.
37
- * @returns {this} - The instance of the RequestEntityMapper.
38
- */
39
- fromJson(json: this['_dto']): this {
40
- if (
41
- !this.schemaValidator.validate(
42
- this.schemaValidator.schemify(this.schema),
43
- json
44
- )
45
- ) {
46
- throw new Error('Invalid DTO');
47
- }
48
- this.dto = json;
49
- return this;
50
- }
51
-
52
- /**
53
- * Deserializes a JSON object to an entity.
54
- *
55
- * @param {this['_dto']} json - The JSON object.
56
- * @param {...unknown[]} additionalArgs - Additional arguments.
57
- * @returns {Entity} - The entity.
58
- */
59
- deserializeJsonToEntity(
60
- json: this['_dto'],
61
- ...additionalArgs: unknown[]
62
- ): Entity {
63
- return this.fromJson(json).toEntity(...additionalArgs);
64
- }
65
-
66
- /**
67
- * Creates an instance of a RequestEntityMapper from a JSON object.
68
- *
69
- * @template T - A type that extends RequestEntityMapper.
70
- * @param {EntityMapperConstructor<T>} this - The constructor of the T.
71
- * @param {T['_SV']} schemaValidator - The schema provider.
72
- * @param {T['_dto']} json - The JSON object.
73
- * @returns {T} - An instance of the T.
74
- */
75
- static fromJson<
76
- T extends RequestEntityMapper<BaseEntity, SV>,
77
- SV extends AnySchemaValidator,
78
- JsonType extends T['_dto']
79
- >(
80
- this: EntityMapperConstructor<T, SV>,
81
- schemaValidator: SV,
82
- json: JsonType
83
- ): T {
84
- return construct(this, schemaValidator).fromJson(json);
85
- }
86
-
87
- /**
88
- * Deserializes a JSON object to an entity.
89
- *
90
- * @template T - A type that extends RequestEntityMapper.
91
- * @param {EntityMapperConstructor<T>} this - The constructor of the T.
92
- * @param {T['_SV']} schemaValidator - The schema provider.
93
- * @param {T['_dto']} json - The JSON object.
94
- * @param {...unknown[]} additionalArgs - Additional arguments.
95
- * @returns {T['_Entity']} - The entity.
96
- */
97
- static deserializeJsonToEntity<
98
- T extends RequestEntityMapper<BaseEntity, SV>,
99
- SV extends AnySchemaValidator,
100
- JsonType extends T['_dto']
101
- >(
102
- this: EntityMapperConstructor<T, SV>,
103
- schemaValidator: SV,
104
- json: JsonType,
105
- ...additionalArgs: unknown[]
106
- ): T['_Entity'] {
107
- return construct(this, schemaValidator)
108
- .fromJson(json)
109
- .toEntity(...additionalArgs);
110
- }
111
- }
@@ -1,98 +0,0 @@
1
- import { AnySchemaValidator } from '@forklaunch/validator';
2
- import { BaseEntity } from '../../database/mikro/models/entities/base.entity';
3
- import { EntityMapperConstructor } from '../interfaces/entityMapper.interface';
4
- import { BaseEntityMapper, construct } from './baseEntityMapper.model';
5
-
6
- /**
7
- * Abstract class representing a response entityMapper.
8
- *
9
- * @template Entity - A type that extends BaseEntity.
10
- * @template SV - A type that extends SchemaValidator.
11
- * @extends {BaseEntityMapper<SV>}
12
- */
13
- export abstract class ResponseEntityMapper<
14
- Entity extends BaseEntity,
15
- SV extends AnySchemaValidator
16
- > extends BaseEntityMapper<SV> {
17
- /**
18
- * The entity type.
19
- * @type {Entity}
20
- * @protected
21
- */
22
- _Entity!: Entity;
23
-
24
- /**
25
- * Populates entityMapper with DTO from an entity.
26
- *
27
- * @abstract
28
- * @param {Entity} entity - The entity to convert.
29
- * @returns {this} - The instance of the ResponseEntityMapper.
30
- */
31
- abstract fromEntity(entity: Entity, ...additionalArgs: unknown[]): this;
32
-
33
- /**
34
- * Converts the underlying DTO to a JSON object.
35
- *
36
- * @param {...unknown[]} additionalArgs - Additional arguments.
37
- * @returns {this['_dto']} - The JSON object.
38
- */
39
- toJson(): this['_dto'] {
40
- if (
41
- !this.schemaValidator.validate(
42
- this.schemaValidator.schemify(this.schema),
43
- this.dto
44
- )
45
- ) {
46
- throw new Error('Invalid DTO');
47
- }
48
- return this.dto;
49
- }
50
-
51
- /**
52
- * Serializes an entity to a JSON object.
53
- *
54
- * @param {Entity} entity - The entity to serialize.
55
- * @returns {this['_dto']} - The JSON object.
56
- */
57
- serializeEntityToJson(entity: Entity): this['_dto'] {
58
- return this.fromEntity(entity).toJson();
59
- }
60
-
61
- /**
62
- * Populates entityMapper with DTO from an entity.
63
- *
64
- * @template T - A type that extends ResponseEntityMapper.
65
- * @param {EntityMapperConstructor<T>} this - The constructor of the T.
66
- * @param {T['_Entity']} entity - The entity to convert.
67
- * @returns {T} - An instance of the T.
68
- */
69
- static fromEntity<
70
- T extends ResponseEntityMapper<BaseEntity, SV>,
71
- SV extends AnySchemaValidator
72
- >(
73
- this: EntityMapperConstructor<T, SV>,
74
- schemaValidator: SV,
75
- entity: T['_Entity']
76
- ): T {
77
- return construct(this, schemaValidator).fromEntity(entity);
78
- }
79
-
80
- /**
81
- * Serializes an entity to a JSON object.
82
- *
83
- * @template T - A type that extends ResponseEntityMapper.
84
- * @param {EntityMapperConstructor<T>} this - The constructor of the T.
85
- * @param {T['_Entity']} entity - The entity to serialize.
86
- * @returns {T['_dto']} - The JSON object.
87
- */
88
- static serializeEntityToJson<
89
- T extends ResponseEntityMapper<BaseEntity, SV>,
90
- SV extends AnySchemaValidator
91
- >(
92
- this: EntityMapperConstructor<T, SV>,
93
- schemaValidator: SV,
94
- entity: T['_Entity']
95
- ): T['_dto'] {
96
- return construct(this, schemaValidator).serializeEntityToJson(entity);
97
- }
98
- }
@@ -1,12 +0,0 @@
1
- import { AnySchemaValidator } from '@forklaunch/validator';
2
- import { UnboxedObjectSchema } from '@forklaunch/validator/types';
3
-
4
- /**
5
- * Type representing a schema validator object for an entity mapper.
6
- *
7
- * @template SV - A type that extends SchemaValidator.
8
- * @typedef {ValidSchemaObject<SV> | UnboxedObjectSchema<SchemaCatchall<SV>> & {}} EntityMapperSchemaValidatorObject
9
- */
10
- export type EntityMapperSchemaValidatorObject<SV extends AnySchemaValidator> =
11
- | SV['_ValidSchemaObject']
12
- | UnboxedObjectSchema<SV>;
package/eslint.config.mjs DELETED
@@ -1,12 +0,0 @@
1
- import pluginJs from "@eslint/js";
2
- import globals from "globals";
3
- import tseslint from "typescript-eslint";
4
-
5
-
6
- export default [
7
- {files: ["**/*.{ts}"]},
8
- {ignores: ["tests/**/*", "dist/**/*", "node_modules/**/*"]},
9
- {languageOptions: { globals: globals.browser }},
10
- pluginJs.configs.recommended,
11
- ...tseslint.configs.recommended,
12
- ];
package/http/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './middlewares';
2
- export * from './types';
@@ -1,2 +0,0 @@
1
- export * from './request.middleware';
2
- export * from './response.middleware';
@@ -1,257 +0,0 @@
1
- import { AnySchemaValidator, SchemaValidator } from '@forklaunch/validator';
2
- import * as jose from 'jose';
3
- import { v4 } from 'uuid';
4
- import {
5
- ForklaunchNextFunction,
6
- ForklaunchRequest,
7
- ForklaunchResponse
8
- } from '../types/api.types';
9
- import {
10
- AuthMethod,
11
- HttpContractDetails,
12
- PathParamHttpContractDetails,
13
- StringOnlyObject
14
- } from '../types/primitive.types';
15
-
16
- export function createRequestContext<
17
- SV extends AnySchemaValidator,
18
- Request extends ForklaunchRequest<SV>,
19
- Response extends ForklaunchResponse,
20
- NextFunction extends ForklaunchNextFunction
21
- >(schemaValidator: SV) {
22
- return (req: Request, res: Response, next?: NextFunction) => {
23
- req.schemaValidator = schemaValidator as SchemaValidator;
24
-
25
- let correlationId = v4();
26
-
27
- if (req.headers['x-correlation-id']) {
28
- correlationId = req.headers['x-correlation-id'] as string;
29
- }
30
-
31
- res.setHeader('x-correlation-id', correlationId);
32
-
33
- req.context = {
34
- correlationId: correlationId
35
- };
36
-
37
- if (next) {
38
- next();
39
- }
40
- };
41
- }
42
-
43
- export function enrichRequestDetails<
44
- SV extends AnySchemaValidator,
45
- Request extends ForklaunchRequest<SV>,
46
- Response extends ForklaunchResponse,
47
- NextFunction extends ForklaunchNextFunction
48
- >(contractDetails: PathParamHttpContractDetails<SV> | HttpContractDetails<SV>) {
49
- return (req: Request, _res: Response, next?: NextFunction) => {
50
- req.contractDetails = contractDetails;
51
-
52
- if (next) {
53
- next();
54
- }
55
- };
56
- }
57
-
58
- export function preHandlerParse<SV extends AnySchemaValidator>(
59
- schemaValidator: SchemaValidator,
60
- object: unknown,
61
- schemaInput?: StringOnlyObject<SV>
62
- ) {
63
- if (!schemaInput) {
64
- return;
65
- }
66
-
67
- const schema = schemaValidator.schemify(schemaInput);
68
- if (!schemaValidator.validate(schema, object)) {
69
- return 400;
70
- }
71
- }
72
-
73
- export function parseRequestParams<
74
- SV extends AnySchemaValidator,
75
- Request extends ForklaunchRequest<SV>,
76
- Response extends ForklaunchResponse,
77
- NextFunction extends ForklaunchNextFunction
78
- >(req: Request, res: Response, next?: NextFunction) {
79
- const params = req.contractDetails.params;
80
- if (preHandlerParse(req.schemaValidator, req.params, params) === 400) {
81
- res.status(400).send('Invalid request parameters.');
82
- if (next) {
83
- next(new Error('Invalid request parameters.'));
84
- }
85
- }
86
- if (next) {
87
- next();
88
- }
89
- }
90
-
91
- export function parseRequestBody<
92
- SV extends AnySchemaValidator,
93
- Request extends ForklaunchRequest<SV>,
94
- Response extends ForklaunchResponse,
95
- NextFunction extends ForklaunchNextFunction
96
- >(req: Request, res: Response, next?: NextFunction) {
97
- if (req.headers['content-type'] === 'application/json') {
98
- const body = (req.schemaValidator,
99
- req.contractDetails as HttpContractDetails<SV>).body;
100
- if (
101
- preHandlerParse(
102
- req.schemaValidator,
103
- req.body,
104
- body as StringOnlyObject<SV>
105
- ) === 400
106
- ) {
107
- res.status(400).send('Invalid request body.');
108
- if (next) {
109
- next(new Error('Invalid request body.'));
110
- }
111
- }
112
- }
113
- if (next) {
114
- next();
115
- }
116
- }
117
-
118
- export function parseRequestHeaders<
119
- SV extends AnySchemaValidator,
120
- Request extends ForklaunchRequest<SV>,
121
- Response extends ForklaunchResponse,
122
- NextFunction extends ForklaunchNextFunction
123
- >(req: Request, res: Response, next?: NextFunction) {
124
- const headers = req.contractDetails.requestHeaders;
125
- if (preHandlerParse(req.schemaValidator, req.headers, headers) === 400) {
126
- res.status(400).send('Invalid request headers.');
127
- if (next) {
128
- next(new Error('Invalid request headers.'));
129
- }
130
- }
131
- if (next) {
132
- next();
133
- }
134
- }
135
-
136
- export function parseRequestQuery<
137
- SV extends AnySchemaValidator,
138
- Request extends ForklaunchRequest<SV>,
139
- Response extends ForklaunchResponse,
140
- NextFunction extends ForklaunchNextFunction
141
- >(req: Request, res: Response, next?: NextFunction) {
142
- const query = req.contractDetails.query;
143
- if (preHandlerParse(req.schemaValidator, req.query, query) === 400) {
144
- res.status(400).send('Invalid request query.');
145
- if (next) {
146
- next(new Error('Invalid request query.'));
147
- }
148
- }
149
- if (next) {
150
- next();
151
- }
152
- }
153
-
154
- async function checkAuthorizationToken(
155
- authorizationMethod?: AuthMethod,
156
- authorizationString?: string
157
- ): Promise<[401 | 403, string] | string | undefined> {
158
- if (!authorizationString) {
159
- return [401, 'No Authorization token provided.'];
160
- }
161
- switch (authorizationMethod) {
162
- case 'jwt': {
163
- if (!authorizationString.startsWith('Bearer ')) {
164
- return [401, 'Invalid Authorization token format.'];
165
- }
166
- try {
167
- const decodedJwt = await jose.jwtVerify(
168
- authorizationString.split(' ')[1],
169
- new TextEncoder().encode(
170
- process.env.JWT_SECRET || 'your-256-bit-secret'
171
- )
172
- );
173
- return decodedJwt.payload.iss;
174
- } catch (error) {
175
- console.error(error);
176
- return [403, 'Invalid Authorization token.'];
177
- }
178
- }
179
- default:
180
- return [401, 'Invalid Authorization method.'];
181
- }
182
- }
183
-
184
- function mapRoles(
185
- authorizationType?: AuthMethod,
186
- authorizationToken?: string
187
- ): string[] {
188
- return [];
189
- }
190
- function mapPermissions(
191
- authorizationType?: AuthMethod,
192
- authorizationToken?: string
193
- ): string[] {
194
- return [];
195
- }
196
-
197
- export async function parseRequestAuth<
198
- SV extends AnySchemaValidator,
199
- Request extends ForklaunchRequest<SV>,
200
- Response extends ForklaunchResponse,
201
- NextFunction extends ForklaunchNextFunction
202
- >(req: Request, res: Response, next?: NextFunction) {
203
- const auth = req.contractDetails.auth;
204
- if (auth) {
205
- const errorAndMessage = await checkAuthorizationToken(
206
- auth.method,
207
- req.headers.authorization
208
- );
209
- if (Array.isArray(errorAndMessage)) {
210
- res.status(errorAndMessage[0]).send(errorAndMessage[1]);
211
- if (next) {
212
- next(new Error(errorAndMessage[1]));
213
- }
214
- }
215
-
216
- // TODO: Implement role and permission checking
217
- const permissionSlugs = mapPermissions(
218
- auth.method,
219
- req.headers.authorization
220
- );
221
- const roles = mapRoles(auth.method, req.headers.authorization);
222
-
223
- const permissionErrorMessage =
224
- 'User does not have sufficient permissions to perform action.';
225
- const roleErrorMessage =
226
- 'User does not have correct role to perform action.';
227
-
228
- // this is wrong, we need to check if any of the user's permissions are in the allowed permissions, while checking that any of the permissions is not in the forbidden slugs
229
- // currently this is checking if any of the user's permissions are NOT in the allowed permissions
230
- permissionSlugs.forEach((permissionSlug) => {
231
- if (
232
- !req.contractDetails.auth?.allowedSlugs?.has(permissionSlug) ||
233
- req.contractDetails.auth?.forbiddenSlugs?.has(permissionSlug)
234
- ) {
235
- res.status(403).send(permissionErrorMessage);
236
- if (next) {
237
- next(new Error(permissionErrorMessage));
238
- }
239
- }
240
- });
241
- roles.forEach((role) => {
242
- if (
243
- !req.contractDetails.auth?.allowedRoles?.has(role) ||
244
- req.contractDetails.auth?.forbiddenRoles?.has(role)
245
- ) {
246
- res.status(403).send(roleErrorMessage);
247
- if (next) {
248
- next(new Error(roleErrorMessage));
249
- }
250
- }
251
- });
252
- }
253
-
254
- // if (next) {
255
- // next();
256
- // }
257
- }
@@ -1,63 +0,0 @@
1
- import { AnySchemaValidator } from '@forklaunch/validator';
2
- import {
3
- ForklaunchNextFunction,
4
- ForklaunchRequest,
5
- ForklaunchResponse
6
- } from '../types/api.types';
7
- import { HttpContractDetails } from '../types/primitive.types';
8
-
9
- function checkAnyValidation<SV extends AnySchemaValidator>(
10
- contractDetails: HttpContractDetails<SV>
11
- ) {
12
- return (
13
- contractDetails.body ||
14
- contractDetails.params ||
15
- contractDetails.requestHeaders ||
16
- contractDetails.query
17
- );
18
- }
19
-
20
- export function parseResponse<
21
- SV extends AnySchemaValidator,
22
- Request extends ForklaunchRequest<SV>,
23
- Response extends ForklaunchResponse,
24
- NextFunction extends ForklaunchNextFunction
25
- >(req: Request, res: Response, next?: NextFunction) {
26
- if (req.contractDetails.responseHeaders) {
27
- const schema = req.schemaValidator.schemify(
28
- req.contractDetails.responseHeaders
29
- );
30
- req.schemaValidator.validate(schema, res.getHeaders());
31
- }
32
-
33
- if (
34
- res.statusCode === 500 ||
35
- (checkAnyValidation(req.contractDetails) && res.statusCode === 400) ||
36
- (req.contractDetails.auth &&
37
- (res.statusCode === 401 || res.statusCode === 403))
38
- ) {
39
- req.schemaValidator.validate(req.schemaValidator.string, res.bodyData);
40
- return;
41
- }
42
- if (
43
- Object.prototype.hasOwnProperty.call(
44
- !req.contractDetails.responses,
45
- res.statusCode
46
- )
47
- ) {
48
- if (next) {
49
- next(
50
- new Error(`Response code ${res.statusCode} not defined in contract.`)
51
- );
52
- }
53
- }
54
-
55
- const schema = req.schemaValidator.schemify(
56
- req.contractDetails.responses[res.statusCode]
57
- );
58
- req.schemaValidator.validate(schema, res.bodyData);
59
-
60
- if (next) {
61
- next();
62
- }
63
- }
@@ -1,83 +0,0 @@
1
- import { Prettify } from '@forklaunch/common';
2
- import {
3
- AnySchemaValidator,
4
- Schema
5
- } from '@forklaunch/validator';
6
- import { IdiomaticSchema, SchemaValidator } from '@forklaunch/validator/types';
7
- import { IncomingHttpHeaders, OutgoingHttpHeader } from 'http';
8
- import { ParsedQs } from 'qs';
9
- import {
10
- HttpContractDetails,
11
- ParamsDictionary,
12
- PathParamHttpContractDetails
13
- } from './primitive.types';
14
-
15
- export interface RequestContext {
16
- correlationId: string;
17
- idempotencyKey?: string;
18
- }
19
-
20
- export interface ForklaunchRequest<
21
- SV extends AnySchemaValidator,
22
- P = ParamsDictionary,
23
- ReqBody = unknown,
24
- ReqQuery = ParsedQs,
25
- Headers = IncomingHttpHeaders
26
- > {
27
- context: Prettify<RequestContext>;
28
- contractDetails: HttpContractDetails<SV> | PathParamHttpContractDetails<SV>;
29
- schemaValidator: SchemaValidator;
30
-
31
- params: P;
32
- headers: Headers;
33
- body: ReqBody;
34
- query: ReqQuery;
35
- }
36
-
37
- export interface ForklaunchResponse<
38
- ResBody = {
39
- 400: unknown;
40
- 401: unknown;
41
- 403: unknown;
42
- 500: unknown;
43
- },
44
- StatusCode = number
45
- > {
46
- bodyData: unknown;
47
- statusCode: StatusCode;
48
- corked: boolean;
49
-
50
- getHeaders: () => OutgoingHttpHeader;
51
- setHeader: (key: string, value: string) => void;
52
- status: {
53
- <U extends keyof ResBody>(code: U): ForklaunchResponse<ResBody[U], U>;
54
- <U extends keyof ResBody>(
55
- code: U,
56
- message?: string
57
- ): ForklaunchResponse<ResBody[U], U>;
58
- <U extends 500>(code: U): ForklaunchResponse<string, U>;
59
- <U extends 500>(code: U, message?: string): ForklaunchResponse<string, U>;
60
- };
61
- send: {
62
- <T>(body?: ResBody, close_connection?: boolean): T;
63
- <T>(body?: ResBody): T;
64
- };
65
- json: {
66
- (body?: ResBody): boolean;
67
- <T>(body?: ResBody): T;
68
- };
69
- jsonp: {
70
- (body?: ResBody): boolean;
71
- <T>(body?: ResBody): T;
72
- };
73
- }
74
- export type MapSchema<
75
- SV extends AnySchemaValidator,
76
- T extends IdiomaticSchema<SV> | SV['_ValidSchemaObject']
77
- > =
78
- Schema<T, SV> extends infer U
79
- ? { [key: string]: unknown } extends U
80
- ? never
81
- : U
82
- : never;
83
- export type ForklaunchNextFunction = (err?: unknown) => void;
@@ -1,2 +0,0 @@
1
- export * from './api.types';
2
- export * from './primitive.types';
@@ -1,76 +0,0 @@
1
- import { AnySchemaValidator } from '@forklaunch/validator';
2
- import { UnboxedObjectSchema } from '@forklaunch/validator/types';
3
-
4
- export type ParamsDictionary = { [key: string]: string };
5
-
6
- export type StringOnlyObject<SV extends AnySchemaValidator> = Omit<
7
- UnboxedObjectSchema<SV>,
8
- number | symbol
9
- >;
10
- export type NumberOnlyObject<SV extends AnySchemaValidator> = Omit<
11
- UnboxedObjectSchema<SV>,
12
- string | symbol
13
- >;
14
-
15
- export type BodyObject<SV extends AnySchemaValidator> = StringOnlyObject<SV> &
16
- unknown;
17
- export type ParamsObject<SV extends AnySchemaValidator> = StringOnlyObject<SV> &
18
- unknown;
19
- export type QueryObject<SV extends AnySchemaValidator> = StringOnlyObject<SV> &
20
- unknown;
21
- export type HeadersObject<SV extends AnySchemaValidator> =
22
- StringOnlyObject<SV> & unknown;
23
- export type ResponsesObject<SV extends AnySchemaValidator> = {
24
- [key: number]:
25
- | SV['_ValidSchemaObject']
26
- | UnboxedObjectSchema<SV>
27
- | string
28
- | SV['string'];
29
- } & unknown;
30
-
31
- export type Body<SV extends AnySchemaValidator> =
32
- | BodyObject<SV>
33
- | SV['_ValidSchemaObject']
34
- | SV['_SchemaCatchall'];
35
-
36
- export type AuthMethod = 'jwt' | 'session';
37
- export interface PathParamHttpContractDetails<
38
- SV extends AnySchemaValidator,
39
- ParamSchemas extends ParamsObject<SV> = ParamsObject<SV>,
40
- ResponseSchemas extends ResponsesObject<SV> = ResponsesObject<SV>,
41
- QuerySchemas extends QueryObject<SV> = QueryObject<SV>
42
- > {
43
- name: string;
44
- summary: string;
45
- responses: ResponseSchemas;
46
- requestHeaders?: HeadersObject<SV>;
47
- responseHeaders?: HeadersObject<SV>;
48
- params?: ParamSchemas;
49
- query?: QuerySchemas;
50
- auth?: {
51
- method: AuthMethod;
52
- allowedSlugs?: Set<string>;
53
- forbiddenSlugs?: Set<string>;
54
- allowedRoles?: Set<string>;
55
- forbiddenRoles?: Set<string>;
56
- };
57
- }
58
-
59
- export interface HttpContractDetails<
60
- SV extends AnySchemaValidator,
61
- ParamSchemas extends ParamsObject<SV> = ParamsObject<SV>,
62
- ResponseSchemas extends ResponsesObject<SV> = ResponsesObject<SV>,
63
- BodySchema extends Body<SV> = Body<SV>,
64
- QuerySchemas extends QueryObject<SV> = QueryObject<SV>
65
- > extends PathParamHttpContractDetails<
66
- SV,
67
- ParamSchemas,
68
- ResponseSchemas,
69
- QuerySchemas
70
- > {
71
- body?: BodySchema;
72
- contentType?:
73
- | 'application/json'
74
- | 'multipart/form-data'
75
- | 'application/x-www-form-urlencoded';
76
- }
package/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './cache';
2
- export * from './controllers';
3
- export * from './database';
4
- export * from './entityMapper';
5
- export * from './http';
6
- export * from './services';
package/jest.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { Config } from 'jest';
2
-
3
- const config: Config = {
4
- verbose: true,
5
- preset: 'ts-jest',
6
- testEnvironment: 'node',
7
- testPathIgnorePatterns: ['dist/', 'node_modules/']
8
- };
9
-
10
- export default config;
@@ -1,219 +0,0 @@
1
- import {
2
- TypeboxSchemaValidator,
3
- number,
4
- string
5
- } from '@forklaunch/validator/typebox';
6
- import { BaseEntity } from '../database/mikro/models/entities/base.entity';
7
- import { RequestEntityMapper } from '../entityMapper/models/requestEntityMapper.model';
8
- import { ResponseEntityMapper } from '../entityMapper/models/responseEntityMapper.model';
9
-
10
- class TestEntity extends BaseEntity {
11
- name: string;
12
- age: number;
13
- }
14
-
15
- class TestRequestEntityMapper extends RequestEntityMapper<
16
- TestEntity,
17
- TypeboxSchemaValidator
18
- > {
19
- schema = {
20
- id: string,
21
- name: string,
22
- age: number
23
- };
24
-
25
- toEntity(...additionalArgs: unknown[]): TestEntity {
26
- const entity = new TestEntity();
27
- entity.id = this.dto.id;
28
- entity.name = this.dto.name;
29
- entity.age = this.dto.age;
30
-
31
- return entity;
32
- }
33
- }
34
-
35
- class TestResponseEntityMapper extends ResponseEntityMapper<
36
- TestEntity,
37
- TypeboxSchemaValidator
38
- > {
39
- schema = {
40
- id: string,
41
- name: string,
42
- age: number
43
- };
44
-
45
- fromEntity(entity: TestEntity): this {
46
- this.dto = {
47
- id: entity.id,
48
- name: entity.name,
49
- age: entity.age
50
- };
51
-
52
- return this;
53
- }
54
- }
55
-
56
- function extractNonTimeBasedEntityFields<T extends BaseEntity>(entity: T): T {
57
- entity.createdAt = new Date(0);
58
- entity.updatedAt = new Date(0);
59
- return entity;
60
- }
61
-
62
- describe('Request Entity Mapper Test', () => {
63
- let TestRequestEM: TestRequestEntityMapper;
64
-
65
- beforeAll(() => {
66
- TestRequestEM = new TestRequestEntityMapper(new TypeboxSchemaValidator());
67
- });
68
-
69
- test('Schema Equality', async () => {
70
- expect(TestRequestEM.schema).toEqual(TestRequestEntityMapper.schema());
71
- });
72
-
73
- test('From JSON', async () => {
74
- const json = {
75
- id: '123',
76
- name: 'test',
77
- age: 1
78
- };
79
-
80
- const responseEM = TestRequestEM.fromJson(json);
81
- const staticEM = TestRequestEntityMapper.fromJson(
82
- new TypeboxSchemaValidator(),
83
- json
84
- );
85
- const expectedDto = {
86
- id: '123',
87
- name: 'test',
88
- age: 1
89
- };
90
-
91
- expect(staticEM.dto).toEqual(expectedDto);
92
- expect(responseEM.dto).toEqual(expectedDto);
93
- expect(responseEM.dto).toEqual(staticEM.dto);
94
- });
95
-
96
- test('Deserialization Equality', async () => {
97
- const json = {
98
- id: '123',
99
- name: 'test',
100
- age: 1
101
- };
102
-
103
- const entity = extractNonTimeBasedEntityFields(
104
- TestRequestEM.deserializeJsonToEntity(json)
105
- );
106
- const objectEntity = extractNonTimeBasedEntityFields(
107
- TestRequestEM.fromJson(json).toEntity()
108
- );
109
- const staticEntity = extractNonTimeBasedEntityFields(
110
- TestRequestEntityMapper.deserializeJsonToEntity(
111
- new TypeboxSchemaValidator(),
112
- json
113
- )
114
- );
115
- let expectedEntity = new TestEntity();
116
- expectedEntity.id = '123';
117
- expectedEntity.name = 'test';
118
- expectedEntity.age = 1;
119
-
120
- expectedEntity = extractNonTimeBasedEntityFields(expectedEntity);
121
-
122
- expect(entity).toEqual(expectedEntity);
123
- expect(objectEntity).toEqual(expectedEntity);
124
- expect(staticEntity).toEqual(expectedEntity);
125
- expect(entity).toEqual(objectEntity);
126
- expect(entity).toEqual(staticEntity);
127
- expect(staticEntity).toEqual(expectedEntity);
128
- expect(staticEntity).toEqual(objectEntity);
129
- });
130
-
131
- test('Serialization Failure', async () => {
132
- const json = {
133
- id: '123',
134
- name: 'test'
135
- };
136
-
137
- // @ts-expect-error
138
- expect(() => TestRequestEM.fromJson(json)).toThrow();
139
- expect(() =>
140
- // @ts-expect-error
141
- TestRequestEntityMapper.fromJson(new TypeboxSchemaValidator(), json)
142
- ).toThrow();
143
- });
144
- });
145
-
146
- describe('Response Entity Mapper Test', () => {
147
- let TestResponseEM: TestResponseEntityMapper;
148
-
149
- beforeAll(() => {
150
- TestResponseEM = new TestResponseEntityMapper(new TypeboxSchemaValidator());
151
- });
152
-
153
- test('Schema Equality', async () => {
154
- expect(TestResponseEM.schema).toEqual(TestResponseEntityMapper.schema());
155
- });
156
-
157
- test('From Entity', async () => {
158
- const entity = new TestEntity();
159
- entity.id = '123';
160
- entity.name = 'test';
161
- entity.age = 1;
162
-
163
- const responseEM = TestResponseEM.fromEntity(entity);
164
- const staticEM = TestResponseEntityMapper.fromEntity(
165
- new TypeboxSchemaValidator(),
166
- entity
167
- );
168
- const expectedDto = {
169
- id: '123',
170
- name: 'test',
171
- age: 1
172
- };
173
-
174
- expect(staticEM.dto).toEqual(expectedDto);
175
- expect(responseEM.dto).toEqual(expectedDto);
176
- expect(responseEM.dto).toEqual(staticEM.dto);
177
- });
178
-
179
- test('Serialization Equality', async () => {
180
- const entity = new TestEntity();
181
- entity.id = '123';
182
- entity.name = 'test';
183
- entity.age = 1;
184
-
185
- const json = TestResponseEM.serializeEntityToJson(entity);
186
- const objectJson = TestResponseEM.fromEntity(entity).toJson();
187
- const staticJson = TestResponseEntityMapper.serializeEntityToJson(
188
- new TypeboxSchemaValidator(),
189
- entity
190
- );
191
- const expectedJson = {
192
- id: '123',
193
- name: 'test',
194
- age: 1
195
- };
196
-
197
- expect(json).toEqual(expectedJson);
198
- expect(objectJson).toEqual(expectedJson);
199
- expect(staticJson).toEqual(expectedJson);
200
- expect(json).toEqual(objectJson);
201
- expect(json).toEqual(staticJson);
202
- expect(staticJson).toEqual(expectedJson);
203
- expect(staticJson).toEqual(objectJson);
204
- });
205
-
206
- test('Serialization Failure', async () => {
207
- const entity = new TestEntity();
208
- entity.id = '123';
209
- entity.name = 'test';
210
-
211
- expect(() => TestResponseEM.fromEntity(entity).toJson()).toThrow();
212
- expect(() =>
213
- TestResponseEntityMapper.fromEntity(
214
- new TypeboxSchemaValidator(),
215
- entity
216
- ).toJson()
217
- ).toThrow();
218
- });
219
- });
@@ -1,99 +0,0 @@
1
- import { SchemaValidator } from '@forklaunch/validator';
2
- import {
3
- MockSchemaValidator,
4
- literal,
5
- mockSchemaValidator,
6
- optional,
7
- union
8
- } from '@forklaunch/validator/tests/mockSchemaValidator';
9
- import { ForklaunchRequest, ForklaunchResponse, HttpContractDetails, RequestContext, createRequestContext, enrichRequestDetails, parseRequestBody, parseRequestHeaders, parseRequestParams, parseRequestQuery, parseResponse } from '../http';
10
-
11
- describe('Http Middleware Tests', () => {
12
- let contractDetails: HttpContractDetails<MockSchemaValidator>;
13
- let req: ForklaunchRequest<MockSchemaValidator>;
14
- let resp: ForklaunchResponse;
15
-
16
- const nextFunction = (err?: unknown) => {
17
- expect(err).toBeFalsy()
18
- };
19
-
20
- const testSchema = {
21
- test: union(['a', optional(literal('test'))] as const)
22
- };
23
-
24
- beforeAll(() => {
25
- contractDetails = {
26
- name: 'Test Contract',
27
- summary: 'Test Contract Summary',
28
- body: testSchema,
29
- params: testSchema,
30
- requestHeaders: testSchema,
31
- query: testSchema,
32
- responses: {
33
- 200: testSchema
34
- }
35
- };
36
-
37
- req = {
38
- context: {} as RequestContext,
39
- contractDetails: {} as HttpContractDetails<MockSchemaValidator>,
40
- schemaValidator: {} as SchemaValidator,
41
- params: testSchema,
42
- headers: testSchema,
43
- body: testSchema,
44
- query: testSchema
45
- };
46
-
47
- resp = {
48
- bodyData: {},
49
- statusCode: 200,
50
- corked: false,
51
- getHeaders: jest.fn(),
52
- setHeader: jest.fn(),
53
- status: jest.fn(),
54
- send: jest.fn(),
55
- json: jest.fn(),
56
- jsonp: jest.fn(),
57
- };
58
- });
59
-
60
-
61
-
62
- test('Create Request Context', async () => {
63
- req.context = {} as RequestContext;
64
- req.schemaValidator = {} as SchemaValidator;
65
- createRequestContext(mockSchemaValidator)(req, resp, nextFunction);
66
- expect(req.context.correlationId).not.toBe('123');
67
- expect(req.schemaValidator).toBe(mockSchemaValidator);
68
- });
69
-
70
- test('Enrich Request Details', async () => {
71
- req.contractDetails = {} as HttpContractDetails<MockSchemaValidator>;
72
- enrichRequestDetails(contractDetails)(req, resp, nextFunction);
73
- expect(req.contractDetails).toEqual(contractDetails);
74
- });
75
-
76
- test('Validate Request Params', async () => {
77
- parseRequestParams(req, resp, nextFunction);
78
- });
79
-
80
- test('Validate Request Headers', async () => {
81
- parseRequestHeaders(req, resp, nextFunction);
82
- });
83
-
84
- test('Validate Request Body', async () => {
85
- parseRequestBody(req, resp, nextFunction);
86
- });
87
-
88
- test('Validate Request Query Params', async () => {
89
- parseRequestQuery(req, resp, nextFunction);
90
- });
91
-
92
- test('Validate Response', async () => {
93
- parseResponse(req, resp, nextFunction);
94
- });
95
-
96
- // Not supported yet
97
- // test('Validate Auth', async () => {
98
- // });
99
- });
@@ -1,62 +0,0 @@
1
- import { GenericContainer, StartedTestContainer } from 'testcontainers';
2
- import { RedisTtlCache } from '../cache/redisTtlCache';
3
-
4
- describe('RedisTtlCache', () => {
5
- let container: StartedTestContainer;
6
- let cache: RedisTtlCache;
7
- let key: string;
8
- let value: unknown;
9
- let ttlMilliseconds: number;
10
-
11
- beforeAll(async () => {
12
- container = await new GenericContainer('redis')
13
- .withExposedPorts(6379)
14
- .start();
15
-
16
- cache = new RedisTtlCache(5000, {
17
- url: `redis://${container.getHost()}:${container.getMappedPort(6379)}`
18
- });
19
-
20
- key = 'testKey';
21
- value = { data: 'testValue' };
22
- ttlMilliseconds = 1000;
23
- }, 30000);
24
-
25
- afterAll(async () => {
26
- await cache.disconnect();
27
- await container.stop();
28
- });
29
-
30
- it('PutRecord', async () => {
31
- await cache.putRecord({ key, value, ttlMilliseconds });
32
- });
33
-
34
- test('Read Record', async () => {
35
- const storedValue = await cache.readRecord(key);
36
-
37
- expect(storedValue).toEqual({
38
- key,
39
- ttlMilliseconds,
40
- value
41
- });
42
- });
43
-
44
- test('Peek Record', async () => {
45
- const exists = await cache.peekRecord(key);
46
-
47
- expect(exists).toBeTruthy();
48
- });
49
-
50
- test('Delete Record', async () => {
51
- await cache.deleteRecord(key);
52
- const existsAfterDelete = await cache.peekRecord(key);
53
-
54
- expect(existsAfterDelete).toBeFalsy();
55
- });
56
-
57
- test('Check No Record', async () => {
58
- await Promise.resolve(setTimeout(async () => {}, ttlMilliseconds));
59
- const existsAfterTtl = await cache.peekRecord(key);
60
- expect(existsAfterTtl).toBeFalsy();
61
- });
62
- });