mock-config-server 3.2.0 → 3.3.1
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 +153 -3
- package/dist/bin/validateMockServerConfig/helpers/isDescriptorValueValid/isDescriptorValueValid.d.ts +2 -1
- package/dist/bin/validateMockServerConfig/helpers/isDescriptorValueValid/isDescriptorValueValid.js +30 -7
- package/dist/bin/validateMockServerConfig/validateGraphqlConfig/validateGraphqlConfig.js +9 -3
- package/dist/bin/validateMockServerConfig/validateGraphqlConfig/validateRoutes/validateRoutes.js +67 -18
- package/dist/bin/validateMockServerConfig/validateQueue/validateQueue.d.ts +1 -0
- package/dist/bin/validateMockServerConfig/validateQueue/validateQueue.js +29 -0
- package/dist/bin/validateMockServerConfig/validateRestConfig/validateRoutes/validateRoutes.js +67 -18
- package/dist/bin/validateMockServerConfig/validateSettings/validateSettings.d.ts +1 -0
- package/dist/bin/validateMockServerConfig/validateSettings/validateSettings.js +34 -0
- package/dist/src/core/database/createDatabaseRoutes/createDatabaseRoutes.test.ts +112 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/array/createNewId/createNewId.test.ts +13 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/array/findIndexById/findIndexById.test.ts +17 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/array/isIndex/isIndex.test.ts +30 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/createNestedDatabaseRoutes/createNestedDatabaseRoutes.js +45 -2
- package/dist/src/core/database/createDatabaseRoutes/helpers/createNestedDatabaseRoutes/createNestedDatabaseRoutes.test.ts +399 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/createShallowDatabaseRoutes/createShallowDatabaseRoutes.test.ts +118 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/filter/filter.d.ts +2 -1
- package/dist/src/core/database/createDatabaseRoutes/helpers/operators/operators.d.ts +3 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/operators/operators.js +30 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/pagination/pagination.d.ts +13 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/pagination/pagination.js +36 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/search/search.d.ts +3 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/search/search.js +31 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/sort/sort.d.ts +2 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/sort/sort.js +42 -0
- package/dist/src/core/database/createDatabaseRoutes/helpers/splitDatabaseByNesting/splitDatabaseByNesting.test.ts +25 -0
- package/dist/src/core/database/createDatabaseRoutes/storages/File/FileStorage.test.ts +156 -0
- package/dist/src/core/database/createDatabaseRoutes/storages/File/FileWriter.test.ts +48 -0
- package/dist/src/core/database/createDatabaseRoutes/storages/Memory/MemoryStorage.test.ts +96 -0
- package/dist/src/core/graphql/createGraphQLRoutes/createGraphQLRoutes.d.ts +7 -1
- package/dist/src/core/graphql/createGraphQLRoutes/createGraphQLRoutes.js +65 -24
- package/dist/src/core/graphql/createGraphQLRoutes/createGraphQLRoutes.test.ts +851 -0
- package/dist/src/core/graphql/createGraphQLRoutes/helpers/prepareGraphQLRequestConfigs/prepareGraphQLRequestConfigs.test.ts +116 -0
- package/dist/src/core/middlewares/cookieParseMiddleware/cookieParseMiddleware.test.ts +22 -0
- package/dist/src/core/middlewares/cookieParseMiddleware/helpers/parseCookie/parseCookie.test.ts +45 -0
- package/dist/src/core/middlewares/corsMiddleware/corsMiddleware.test.ts +152 -0
- package/dist/src/core/middlewares/corsMiddleware/helpers/getAllowedOrigins/getAllowedOrigins.test.ts +15 -0
- package/dist/src/core/middlewares/errorMiddleware/errorMiddleware.test.ts +29 -0
- package/dist/src/core/middlewares/noCorsMiddleware/noCorsMiddleware.test.ts +49 -0
- package/dist/src/core/middlewares/notFoundMiddleware/helpers/getGraphqlUrlSuggestions/getGraphqlUrlSuggestions.test.ts +27 -0
- package/dist/src/core/middlewares/notFoundMiddleware/helpers/getLevenshteinDistance/getLevenshteinDistance.test.ts +12 -0
- package/dist/src/core/middlewares/notFoundMiddleware/helpers/getRestUrlSuggestions/getRestUrlSuggestions.test.ts +54 -0
- package/dist/src/core/middlewares/notFoundMiddleware/helpers/getRestUrlSuggestions/helpers/getActualRestUrlMeaningfulString/getActualRestUrlMeaningfulString.test.ts +12 -0
- package/dist/src/core/middlewares/notFoundMiddleware/helpers/getRestUrlSuggestions/helpers/getPatternRestUrlMeaningfulString/getPatternRestUrlMeaningfulString.test.ts +10 -0
- package/dist/src/core/middlewares/notFoundMiddleware/notFoundMiddleware.js +1 -3
- package/dist/src/core/middlewares/notFoundMiddleware/notFoundMiddleware.test.ts +285 -0
- package/dist/src/core/middlewares/requestInterceptorMiddleware/requestInterceptorMiddleware.d.ts +7 -1
- package/dist/src/core/middlewares/requestInterceptorMiddleware/requestInterceptorMiddleware.js +6 -2
- package/dist/src/core/rest/createRestRoutes/createRestRoutes.d.ts +7 -1
- package/dist/src/core/rest/createRestRoutes/createRestRoutes.js +55 -12
- package/dist/src/core/rest/createRestRoutes/createRestRoutes.test.ts +648 -0
- package/dist/src/core/rest/createRestRoutes/helpers/prepareRestRequestConfigs/prepareRestRequestConfigs.test.ts +154 -0
- package/dist/src/server/createDatabaseMockServer/createDatabaseMockServer.js +4 -1
- package/dist/src/server/createGraphQLMockServer/createGraphQLMockServer.js +11 -4
- package/dist/src/server/createMockServer/createMockServer.js +28 -9
- package/dist/src/server/createRestMockServer/createRestMockServer.js +11 -4
- package/dist/src/server/index.d.ts +3 -3
- package/dist/src/server/index.js +23 -23
- package/dist/src/static/views/features/scheme/index.js +31 -31
- package/dist/src/utils/helpers/config/resolveEntityValues/resolveEntityValues.test.ts +1452 -0
- package/dist/src/utils/helpers/entities/convertToEntityDescriptor/convertToEntityDescriptor.test.ts +27 -0
- package/dist/src/utils/helpers/entities/isEntityDescriptor/isEntityDescriptor.d.ts +2 -1
- package/dist/src/utils/helpers/entities/isEntityDescriptor/isEntityDescriptor.test.ts +15 -0
- package/dist/src/utils/helpers/graphql/getGraphQLInput/getGraphQLInput.d.ts +7 -2
- package/dist/src/utils/helpers/graphql/getGraphQLInput/getGraphQLInput.js +6 -4
- package/dist/src/utils/helpers/graphql/getGraphQLInput/getGraphQLInput.test.ts +140 -0
- package/dist/src/utils/helpers/graphql/parseQuery/parseQuery.d.ts +1 -1
- package/dist/src/utils/helpers/graphql/parseQuery/parseQuery.js +1 -1
- package/dist/src/utils/helpers/graphql/parseQuery/parseQuery.test.ts +32 -0
- package/dist/src/utils/helpers/interceptors/callRequestInterceptor/callRequestInterceptors.test.ts +53 -0
- package/dist/src/utils/helpers/interceptors/callResponseInterceptors/callResponseInterceptors.js +19 -10
- package/dist/src/utils/helpers/interceptors/callResponseInterceptors/callResponseInterceptors.test.ts +262 -0
- package/dist/src/utils/helpers/isPlainObject/isPlainObject.test.ts +20 -0
- package/dist/src/utils/helpers/isPrimitive/isPrimitive.test.ts +26 -0
- package/dist/src/utils/helpers/isRegExp/isRegExp.test.ts +20 -0
- package/dist/src/utils/helpers/url/convertWin32PathToUnix/convertWin32PathToUnix.test.ts +21 -0
- package/dist/src/utils/helpers/url/getUrlParts/getUrlParts.test.ts +8 -0
- package/dist/src/utils/helpers/url/removeLeadingAndTrailingSlashes/removeLeadingAndTrailingSlashes.test.ts +10 -0
- package/dist/src/utils/helpers/url/urlJoin/urlJoin.test.ts +9 -0
- package/dist/src/utils/types/graphql.d.ts +60 -51
- package/dist/src/utils/types/index.d.ts +1 -0
- package/dist/src/utils/types/index.js +11 -0
- package/dist/src/utils/types/interceptors.d.ts +1 -1
- package/dist/src/utils/types/rest.d.ts +47 -40
- package/dist/src/utils/types/utils.d.ts +8 -0
- package/dist/src/utils/types/utils.js +1 -0
- package/dist/src/utils/types/values.d.ts +0 -1
- package/package.json +2 -3
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Express } from 'express';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import request from 'supertest';
|
|
4
|
+
|
|
5
|
+
import type { ShallowDatabase } from '@/utils/types';
|
|
6
|
+
|
|
7
|
+
import { MemoryStorage } from '../../storages';
|
|
8
|
+
|
|
9
|
+
import { createShallowDatabaseRoutes } from './createShallowDatabaseRoutes';
|
|
10
|
+
|
|
11
|
+
describe('createShallowDatabaseRoutes', () => {
|
|
12
|
+
const createShallowDatabase = () => ({
|
|
13
|
+
john: { name: 'John Doe', age: 25 },
|
|
14
|
+
jane: { name: 'Jane Smith', age: 30 }
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const createServer = (shallowDatabase: ShallowDatabase) => {
|
|
18
|
+
const server = express();
|
|
19
|
+
const routerBase = express.Router();
|
|
20
|
+
const storage = new MemoryStorage(shallowDatabase);
|
|
21
|
+
|
|
22
|
+
const routerWithRoutesForShallowDatabase = createShallowDatabaseRoutes(
|
|
23
|
+
routerBase,
|
|
24
|
+
shallowDatabase,
|
|
25
|
+
storage
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
server.use(express.json());
|
|
29
|
+
server.use(express.text());
|
|
30
|
+
server.use('/', routerWithRoutesForShallowDatabase);
|
|
31
|
+
|
|
32
|
+
return server;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('createShallowDatabaseRoutes: GET method', () => {
|
|
36
|
+
const shallowDatabase = createShallowDatabase();
|
|
37
|
+
const server = createServer(shallowDatabase);
|
|
38
|
+
|
|
39
|
+
test('Should return correct data for valid key', async () => {
|
|
40
|
+
const response = await request(server).get('/john');
|
|
41
|
+
|
|
42
|
+
expect(response.statusCode).toBe(200);
|
|
43
|
+
expect(response.body).toStrictEqual(shallowDatabase.john);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Should return correct Cache-Control header for valid key', async () => {
|
|
47
|
+
const response = await request(server).get('/john');
|
|
48
|
+
|
|
49
|
+
expect(response.headers['cache-control']).toBe('max-age=0, must-revalidate');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('createShallowDatabaseRoutes: POST method', () => {
|
|
54
|
+
let shallowDatabase: ReturnType<typeof createShallowDatabase>;
|
|
55
|
+
let server: Express;
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
shallowDatabase = createShallowDatabase();
|
|
58
|
+
server = createServer(shallowDatabase);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('Should return correct data for valid key and successfully update database', async () => {
|
|
62
|
+
const newJohnInfo = { age: 26, standName: 'The World' };
|
|
63
|
+
|
|
64
|
+
const postResponse = await request(server).post('/john').send(newJohnInfo);
|
|
65
|
+
expect(postResponse.statusCode).toBe(201);
|
|
66
|
+
expect(postResponse.body).toStrictEqual(newJohnInfo);
|
|
67
|
+
|
|
68
|
+
const getResponse = await request(server).get('/john');
|
|
69
|
+
expect(getResponse.body).toStrictEqual(newJohnInfo);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('Should return correct Location header for valid key', async () => {
|
|
73
|
+
const response = await request(server).post('/john').send(undefined);
|
|
74
|
+
|
|
75
|
+
expect(response.headers.location).toBe('/john');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('createShallowDatabaseRoutes: PUT method', () => {
|
|
80
|
+
let shallowDatabase: ReturnType<typeof createShallowDatabase>;
|
|
81
|
+
let server: Express;
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
shallowDatabase = createShallowDatabase();
|
|
84
|
+
server = createServer(shallowDatabase);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('Should return correct data for valid key and successfully update database', async () => {
|
|
88
|
+
const newJohnInfo = { age: 26, standName: 'The World' };
|
|
89
|
+
|
|
90
|
+
const putResponse = await request(server).put('/john').send(newJohnInfo);
|
|
91
|
+
expect(putResponse.statusCode).toBe(200);
|
|
92
|
+
expect(putResponse.body).toStrictEqual(newJohnInfo);
|
|
93
|
+
|
|
94
|
+
const getResponse = await request(server).get('/john');
|
|
95
|
+
expect(getResponse.body).toStrictEqual(newJohnInfo);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('createShallowDatabaseRoutes: PATCH method', () => {
|
|
100
|
+
let shallowDatabase: ReturnType<typeof createShallowDatabase>;
|
|
101
|
+
let server: Express;
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
shallowDatabase = createShallowDatabase();
|
|
104
|
+
server = createServer(shallowDatabase);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('Should return correct data for valid key and successfully update database', async () => {
|
|
108
|
+
const newJohnInfo = { age: 26, standName: 'The World' };
|
|
109
|
+
|
|
110
|
+
const patchResponse = await request(server).patch('/john').send(newJohnInfo);
|
|
111
|
+
expect(patchResponse.statusCode).toBe(200);
|
|
112
|
+
expect(patchResponse.body).toStrictEqual({ ...shallowDatabase.john, ...newJohnInfo });
|
|
113
|
+
|
|
114
|
+
const getResponse = await request(server).get('/john');
|
|
115
|
+
expect(getResponse.body).toStrictEqual({ ...shallowDatabase.john, ...newJohnInfo });
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ParsedUrlQuery } from 'node:querystring';
|
|
2
|
+
export declare const filter: (array: any[], filters: ParsedUrlQuery) => any[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.operators = void 0;
|
|
7
|
+
// import { flatten } from 'flat';
|
|
8
|
+
|
|
9
|
+
const OPERATORS = {
|
|
10
|
+
neq: (a, b) => a !== b,
|
|
11
|
+
gt: (a, b) => a > b,
|
|
12
|
+
gte: (a, b) => a >= b,
|
|
13
|
+
lt: (a, b) => a < b,
|
|
14
|
+
lte: (a, b) => a <= b,
|
|
15
|
+
in: (a, b) => b.includes(a),
|
|
16
|
+
nin: (a, b) => !b.includes(a),
|
|
17
|
+
cn: (a, b) => a.includes(b),
|
|
18
|
+
ncn: (a, b) => !a.includes(b),
|
|
19
|
+
sw: (a, b) => a.startsWith(b),
|
|
20
|
+
nsw: (a, b) => !a.startsWith(b),
|
|
21
|
+
ew: (a, b) => a.endsWith(b),
|
|
22
|
+
new: (a, b) => !a.endsWith(b),
|
|
23
|
+
rgx: (a, b) => new RegExp(b).test(a)
|
|
24
|
+
};
|
|
25
|
+
console.log('@', OPERATORS);
|
|
26
|
+
const operators = (array, query) => {
|
|
27
|
+
const operators1 = Object.entries(query).map(([key, value]) => console.log('@', [key, value]));
|
|
28
|
+
console.log('@', array, operators1);
|
|
29
|
+
};
|
|
30
|
+
exports.operators = operators;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ParsedUrlQuery } from 'node:querystring';
|
|
2
|
+
export declare const pagination: (array: any[], queries: ParsedUrlQuery) => any[] | {
|
|
3
|
+
_link: {
|
|
4
|
+
count: number;
|
|
5
|
+
pages: number;
|
|
6
|
+
first: number;
|
|
7
|
+
current: number;
|
|
8
|
+
next: number | null;
|
|
9
|
+
prev: number | null;
|
|
10
|
+
last: number | null;
|
|
11
|
+
};
|
|
12
|
+
results: any[];
|
|
13
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.pagination = void 0;
|
|
7
|
+
const DEFAULT_LIMIT = 10;
|
|
8
|
+
const pagination = (array, queries) => {
|
|
9
|
+
const {
|
|
10
|
+
_page
|
|
11
|
+
} = queries;
|
|
12
|
+
if (!_page || +_page <= 0) return array;
|
|
13
|
+
const page = +_page;
|
|
14
|
+
const limit = queries._limit && +queries._limit > 0 ? +queries._limit : DEFAULT_LIMIT;
|
|
15
|
+
const pages = Math.ceil(array.length / limit);
|
|
16
|
+
if (page > pages) return array;
|
|
17
|
+
const start = page * limit - limit;
|
|
18
|
+
const end = page * limit;
|
|
19
|
+
const results = array.slice(start, end);
|
|
20
|
+
const next = page + 1 <= pages ? page + 1 : null;
|
|
21
|
+
const prev = page - 1 !== 0 ? page - 1 : null;
|
|
22
|
+
const last = pages > 0 ? pages : null;
|
|
23
|
+
return {
|
|
24
|
+
_link: {
|
|
25
|
+
count: array.length,
|
|
26
|
+
pages,
|
|
27
|
+
first: 1,
|
|
28
|
+
current: page,
|
|
29
|
+
next,
|
|
30
|
+
prev,
|
|
31
|
+
last
|
|
32
|
+
},
|
|
33
|
+
results
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
exports.pagination = pagination;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.searchInNestedObjects = exports.search = void 0;
|
|
7
|
+
const searchInNestedObjects = (obj, searchText) => {
|
|
8
|
+
for (const key in obj) {
|
|
9
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
10
|
+
if (searchInNestedObjects(obj[key], searchText)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
} else if (typeof obj[key] === 'string' && obj[key].includes(searchText)) {
|
|
14
|
+
return true;
|
|
15
|
+
} else if (typeof obj[key] === 'number' && obj[key].toString().includes(searchText)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
exports.searchInNestedObjects = searchInNestedObjects;
|
|
22
|
+
const search = (array, searchText) => array.filter(element => {
|
|
23
|
+
if (typeof searchText === 'string') {
|
|
24
|
+
return searchInNestedObjects(element, searchText);
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(searchText)) {
|
|
27
|
+
return searchText.some(text => searchInNestedObjects(element, text));
|
|
28
|
+
}
|
|
29
|
+
throw new Error('search technical error');
|
|
30
|
+
});
|
|
31
|
+
exports.search = search;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.sort = void 0;
|
|
7
|
+
var _flat = _interopRequireDefault(require("flat"));
|
|
8
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
|
+
const DEFAULT_ORDER = 'asc';
|
|
10
|
+
const getOrder = order => {
|
|
11
|
+
if (order === 'asc' || order === 'desc') return order;
|
|
12
|
+
return DEFAULT_ORDER;
|
|
13
|
+
};
|
|
14
|
+
const sortArray = (array, key, order) => array.sort((a, b) => {
|
|
15
|
+
const flattenedA = (0, _flat.default)(a);
|
|
16
|
+
const flattenedB = (0, _flat.default)(b);
|
|
17
|
+
if (!flattenedA[key] || !flattenedB[key]) return 0;
|
|
18
|
+
if (typeof flattenedA[key] === 'string' && typeof flattenedB[key] === 'string') {
|
|
19
|
+
return order === 'asc' ? flattenedA[key].localeCompare(flattenedB[key]) : flattenedB[key].localeCompare(flattenedA[key]);
|
|
20
|
+
}
|
|
21
|
+
return order === 'asc' ? Number(flattenedA[key]) - Number(flattenedB[key]) : Number(flattenedB[key]) - Number(flattenedA[key]);
|
|
22
|
+
});
|
|
23
|
+
const sort = (array, queries) => {
|
|
24
|
+
const {
|
|
25
|
+
_sort,
|
|
26
|
+
_order = DEFAULT_ORDER
|
|
27
|
+
} = queries;
|
|
28
|
+
if (!_sort) return array;
|
|
29
|
+
const result = [...array];
|
|
30
|
+
if (Array.isArray(_sort)) {
|
|
31
|
+
const orders = Array.isArray(_order) ? _order : [_order];
|
|
32
|
+
_sort.forEach((key, index) => {
|
|
33
|
+
const order = getOrder(orders[index]);
|
|
34
|
+
sortArray(result, key, order);
|
|
35
|
+
});
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
const order = getOrder(Array.isArray(_order) ? _order[0] : _order);
|
|
39
|
+
sortArray(result, _sort, order);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
exports.sort = sort;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { splitDatabaseByNesting } from './splitDatabaseByNesting';
|
|
2
|
+
|
|
3
|
+
describe('splitDatabaseByNesting', () => {
|
|
4
|
+
test('Should put in nested database only arrays of objects with unique id (string | number)', () => {
|
|
5
|
+
const databaseConfig = {
|
|
6
|
+
data: {
|
|
7
|
+
key: 'value',
|
|
8
|
+
arrayWithInvalidTypeIds: [{ id: 1 }, null],
|
|
9
|
+
arrayWithNotUniqueIds: [{ id: 1 }, { id: 1 }],
|
|
10
|
+
validArray: [{ id: 1 }, { id: 'string' }]
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const { shallowDatabase, nestedDatabase } = splitDatabaseByNesting(databaseConfig.data);
|
|
15
|
+
|
|
16
|
+
expect(shallowDatabase).toStrictEqual({
|
|
17
|
+
key: databaseConfig.data.key,
|
|
18
|
+
arrayWithInvalidTypeIds: databaseConfig.data.arrayWithInvalidTypeIds,
|
|
19
|
+
arrayWithNotUniqueIds: databaseConfig.data.arrayWithNotUniqueIds
|
|
20
|
+
});
|
|
21
|
+
expect(nestedDatabase).toStrictEqual({
|
|
22
|
+
validArray: databaseConfig.data.validArray
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import { FileStorage } from './FileStorage';
|
|
6
|
+
import { FileWriter } from './FileWriter';
|
|
7
|
+
|
|
8
|
+
jest.mock('./FileWriter');
|
|
9
|
+
|
|
10
|
+
describe('FileStorage', () => {
|
|
11
|
+
const createInitialData = () => ({
|
|
12
|
+
john: { name: 'John Doe', age: 25 },
|
|
13
|
+
jane: { name: 'Jane Smith', age: 30 },
|
|
14
|
+
users: [{ id: 1 }, { id: 2 }]
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('FileStorage: read', () => {
|
|
18
|
+
let tmpDirPath: string;
|
|
19
|
+
let initialData: ReturnType<typeof createInitialData>;
|
|
20
|
+
let fileStorage: FileStorage;
|
|
21
|
+
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
tmpDirPath = fs.mkdtempSync(os.tmpdir());
|
|
24
|
+
initialData = createInitialData();
|
|
25
|
+
|
|
26
|
+
const pathToFileStorage = path.join(tmpDirPath, './database.json');
|
|
27
|
+
fs.writeFileSync(pathToFileStorage, JSON.stringify(initialData));
|
|
28
|
+
fileStorage = new FileStorage(pathToFileStorage);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterAll(() => {
|
|
32
|
+
fs.rmSync(tmpDirPath, { recursive: true, force: true });
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('Should return correct full data for read without keys', () => {
|
|
37
|
+
expect(fileStorage.read()).toStrictEqual(initialData);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('Should return correct data for read with valid single key', () => {
|
|
41
|
+
expect(fileStorage.read('john')).toStrictEqual(initialData.john);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Should return correct data for read with valid array key', () => {
|
|
45
|
+
expect(fileStorage.read(['john', 'name'])).toStrictEqual(initialData.john.name);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('FileStorage: write', () => {
|
|
50
|
+
let tmpDirPath: string;
|
|
51
|
+
let initialData: ReturnType<typeof createInitialData>;
|
|
52
|
+
let fileStorage: FileStorage;
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
tmpDirPath = fs.mkdtempSync(os.tmpdir());
|
|
56
|
+
initialData = createInitialData();
|
|
57
|
+
|
|
58
|
+
const pathToFileStorage = path.join(tmpDirPath, './database.json');
|
|
59
|
+
fs.writeFileSync(pathToFileStorage, JSON.stringify(initialData));
|
|
60
|
+
fileStorage = new FileStorage(pathToFileStorage);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
fs.rmSync(tmpDirPath, { recursive: true, force: true });
|
|
65
|
+
jest.clearAllMocks();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('Should update value with valid single key', () => {
|
|
69
|
+
const johnWithNewAge = { ...initialData.john, age: initialData.john.age + 1 };
|
|
70
|
+
|
|
71
|
+
fileStorage.write('john', johnWithNewAge);
|
|
72
|
+
|
|
73
|
+
expect(fileStorage.read('john')).toStrictEqual(johnWithNewAge);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('Should update value with valid array key', () => {
|
|
77
|
+
const newAge = initialData.john.age + 1;
|
|
78
|
+
|
|
79
|
+
fileStorage.write(['john', 'age'], newAge);
|
|
80
|
+
|
|
81
|
+
expect(fileStorage.read(['john', 'age'])).toBe(newAge);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('Should update value with valid key which contain non-existent last part', () => {
|
|
85
|
+
const stand = 'The World';
|
|
86
|
+
|
|
87
|
+
fileStorage.write(['john', 'stand'], stand);
|
|
88
|
+
|
|
89
|
+
expect(fileStorage.read(['john', 'stand'])).toBe(stand);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('Should write in file updated data', () => {
|
|
93
|
+
const fileWriterWriteMethodMock = jest.spyOn(FileWriter.prototype, 'write');
|
|
94
|
+
|
|
95
|
+
fileStorage.write(['john', 'stand'], 'The World');
|
|
96
|
+
|
|
97
|
+
expect(fileWriterWriteMethodMock).toHaveBeenCalledTimes(1);
|
|
98
|
+
expect(fileWriterWriteMethodMock).toHaveBeenCalledWith(JSON.stringify(fileStorage.read()));
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('FileStorage: delete', () => {
|
|
103
|
+
let tmpDirPath: string;
|
|
104
|
+
let initialData: ReturnType<typeof createInitialData>;
|
|
105
|
+
let fileStorage: FileStorage;
|
|
106
|
+
|
|
107
|
+
beforeEach(() => {
|
|
108
|
+
tmpDirPath = fs.mkdtempSync(os.tmpdir());
|
|
109
|
+
initialData = createInitialData();
|
|
110
|
+
|
|
111
|
+
const pathToFileStorage = path.join(tmpDirPath, './database.json');
|
|
112
|
+
fs.writeFileSync(pathToFileStorage, JSON.stringify(initialData));
|
|
113
|
+
fileStorage = new FileStorage(pathToFileStorage);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
afterEach(() => {
|
|
117
|
+
fs.rmSync(tmpDirPath, { recursive: true, force: true });
|
|
118
|
+
jest.clearAllMocks();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('Should correctly delete object property with valid single key', () => {
|
|
122
|
+
expect(fileStorage.read('john')).toStrictEqual(initialData.john);
|
|
123
|
+
|
|
124
|
+
fileStorage.delete('john');
|
|
125
|
+
|
|
126
|
+
expect(fileStorage.read('john')).toBe(undefined);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('Should correctly delete object property with valid array key', () => {
|
|
130
|
+
expect(fileStorage.read('john')).toStrictEqual(initialData.john);
|
|
131
|
+
|
|
132
|
+
fileStorage.delete(['john', 'age']);
|
|
133
|
+
|
|
134
|
+
expect(fileStorage.read('john')).toStrictEqual({ name: initialData.john.name });
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('Should splice array if delete element from array', () => {
|
|
138
|
+
expect(fileStorage.read('users')).toStrictEqual(initialData.users);
|
|
139
|
+
|
|
140
|
+
fileStorage.delete(['users', 0]);
|
|
141
|
+
|
|
142
|
+
const updatedUsers = fileStorage.read('users');
|
|
143
|
+
expect(updatedUsers).toStrictEqual([{ id: 2 }]);
|
|
144
|
+
expect(updatedUsers.length).toBe(1);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('Should write in file updated data', () => {
|
|
148
|
+
const fileWriterWriteMethodMock = jest.spyOn(FileWriter.prototype, 'write');
|
|
149
|
+
|
|
150
|
+
fileStorage.delete(['users', 0]);
|
|
151
|
+
|
|
152
|
+
expect(fileWriterWriteMethodMock).toHaveBeenCalledTimes(1);
|
|
153
|
+
expect(fileWriterWriteMethodMock).toHaveBeenCalledWith(JSON.stringify(fileStorage.read()));
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import { FileWriter } from './FileWriter';
|
|
6
|
+
|
|
7
|
+
describe('FileWriter', () => {
|
|
8
|
+
let tmpDirPath: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tmpDirPath = fs.mkdtempSync(os.tmpdir());
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
fs.rmSync(tmpDirPath, { recursive: true, force: true });
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('Write asynchronously in file only last data (before Promise become fulfilled)', async () => {
|
|
20
|
+
const fileName = './database.json';
|
|
21
|
+
const filePath = path.join(tmpDirPath, fileName);
|
|
22
|
+
const fileWriter = new FileWriter(filePath);
|
|
23
|
+
|
|
24
|
+
const writePromises: Promise<void>[] = [];
|
|
25
|
+
const writeOperationCount = 100;
|
|
26
|
+
for (let i = 0; i <= writeOperationCount; i += 1) {
|
|
27
|
+
writePromises.push(fileWriter.write(JSON.stringify({ index: i })));
|
|
28
|
+
}
|
|
29
|
+
await Promise.all(writePromises);
|
|
30
|
+
|
|
31
|
+
const fileData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
32
|
+
expect(fileData).toStrictEqual({ index: writeOperationCount });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('Write in file only if writing is unlocked (first write and last write)', async () => {
|
|
36
|
+
const fileWriter = new FileWriter(path.join(tmpDirPath, './database.json'));
|
|
37
|
+
const fsPromisesWriteFileMock = jest.spyOn(fs.promises, 'writeFile');
|
|
38
|
+
|
|
39
|
+
const writePromises: Promise<void>[] = [];
|
|
40
|
+
const writeOperationCount = 100;
|
|
41
|
+
for (let i = 0; i <= writeOperationCount; i += 1) {
|
|
42
|
+
writePromises.push(fileWriter.write(JSON.stringify({ index: i })));
|
|
43
|
+
}
|
|
44
|
+
await Promise.all(writePromises);
|
|
45
|
+
|
|
46
|
+
expect(fsPromisesWriteFileMock).toHaveBeenCalledTimes(2);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { MemoryStorage } from './MemoryStorage';
|
|
2
|
+
|
|
3
|
+
describe('MemoryStorage', () => {
|
|
4
|
+
const createInitialData = () => ({
|
|
5
|
+
john: { name: 'John Doe', age: 25 },
|
|
6
|
+
jane: { name: 'Jane Smith', age: 30 },
|
|
7
|
+
users: [{ id: 1 }, { id: 2 }]
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('MemoryStorage: read', () => {
|
|
11
|
+
const initialData = createInitialData();
|
|
12
|
+
const memoryStorage = new MemoryStorage(initialData);
|
|
13
|
+
|
|
14
|
+
test('Should return correct full data for read without keys', () => {
|
|
15
|
+
expect(memoryStorage.read()).toStrictEqual(initialData);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('Should return correct data for read with valid single key', () => {
|
|
19
|
+
expect(memoryStorage.read('john')).toStrictEqual(initialData.john);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('Should return correct data for read with valid array key', () => {
|
|
23
|
+
expect(memoryStorage.read(['john', 'name'])).toStrictEqual(initialData.john.name);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('MemoryStorage: write', () => {
|
|
28
|
+
let initialData: ReturnType<typeof createInitialData>;
|
|
29
|
+
let memoryStorage: MemoryStorage<typeof initialData>;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
initialData = createInitialData();
|
|
33
|
+
memoryStorage = new MemoryStorage(initialData);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('Should update value with valid single key', () => {
|
|
37
|
+
const johnWithNewAge = { ...initialData.john, age: initialData.john.age + 1 };
|
|
38
|
+
|
|
39
|
+
memoryStorage.write('john', johnWithNewAge);
|
|
40
|
+
|
|
41
|
+
expect(memoryStorage.read('john')).toStrictEqual(johnWithNewAge);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Should update value with valid array key', () => {
|
|
45
|
+
const newAge = initialData.john.age + 1;
|
|
46
|
+
|
|
47
|
+
memoryStorage.write(['john', 'age'], newAge);
|
|
48
|
+
|
|
49
|
+
expect(memoryStorage.read(['john', 'age'])).toBe(newAge);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('Should update value with valid key which contain non-existent last part', () => {
|
|
53
|
+
const stand = initialData.john.age + 1;
|
|
54
|
+
|
|
55
|
+
memoryStorage.write(['john', 'stand'], stand);
|
|
56
|
+
|
|
57
|
+
expect(memoryStorage.read(['john', 'stand'])).toBe(stand);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('MemoryStorage: delete', () => {
|
|
62
|
+
let initialData: ReturnType<typeof createInitialData>;
|
|
63
|
+
let memoryStorage: MemoryStorage<typeof initialData>;
|
|
64
|
+
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
initialData = createInitialData();
|
|
67
|
+
memoryStorage = new MemoryStorage(initialData);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('Should correctly delete object property with valid single key', () => {
|
|
71
|
+
expect(memoryStorage.read('john')).toStrictEqual(initialData.john);
|
|
72
|
+
|
|
73
|
+
memoryStorage.delete('john');
|
|
74
|
+
|
|
75
|
+
expect(memoryStorage.read('john')).toBe(undefined);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('Should correctly delete object property with valid array key', () => {
|
|
79
|
+
expect(memoryStorage.read('john')).toStrictEqual(initialData.john);
|
|
80
|
+
|
|
81
|
+
memoryStorage.delete(['john', 'age']);
|
|
82
|
+
|
|
83
|
+
expect(memoryStorage.read('john')).toStrictEqual({ name: initialData.john.name });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('Should splice array if delete element from array', () => {
|
|
87
|
+
expect(memoryStorage.read('users')).toStrictEqual(initialData.users);
|
|
88
|
+
|
|
89
|
+
memoryStorage.delete(['users', 0]);
|
|
90
|
+
|
|
91
|
+
const updatedUsers = memoryStorage.read('users');
|
|
92
|
+
expect(updatedUsers).toStrictEqual([{ id: 2 }]);
|
|
93
|
+
expect(updatedUsers.length).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import type { IRouter } from 'express';
|
|
2
2
|
import type { GraphqlConfig, Interceptors } from '../../../utils/types';
|
|
3
|
-
|
|
3
|
+
interface CreateGraphQLRoutesParams {
|
|
4
|
+
router: IRouter;
|
|
5
|
+
graphqlConfig: GraphqlConfig;
|
|
6
|
+
serverResponseInterceptor?: Interceptors['response'];
|
|
7
|
+
}
|
|
8
|
+
export declare const createGraphQLRoutes: ({ router, graphqlConfig, serverResponseInterceptor }: CreateGraphQLRoutesParams) => IRouter;
|
|
9
|
+
export {};
|