@c-rex/core 0.0.4 → 0.0.6
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/package.json +9 -21
- package/src/__tests__/logger.test.ts +7 -0
- package/src/__tests__/requests.test.ts +91 -0
- package/src/__tests__/sdk.test.ts +82 -0
- package/src/cli.ts +189 -0
- package/src/index.ts +3 -0
- package/src/logger.ts +24 -0
- package/src/logs/__tests__/server.test.ts +97 -0
- package/src/logs/server.ts +38 -0
- package/src/requests.ts +62 -0
- package/src/sdk.ts +29 -0
- package/src/transports/__tests__/graylog.test.ts +63 -0
- package/src/transports/__tests__/matomo.test.ts +64 -0
- package/src/transports/graylog.ts +34 -0
- package/src/transports/matomo.ts +27 -0
- package/dist/chunk-QOOUMY2T.js +0 -128
- package/dist/chunk-QOOUMY2T.js.map +0 -1
- package/dist/chunk-T7GRZ5WF.mjs +0 -128
- package/dist/chunk-T7GRZ5WF.mjs.map +0 -1
- package/dist/index.d.mts +0 -34
- package/dist/index.d.ts +0 -34
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -9
- package/dist/index.mjs.map +0 -1
- package/dist/server-BUDEHO2T.js +0 -2905
- package/dist/server-BUDEHO2T.js.map +0 -1
- package/dist/server-IY4UU5DM.mjs +0 -2905
- package/dist/server-IY4UU5DM.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,36 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c-rex/core",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"module": "dist/index.mjs",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
3
|
+
"version": "0.0.6",
|
|
7
4
|
"files": [
|
|
8
|
-
"
|
|
5
|
+
"src"
|
|
9
6
|
],
|
|
10
7
|
"publishConfig": {
|
|
11
8
|
"access": "public"
|
|
12
9
|
},
|
|
13
10
|
"exports": {
|
|
14
11
|
".": {
|
|
15
|
-
"types": "./
|
|
16
|
-
"import": "./
|
|
17
|
-
"require": "./
|
|
18
|
-
"default": "./
|
|
12
|
+
"types": "./src/index.ts",
|
|
13
|
+
"import": "./src/index.ts",
|
|
14
|
+
"require": "./src/index.ts",
|
|
15
|
+
"default": "./src/index.ts"
|
|
19
16
|
},
|
|
20
|
-
"./package.json": "./package.json"
|
|
21
|
-
"./logger.server": {
|
|
22
|
-
"import": "./dist/logger.server.mjs",
|
|
23
|
-
"require": "./dist/logger.server.cjs"
|
|
24
|
-
},
|
|
25
|
-
"./logger.client": {
|
|
26
|
-
"import": "./dist/logger.client.mjs",
|
|
27
|
-
"require": "./dist/logger.client.cjs"
|
|
28
|
-
}
|
|
17
|
+
"./package.json": "./package.json"
|
|
29
18
|
},
|
|
30
|
-
"sideEffects": false,
|
|
31
19
|
"scripts": {
|
|
32
|
-
"dev": "
|
|
33
|
-
"build": "
|
|
20
|
+
"dev": "echo 'Nothing to build — using raw TypeScript files'",
|
|
21
|
+
"build": "echo 'No build needed'",
|
|
34
22
|
"test:watch": "jest --watch",
|
|
35
23
|
"test": "jest"
|
|
36
24
|
},
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { CrexApi } from '../requests';
|
|
2
|
+
import { CrexLogger } from '../logger';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
|
|
5
|
+
jest.mock('axios');
|
|
6
|
+
jest.mock('../logger');
|
|
7
|
+
|
|
8
|
+
describe('CrexApi', () => {
|
|
9
|
+
const mockBaseUrl = 'http://api.test.com';
|
|
10
|
+
const mockLogger = new CrexLogger({} as any);
|
|
11
|
+
let api: CrexApi;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
(axios.create as jest.Mock).mockReturnValue({
|
|
16
|
+
request: jest.fn()
|
|
17
|
+
});
|
|
18
|
+
api = new CrexApi(mockBaseUrl, mockLogger);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('execute', () => {
|
|
22
|
+
it('should make a successful API request', async () => {
|
|
23
|
+
const mockResponse = { data: { id: 1 }, status: 200 };
|
|
24
|
+
const mockAxiosInstance = axios.create();
|
|
25
|
+
(mockAxiosInstance.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
26
|
+
|
|
27
|
+
const result = await api.execute({
|
|
28
|
+
url: '/test',
|
|
29
|
+
method: 'GET',
|
|
30
|
+
headers: { 'X-Test': 'test' }
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(result).toEqual(mockResponse.data);
|
|
34
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith({
|
|
35
|
+
url: '/test',
|
|
36
|
+
method: 'GET',
|
|
37
|
+
headers: { 'X-Test': 'test' },
|
|
38
|
+
data: undefined,
|
|
39
|
+
params: undefined
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle request with body and params', async () => {
|
|
44
|
+
const mockResponse = { data: { success: true }, status: 200 };
|
|
45
|
+
const mockAxiosInstance = axios.create();
|
|
46
|
+
(mockAxiosInstance.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
47
|
+
|
|
48
|
+
await api.execute({
|
|
49
|
+
url: '/test',
|
|
50
|
+
method: 'POST',
|
|
51
|
+
body: { name: 'test' },
|
|
52
|
+
params: { id: 1 }
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith({
|
|
56
|
+
url: '/test',
|
|
57
|
+
method: 'POST',
|
|
58
|
+
data: { name: 'test' },
|
|
59
|
+
params: { id: 1 },
|
|
60
|
+
headers: undefined
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should retry failed requests', async () => {
|
|
65
|
+
const mockError = new Error('Network error');
|
|
66
|
+
const mockAxiosInstance = axios.create();
|
|
67
|
+
(mockAxiosInstance.request as jest.Mock).mockRejectedValue(mockError);
|
|
68
|
+
|
|
69
|
+
await expect(api.execute({
|
|
70
|
+
url: '/test',
|
|
71
|
+
method: 'GET'
|
|
72
|
+
})).rejects.toThrow('Network error');
|
|
73
|
+
|
|
74
|
+
expect(mockLogger.log).toHaveBeenCalledWith(
|
|
75
|
+
'error',
|
|
76
|
+
expect.stringContaining('Network error')
|
|
77
|
+
);
|
|
78
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledTimes(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should throw error when no valid response after retries', async () => {
|
|
82
|
+
const mockAxiosInstance = axios.create();
|
|
83
|
+
(mockAxiosInstance.request as jest.Mock).mockResolvedValue(undefined);
|
|
84
|
+
|
|
85
|
+
await expect(api.execute({
|
|
86
|
+
url: '/test',
|
|
87
|
+
method: 'GET'
|
|
88
|
+
})).rejects.toThrow('Failed to retrieve a valid response');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { CrexSDK } from '../sdk';
|
|
2
|
+
import { CrexLogger } from '../logger';
|
|
3
|
+
import { CrexApi } from '../requests';
|
|
4
|
+
import { ConfigInterface } from '@c-rex/interfaces';
|
|
5
|
+
|
|
6
|
+
jest.mock('../logger');
|
|
7
|
+
jest.mock('../requests');
|
|
8
|
+
|
|
9
|
+
describe('CrexSDK', () => {
|
|
10
|
+
const mockConfig: ConfigInterface = {
|
|
11
|
+
projectName: 'test-project',
|
|
12
|
+
baseUrl: 'http://test.com',
|
|
13
|
+
search: {
|
|
14
|
+
fields: [],
|
|
15
|
+
tags: [],
|
|
16
|
+
restrict: [],
|
|
17
|
+
filter: [],
|
|
18
|
+
sparqlWhere: ''
|
|
19
|
+
},
|
|
20
|
+
logs: {
|
|
21
|
+
graylog: {
|
|
22
|
+
silent: true,
|
|
23
|
+
url: 'http://graylog.test',
|
|
24
|
+
app: 'test-app',
|
|
25
|
+
minimumLevel: 'info',
|
|
26
|
+
categoriesLevel: ['Document']
|
|
27
|
+
},
|
|
28
|
+
matomo: {
|
|
29
|
+
silent: false,
|
|
30
|
+
url: '',
|
|
31
|
+
app: '',
|
|
32
|
+
minimumLevel: 'info',
|
|
33
|
+
categoriesLevel: []
|
|
34
|
+
},
|
|
35
|
+
console: {
|
|
36
|
+
silent: false,
|
|
37
|
+
url: '',
|
|
38
|
+
app: '',
|
|
39
|
+
minimumLevel: 'info',
|
|
40
|
+
categoriesLevel: []
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
// Reset the singleton instance
|
|
48
|
+
(CrexSDK as any).instance = undefined;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('constructor', () => {
|
|
52
|
+
it('should create a new instance with correct configuration', () => {
|
|
53
|
+
const sdk = new CrexSDK(mockConfig);
|
|
54
|
+
|
|
55
|
+
expect(sdk.customerConfig).toBe(mockConfig);
|
|
56
|
+
expect(sdk.logger).toBeInstanceOf(CrexLogger);
|
|
57
|
+
expect(sdk.api).toBeInstanceOf(CrexApi);
|
|
58
|
+
expect(CrexLogger).toHaveBeenCalledWith(mockConfig);
|
|
59
|
+
expect(CrexApi).toHaveBeenCalledWith(mockConfig.baseUrl, expect.any(CrexLogger));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should return existing instance if already initialized', () => {
|
|
63
|
+
const firstInstance = new CrexSDK(mockConfig);
|
|
64
|
+
const secondInstance = new CrexSDK(mockConfig);
|
|
65
|
+
|
|
66
|
+
expect(secondInstance).toBe(firstInstance);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('getInstance', () => {
|
|
71
|
+
it('should return existing instance', () => {
|
|
72
|
+
const sdk = new CrexSDK(mockConfig);
|
|
73
|
+
const instance = CrexSDK.getInstance();
|
|
74
|
+
|
|
75
|
+
expect(instance).toBe(sdk);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should throw error if instance not initialized', () => {
|
|
79
|
+
expect(() => CrexSDK.getInstance()).toThrow('SDK not initialized');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/*
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { checkbox, confirm, input, select } from '@inquirer/prompts';
|
|
4
|
+
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { FILTER_OPTIONS, LOG_CATEGORIES, LOG_LEVELS } from '../constants/log';
|
|
8
|
+
import { RESULT_VIEW_OPTIONS } from '../constants/components';
|
|
9
|
+
|
|
10
|
+
type Restriction = {
|
|
11
|
+
key: string;
|
|
12
|
+
value: string;
|
|
13
|
+
operator: string;
|
|
14
|
+
}
|
|
15
|
+
const addRestriction = async (word: string): Promise<Restriction[]> => {
|
|
16
|
+
const restrictions: any = []
|
|
17
|
+
|
|
18
|
+
let aux = await confirm({
|
|
19
|
+
message: `Do you want to add ${word}s to your search?`,
|
|
20
|
+
default: false,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
while (aux) {
|
|
24
|
+
const restriction: Restriction = {
|
|
25
|
+
key: "",
|
|
26
|
+
value: '${value}',
|
|
27
|
+
operator: ""
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
restriction.key = await input({
|
|
31
|
+
message: `Type the ${word} key:`,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
restriction.operator = await select({
|
|
35
|
+
message: `Type the ${word} operator:`,
|
|
36
|
+
choices: FILTER_OPTIONS.map(key => ({ value: key })),
|
|
37
|
+
default: FILTER_OPTIONS[0]
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const byValue = await confirm({
|
|
41
|
+
message: `Do you want apply your ${word} using a fixed value?`,
|
|
42
|
+
default: false
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (byValue) {
|
|
46
|
+
restriction.value = await input({
|
|
47
|
+
message: `Type the ${word} value:`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
restrictions.push(restriction);
|
|
52
|
+
|
|
53
|
+
aux = await confirm({
|
|
54
|
+
message: `Do you want to add more ${word}s?`,
|
|
55
|
+
default: false
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return restrictions;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const log = async (name: string): Promise<any> => {
|
|
63
|
+
const log = {
|
|
64
|
+
silent: false,
|
|
65
|
+
logLevel: [],
|
|
66
|
+
categoriesLevel: [],
|
|
67
|
+
url: "",
|
|
68
|
+
app: "",
|
|
69
|
+
}
|
|
70
|
+
log.silent = await confirm({
|
|
71
|
+
message: `Do you want to silent ${name}?`,
|
|
72
|
+
default: false
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!log.silent) {
|
|
76
|
+
log.url = await input({
|
|
77
|
+
message: `Type the ${name} URL:`,
|
|
78
|
+
});
|
|
79
|
+
log.app = await input({
|
|
80
|
+
message: `Type the ${name} app name:`,
|
|
81
|
+
});
|
|
82
|
+
const logLevel = await checkbox({
|
|
83
|
+
message: `Select the log level to ${name}:`,
|
|
84
|
+
choices: Object.keys(LOG_LEVELS).map(key => {
|
|
85
|
+
return {
|
|
86
|
+
value: key,
|
|
87
|
+
checked: true,
|
|
88
|
+
};
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
const categoriesLevel = await checkbox({
|
|
92
|
+
message: `Select the categories level to ${name}:`,
|
|
93
|
+
choices: LOG_CATEGORIES.map(key => {
|
|
94
|
+
return {
|
|
95
|
+
value: key,
|
|
96
|
+
checked: true,
|
|
97
|
+
};
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
log.logLevel = logLevel as any;
|
|
102
|
+
log.categoriesLevel = categoriesLevel as any;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return log;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main() {
|
|
109
|
+
try {
|
|
110
|
+
const projectName = await input({
|
|
111
|
+
message: 'Set the project name:',
|
|
112
|
+
default: "c-rex.net"
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const baseUrl = await input({
|
|
116
|
+
message: 'Set the base URL:',
|
|
117
|
+
default: "https://c-rex.net/ids/api/iirds/v1/"
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const resultViewStyle = await select({
|
|
121
|
+
message: 'Results should be shown as:',
|
|
122
|
+
choices: Object.keys(RESULT_VIEW_OPTIONS).map(key => {
|
|
123
|
+
return { value: key };
|
|
124
|
+
}),
|
|
125
|
+
default: RESULT_VIEW_OPTIONS.table
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const searchFields = await input({
|
|
129
|
+
message: 'Type the fields used in search separated by comma:',
|
|
130
|
+
default: "titles,labels,languages"
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const searchTags = await input({
|
|
134
|
+
message: 'Type the tags used in search separated by comma:',
|
|
135
|
+
default: ""
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const restrictions = await addRestriction("restriction");
|
|
139
|
+
const filters = await addRestriction("filter");
|
|
140
|
+
|
|
141
|
+
const sparqlWhere = await input({
|
|
142
|
+
message: 'Type the sparqlWhere string:',
|
|
143
|
+
default: ""
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const graylog = await log("Graylog");
|
|
147
|
+
const matomo = await log("Matomo");
|
|
148
|
+
|
|
149
|
+
const config = {
|
|
150
|
+
projectName: projectName,
|
|
151
|
+
baseUrl: baseUrl,
|
|
152
|
+
search: {
|
|
153
|
+
fields: searchFields.split(','),
|
|
154
|
+
tags: searchTags.split(','),
|
|
155
|
+
restrict: restrictions,
|
|
156
|
+
filter: filters,
|
|
157
|
+
sparqlWhere: sparqlWhere
|
|
158
|
+
},
|
|
159
|
+
resultViewStyle: resultViewStyle,
|
|
160
|
+
logs: {
|
|
161
|
+
graylog: graylog,
|
|
162
|
+
matomo: matomo
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const configFilePath = path.join(process.cwd(), 'src', 'config', 'customerConfig.ts');
|
|
167
|
+
|
|
168
|
+
const configFileContent = `
|
|
169
|
+
import { ConfigInterface } from "@/interfaces/config";
|
|
170
|
+
export const CUSTOMER_CONFIG: ConfigInterface = ${JSON.stringify(config, null, 4)};
|
|
171
|
+
`;
|
|
172
|
+
|
|
173
|
+
fs.writeFileSync(configFilePath, configFileContent);
|
|
174
|
+
|
|
175
|
+
console.log('Installing dependencies...');
|
|
176
|
+
execSync('npm install', { stdio: 'inherit' });
|
|
177
|
+
|
|
178
|
+
console.log('Configuration completed successfully!');
|
|
179
|
+
} catch (error: any) {
|
|
180
|
+
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
181
|
+
console.log('👋 until next time!');
|
|
182
|
+
} else {
|
|
183
|
+
console.error('Error during configuration:', error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
main();
|
|
189
|
+
*/
|
package/src/index.ts
ADDED
package/src/logger.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ConfigInterface } from "@c-rex/interfaces";
|
|
2
|
+
import { LogCategoriesType, LogLevelType } from "@c-rex/types";
|
|
3
|
+
|
|
4
|
+
export class CrexLogger {
|
|
5
|
+
private customerConfig: ConfigInterface;
|
|
6
|
+
|
|
7
|
+
constructor(config: ConfigInterface) {
|
|
8
|
+
this.customerConfig = config;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async log(level: LogLevelType, message: string, category?: LogCategoriesType) {
|
|
12
|
+
if (typeof window === "undefined") {
|
|
13
|
+
const { LoggerServer } = await import("./logs/server");
|
|
14
|
+
const serverLog = new LoggerServer(this.customerConfig);
|
|
15
|
+
serverLog.log(level, message, category);
|
|
16
|
+
} else {
|
|
17
|
+
fetch("/api/log", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "Content-Type": "application/json" },
|
|
20
|
+
body: JSON.stringify({ level, message, category }),
|
|
21
|
+
}).catch((err) => console.error("Erro ao enviar log", err));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import { LogLevelType, LogCategoriesType } from '@c-rex/types';
|
|
3
|
+
import { ConfigInterface } from '@c-rex/interfaces';
|
|
4
|
+
import { LoggerServer } from '../server';
|
|
5
|
+
import { MatomoTransport } from '../../transports/matomo';
|
|
6
|
+
import { GraylogTransport } from '../../transports/graylog';
|
|
7
|
+
|
|
8
|
+
jest.mock('winston', () => {
|
|
9
|
+
const actual = jest.requireActual('winston');
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
createLogger: jest.fn(),
|
|
13
|
+
transports: {
|
|
14
|
+
Console: jest.fn().mockImplementation(() => ({
|
|
15
|
+
name: 'ConsoleTransport',
|
|
16
|
+
})),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
jest.mock('../../transports/matomo', () => ({
|
|
22
|
+
MatomoTransport: jest.fn().mockImplementation(() => ({
|
|
23
|
+
name: 'MatomoTransport',
|
|
24
|
+
})),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock('../../transports/graylog', () => ({
|
|
28
|
+
GraylogTransport: jest.fn().mockImplementation(() => ({
|
|
29
|
+
name: 'GraylogTransport',
|
|
30
|
+
})),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
describe('LoggerServer', () => {
|
|
34
|
+
const mockConfig: ConfigInterface = {
|
|
35
|
+
logs: {
|
|
36
|
+
console: {
|
|
37
|
+
minimumLevel: 'info',
|
|
38
|
+
silent: false,
|
|
39
|
+
},
|
|
40
|
+
graylog: {
|
|
41
|
+
minimumLevel: 'error',
|
|
42
|
+
silent: true,
|
|
43
|
+
},
|
|
44
|
+
matomo: {
|
|
45
|
+
categoriesLevel: [],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
customerConfig: {
|
|
49
|
+
logs: {
|
|
50
|
+
matomo: {
|
|
51
|
+
categoriesLevel: [],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
} as unknown as ConfigInterface;
|
|
56
|
+
|
|
57
|
+
const mockLogger = {
|
|
58
|
+
log: jest.fn(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
(winston.createLogger as jest.Mock).mockReturnValue(mockLogger);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
jest.clearAllMocks();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('deve criar o logger com os transports corretos', () => {
|
|
70
|
+
new LoggerServer(mockConfig);
|
|
71
|
+
|
|
72
|
+
expect(winston.transports.Console).toHaveBeenCalledWith({
|
|
73
|
+
level: 'info',
|
|
74
|
+
silent: false,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(MatomoTransport).toHaveBeenCalledWith({
|
|
78
|
+
level: 'info',
|
|
79
|
+
silent: false,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(GraylogTransport).toHaveBeenCalledWith({
|
|
83
|
+
level: 'error',
|
|
84
|
+
silent: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(winston.createLogger).toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('deve chamar logger.log com os parâmetros corretos', () => {
|
|
91
|
+
const loggerServer = new LoggerServer(mockConfig);
|
|
92
|
+
|
|
93
|
+
loggerServer.log('info' as LogLevelType, 'mensagem de teste', 'analytics' as LogCategoriesType);
|
|
94
|
+
|
|
95
|
+
expect(mockLogger.log).toHaveBeenCalledWith('info', 'mensagem de teste', 'analytics');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import winston from "winston"
|
|
2
|
+
import { LOG_LEVELS } from "@c-rex/constants";
|
|
3
|
+
import { MatomoTransport } from "../transports/matomo"
|
|
4
|
+
import { GraylogTransport } from "../transports/graylog"
|
|
5
|
+
import { LogLevelType, LogCategoriesType } from "@c-rex/types";
|
|
6
|
+
import { ConfigInterface } from "@c-rex/interfaces";
|
|
7
|
+
|
|
8
|
+
const logger = (config: ConfigInterface) => {
|
|
9
|
+
return winston.createLogger({
|
|
10
|
+
levels: LOG_LEVELS,
|
|
11
|
+
transports: [
|
|
12
|
+
new winston.transports.Console({
|
|
13
|
+
level: config.logs.console.minimumLevel as string,
|
|
14
|
+
silent: config.logs.console.silent,
|
|
15
|
+
}),
|
|
16
|
+
new MatomoTransport({
|
|
17
|
+
level: config.logs.console.minimumLevel,
|
|
18
|
+
silent: config.logs.console.silent,
|
|
19
|
+
}),
|
|
20
|
+
new GraylogTransport({
|
|
21
|
+
level: config.logs.graylog.minimumLevel,
|
|
22
|
+
silent: config.logs.graylog.silent,
|
|
23
|
+
}),
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class LoggerServer {
|
|
29
|
+
logger: winston.Logger;
|
|
30
|
+
|
|
31
|
+
constructor(config: ConfigInterface) {
|
|
32
|
+
this.logger = logger(config);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
log(level: LogLevelType, message: string, category?: LogCategoriesType) {
|
|
36
|
+
this.logger.log(level, message, category);
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/requests.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import axios, { AxiosResponse, Method, AxiosInstance } from "axios";
|
|
2
|
+
import { API } from "@c-rex/constants";
|
|
3
|
+
import { CrexLogger } from "./logger";
|
|
4
|
+
|
|
5
|
+
interface APIGenericResponse<T> extends AxiosResponse {
|
|
6
|
+
data: T;
|
|
7
|
+
statusCode: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface CallParams {
|
|
11
|
+
url: string;
|
|
12
|
+
method: Method;
|
|
13
|
+
body?: any;
|
|
14
|
+
headers?: any;
|
|
15
|
+
params?: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class CrexApi {
|
|
19
|
+
private apiClient: AxiosInstance;
|
|
20
|
+
private logger: CrexLogger;
|
|
21
|
+
|
|
22
|
+
public constructor(baseUrl: string, logger: CrexLogger) {
|
|
23
|
+
this.apiClient = axios.create({
|
|
24
|
+
baseURL: baseUrl,
|
|
25
|
+
headers: {
|
|
26
|
+
"content-Type": "application/json",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async execute<T>({
|
|
33
|
+
url,
|
|
34
|
+
method,
|
|
35
|
+
params,
|
|
36
|
+
body,
|
|
37
|
+
headers,
|
|
38
|
+
}: CallParams): Promise<any> {
|
|
39
|
+
let response: APIGenericResponse<T> | undefined = undefined;
|
|
40
|
+
|
|
41
|
+
for (let retry = 0; retry < API.MAX_RETRY; retry++) {
|
|
42
|
+
try {
|
|
43
|
+
response = await this.apiClient.request({
|
|
44
|
+
url,
|
|
45
|
+
method,
|
|
46
|
+
data: body,
|
|
47
|
+
params,
|
|
48
|
+
headers,
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
this.logger.log("error", `API.execute error when request ${url}. Error: ${error}`);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (response) {
|
|
57
|
+
return response.data
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw new Error("API.execute error: Failed to retrieve a valid response");
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CrexApi } from "./requests";
|
|
2
|
+
import { CrexLogger } from "./logger";
|
|
3
|
+
import { ConfigInterface } from "@c-rex/interfaces";
|
|
4
|
+
|
|
5
|
+
export class CrexSDK {
|
|
6
|
+
private static instance: CrexSDK;
|
|
7
|
+
public customerConfig!: ConfigInterface;
|
|
8
|
+
public logger!: CrexLogger;
|
|
9
|
+
public api!: CrexApi;
|
|
10
|
+
|
|
11
|
+
public constructor(config: ConfigInterface) {
|
|
12
|
+
if (CrexSDK.instance) {
|
|
13
|
+
return CrexSDK.instance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.customerConfig = config;
|
|
17
|
+
this.logger = new CrexLogger(this.customerConfig);
|
|
18
|
+
this.api = new CrexApi(this.customerConfig.baseUrl, this.logger);
|
|
19
|
+
|
|
20
|
+
CrexSDK.instance = this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public static getInstance(): CrexSDK {
|
|
24
|
+
if (!CrexSDK.instance) {
|
|
25
|
+
throw new Error("SDK not initialized");
|
|
26
|
+
}
|
|
27
|
+
return CrexSDK.instance;
|
|
28
|
+
}
|
|
29
|
+
}
|