@adobe/aio-cli-plugin-api-mesh 3.7.0 → 3.8.0-alpha.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 +3 -3
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
- package/src/commands/__fixtures__/files/requestParams.json +2 -2
- package/src/commands/__fixtures__/openapi-schema.json +3 -3
- package/src/commands/__fixtures__/requestParams.json +2 -2
- package/src/commands/__fixtures__/sample_fully_qualified_mesh.json +28 -28
- package/src/commands/__fixtures__/sample_mesh_files.json +22 -22
- package/src/commands/__fixtures__/sample_mesh_invalid_file_content.json +13 -13
- package/src/commands/__fixtures__/sample_mesh_invalid_file_name.json +24 -26
- package/src/commands/__fixtures__/sample_mesh_invalid_paths.json +22 -22
- package/src/commands/__fixtures__/sample_mesh_invalid_type.json +24 -26
- package/src/commands/__fixtures__/sample_mesh_mismatching_path.json +28 -28
- package/src/commands/__fixtures__/sample_mesh_outside_workspace_dir.json +22 -22
- package/src/commands/__fixtures__/sample_mesh_path_from_home.json +13 -13
- package/src/commands/__fixtures__/sample_mesh_subdirectory.json +22 -22
- package/src/commands/__fixtures__/sample_mesh_with_files_array.json +28 -28
- package/src/commands/__fixtures__/sample_secrets_mesh.json +1 -1
- package/src/commands/api-mesh/__tests__/log-get-bulk.test.js +253 -0
- package/src/commands/api-mesh/__tests__/log-get.test.js +234 -0
- package/src/commands/api-mesh/__tests__/log-list.test.js +162 -0
- package/src/commands/api-mesh/log-get-bulk.js +243 -0
- package/src/commands/api-mesh/log-get.js +83 -0
- package/src/commands/api-mesh/log-list.js +105 -0
- package/src/lib/devConsole.js +139 -0
- package/src/utils.js +70 -0
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
2
|
+
"meshConfig": {
|
|
3
|
+
"sources": [
|
|
4
|
+
{
|
|
5
|
+
"name": "<json_source_name>",
|
|
6
|
+
"handler": {
|
|
7
|
+
"JsonSchema": {
|
|
8
|
+
"baseUrl": "<json_source__baseurl>",
|
|
9
|
+
"operations": [
|
|
10
|
+
{
|
|
11
|
+
"type": "Query",
|
|
12
|
+
"field": "<query>",
|
|
13
|
+
"path": "<query_path>",
|
|
14
|
+
"method": "POST",
|
|
15
|
+
"requestSchema": "./requestParams.json"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
{
|
|
24
|
+
"path": "./requestParams.json",
|
|
25
|
+
"content": "{\"type\":\"dummyContent\"}"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const GetBulkLogCommand = require('../log-get-bulk');
|
|
4
|
+
const { initRequestId, initSdk, promptConfirm } = require('../../../helpers');
|
|
5
|
+
const { getMeshId, getPresignedUrls } = require('../../../lib/devConsole');
|
|
6
|
+
const { suggestCorrectedDateFormat } = require('../../../utils');
|
|
7
|
+
|
|
8
|
+
jest.mock('fs');
|
|
9
|
+
jest.mock('axios');
|
|
10
|
+
jest.mock('../../../helpers', () => ({
|
|
11
|
+
initSdk: jest.fn().mockResolvedValue({}),
|
|
12
|
+
initRequestId: jest.fn().mockResolvedValue({}),
|
|
13
|
+
promptConfirm: jest.fn().mockResolvedValue(true),
|
|
14
|
+
}));
|
|
15
|
+
jest.mock('../../../lib/devConsole');
|
|
16
|
+
jest.mock('../../../classes/logger');
|
|
17
|
+
|
|
18
|
+
describe('GetBulkLogCommand', () => {
|
|
19
|
+
let parseSpy;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
// Setup spies and mock functions
|
|
23
|
+
parseSpy = jest.spyOn(GetBulkLogCommand.prototype, 'parse').mockResolvedValue({
|
|
24
|
+
flags: {
|
|
25
|
+
startTime: '2024-08-29T12:00:00Z',
|
|
26
|
+
endTime: '2024-08-29T12:30:00Z',
|
|
27
|
+
filename: 'test.csv',
|
|
28
|
+
ignoreCache: false,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// initRequestId.mockResolvedValue();
|
|
33
|
+
initSdk.mockResolvedValue({
|
|
34
|
+
imsOrgId: 'orgId',
|
|
35
|
+
imsOrgCode: 'orgCode',
|
|
36
|
+
projectId: 'projectId',
|
|
37
|
+
workspaceId: 'workspaceId',
|
|
38
|
+
workspaceName: 'workspaceName',
|
|
39
|
+
});
|
|
40
|
+
getMeshId.mockResolvedValue('meshId');
|
|
41
|
+
getPresignedUrls.mockResolvedValue({
|
|
42
|
+
presignedUrls: [{ key: 'log1.csv', url: 'http://example.com/someHash' }],
|
|
43
|
+
totalSize: 2048,
|
|
44
|
+
});
|
|
45
|
+
promptConfirm.mockResolvedValue(true);
|
|
46
|
+
global.requestId = 'dummy_request_id';
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
jest.clearAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('throws an error if the time difference between startTime and endTime is greater than 30 minutes', async () => {
|
|
54
|
+
// Mock the file system checks even if they are not the focus of this test
|
|
55
|
+
fs.existsSync.mockReturnValue(true); // Assume the file exists
|
|
56
|
+
fs.statSync.mockReturnValue({ size: 0 }); // Assume the file is empty
|
|
57
|
+
|
|
58
|
+
// Get the current date and time
|
|
59
|
+
const now = new Date();
|
|
60
|
+
|
|
61
|
+
// Create dynamic startTime and endTime
|
|
62
|
+
const startTime = new Date(now);
|
|
63
|
+
const endTime = new Date(now);
|
|
64
|
+
endTime.setMinutes(startTime.getMinutes() + 45); // Set endTime to 45 minutes after startTime
|
|
65
|
+
|
|
66
|
+
const formattedStartTime = startTime.toISOString().slice(0, 19) + 'Z';
|
|
67
|
+
const formattedEndTime = endTime.toISOString().slice(0, 19) + 'Z';
|
|
68
|
+
|
|
69
|
+
parseSpy.mockResolvedValueOnce({
|
|
70
|
+
flags: {
|
|
71
|
+
startTime: formattedStartTime,
|
|
72
|
+
endTime: formattedEndTime,
|
|
73
|
+
filename: 'test.csv',
|
|
74
|
+
ignoreCache: false,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const command = new GetBulkLogCommand([], {});
|
|
79
|
+
await expect(command.run()).rejects.toThrow(
|
|
80
|
+
'Max duration between startTime and endTime should be 30 minutes. Current duration is 0 hours 45 minutes and 0 seconds.',
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('throws an error if startTime format is invalid', async () => {
|
|
85
|
+
parseSpy.mockResolvedValueOnce({
|
|
86
|
+
flags: {
|
|
87
|
+
// startTime: '20240809123456',
|
|
88
|
+
startTime: '20241213223832',
|
|
89
|
+
endTime: '2024-08-29T12:30:00Z',
|
|
90
|
+
filename: 'test.csv',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const command = new GetBulkLogCommand([], {});
|
|
95
|
+
|
|
96
|
+
// Assuming your suggestCorrectedDateFormat function corrects the format to "2024-08-09T09:08:33Z"
|
|
97
|
+
const correctedStartTime = '2024-12-13T22:38:32Z'; // Use an appropriate correction - 2024-08-09T12:34:56Z
|
|
98
|
+
|
|
99
|
+
await expect(command.run()).rejects.toThrow(
|
|
100
|
+
`Use the format YYYY-MM-DDTHH:MM:SSZ for startTime. Did you mean ${correctedStartTime}?`,
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('throws an error if endTime format is invalid', async () => {
|
|
105
|
+
parseSpy.mockResolvedValueOnce({
|
|
106
|
+
flags: {
|
|
107
|
+
startTime: '2024-08-29T12:00:00Z',
|
|
108
|
+
endTime: '2024-08-29:23:45:56Z',
|
|
109
|
+
filename: 'test.csv',
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const command = new GetBulkLogCommand([], {});
|
|
114
|
+
|
|
115
|
+
// Assuming your suggestCorrectedDateFormat function corrects the format to "2024-08-09T09:08:33Z"
|
|
116
|
+
const correctedStartTime = '2024-08-29T23:45:56Z'; // Use an appropriate correction
|
|
117
|
+
await expect(command.run()).rejects.toThrow(
|
|
118
|
+
`Use the format YYYY-MM-DDTHH:MM:SSZ for endTime. Did you mean ${correctedStartTime}?`,
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Test for totalSize being 0
|
|
123
|
+
test('throws an error if totalSize is 0', async () => {
|
|
124
|
+
// Mock the file system checks even if they are not the focus of this test
|
|
125
|
+
fs.existsSync.mockReturnValue(true); // Assume the file exists
|
|
126
|
+
fs.statSync.mockReturnValue({ size: 0 }); // Assume the file is empty
|
|
127
|
+
// Mock getPresignedUrls to return totalSize as 0
|
|
128
|
+
getPresignedUrls.mockResolvedValueOnce({
|
|
129
|
+
presignedUrls: [{ key: 'log1', url: 'http://example.com/log1' }],
|
|
130
|
+
totalSize: 0, // totalSize is 0
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const command = new GetBulkLogCommand([], {});
|
|
134
|
+
await expect(command.run()).rejects.toThrow('No logs available to download');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('throws an error if logs are requested for a date older than 30 days', async () => {
|
|
138
|
+
const today = new Date();
|
|
139
|
+
const thirtyDaysAgo = new Date(today);
|
|
140
|
+
thirtyDaysAgo.setUTCDate(today.getUTCDate() - 30);
|
|
141
|
+
|
|
142
|
+
const startTime = new Date(thirtyDaysAgo);
|
|
143
|
+
startTime.setUTCDate(thirtyDaysAgo.getUTCDate() - 1);
|
|
144
|
+
const formattedStartTime = startTime.toISOString().slice(0, 19) + 'Z';
|
|
145
|
+
|
|
146
|
+
parseSpy.mockResolvedValueOnce({
|
|
147
|
+
flags: {
|
|
148
|
+
startTime: formattedStartTime,
|
|
149
|
+
endTime: '2024-08-30T12:30:00Z',
|
|
150
|
+
filename: 'test.csv',
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const command = new GetBulkLogCommand([], {});
|
|
155
|
+
await expect(command.run()).rejects.toThrow(
|
|
156
|
+
'Cannot get logs more than 30 days old. Adjust your time range.',
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Test for file creation and emptiness check
|
|
161
|
+
test('creates file if it does not exist and checks if file is empty before proceeding', async () => {
|
|
162
|
+
fs.existsSync.mockReturnValue(false); // Mock file does not exist
|
|
163
|
+
fs.statSync.mockReturnValue({ size: 0 }); // Mock file is empty
|
|
164
|
+
|
|
165
|
+
const mockWriteStream = {
|
|
166
|
+
write: jest.fn(),
|
|
167
|
+
end: jest.fn(),
|
|
168
|
+
on: jest.fn((event, callback) => {
|
|
169
|
+
if (event === 'finish') {
|
|
170
|
+
callback();
|
|
171
|
+
}
|
|
172
|
+
}),
|
|
173
|
+
};
|
|
174
|
+
fs.createWriteStream.mockReturnValue(mockWriteStream);
|
|
175
|
+
|
|
176
|
+
const command = new GetBulkLogCommand([], {});
|
|
177
|
+
await command.run();
|
|
178
|
+
|
|
179
|
+
expect(fs.existsSync).toHaveBeenCalledWith(path.resolve(process.cwd(), 'test.csv'));
|
|
180
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), 'test.csv'), ''); // Ensures file is created if not exists
|
|
181
|
+
expect(mockWriteStream.write).toHaveBeenCalled(); // Writes content to file
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('throws an error if the file is not empty', async () => {
|
|
185
|
+
fs.existsSync.mockReturnValue(true);
|
|
186
|
+
fs.statSync.mockReturnValue({ size: 1024 });
|
|
187
|
+
|
|
188
|
+
const command = new GetBulkLogCommand([], {});
|
|
189
|
+
await expect(command.run()).rejects.toThrow('Make sure the file: test.csv is empty');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('downloads logs if all conditions are met', async () => {
|
|
193
|
+
fs.existsSync.mockReturnValue(true);
|
|
194
|
+
fs.statSync.mockReturnValue({ size: 0 });
|
|
195
|
+
|
|
196
|
+
const mockWriteStream = {
|
|
197
|
+
write: jest.fn(),
|
|
198
|
+
end: jest.fn(),
|
|
199
|
+
on: jest.fn((event, callback) => {
|
|
200
|
+
if (event === 'finish') {
|
|
201
|
+
callback();
|
|
202
|
+
}
|
|
203
|
+
}),
|
|
204
|
+
};
|
|
205
|
+
fs.createWriteStream.mockReturnValue(mockWriteStream);
|
|
206
|
+
|
|
207
|
+
const command = new GetBulkLogCommand([], {});
|
|
208
|
+
await command.run();
|
|
209
|
+
|
|
210
|
+
expect(initRequestId).toHaveBeenCalled();
|
|
211
|
+
expect(initSdk).toHaveBeenCalled();
|
|
212
|
+
expect(getMeshId).toHaveBeenCalledWith('orgId', 'projectId', 'workspaceId', 'workspaceName');
|
|
213
|
+
expect(getPresignedUrls).toHaveBeenCalledWith(
|
|
214
|
+
'orgCode',
|
|
215
|
+
'projectId',
|
|
216
|
+
'workspaceId',
|
|
217
|
+
'meshId',
|
|
218
|
+
expect.any(String),
|
|
219
|
+
expect.any(String),
|
|
220
|
+
);
|
|
221
|
+
expect(fs.createWriteStream).toHaveBeenCalledWith(path.resolve(process.cwd(), 'test.csv'), {
|
|
222
|
+
flags: 'a',
|
|
223
|
+
});
|
|
224
|
+
expect(mockWriteStream.write).toHaveBeenCalled();
|
|
225
|
+
expect(mockWriteStream.end).toHaveBeenCalled();
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
describe('GetBulkLogCommand startTime and endTime validation', () => {
|
|
229
|
+
// Define the test cases as an array of [inputDate, expectedOutput] pairs
|
|
230
|
+
const testCases = [
|
|
231
|
+
// Invalid formats that should be corrected
|
|
232
|
+
['2024-0812T123445', '2024-08-12T12:34:45Z'],
|
|
233
|
+
['2024:08:12-09-08-36Z', '2024-08-12T09:08:36Z'],
|
|
234
|
+
['20241223T234556Z', '2024-12-23T23:45:56Z'],
|
|
235
|
+
['20241213223832', '2024-12-13T22:38:32Z'],
|
|
236
|
+
['2024-12-13T223832Z', '2024-12-13T22:38:32Z'],
|
|
237
|
+
['2024-11-23T21:34:45', '2024-11-23T21:34:45Z'],
|
|
238
|
+
|
|
239
|
+
// Invalid date components that should not be corrected, but return null which lets the user know the date is invalid
|
|
240
|
+
['20240834123456Z', null], // Invalid day (34)
|
|
241
|
+
['2024-12-34T23:34:45Z', null], // Invalid day (34)
|
|
242
|
+
['2024-13-23:21:34:45', null], // Invalid month (13)
|
|
243
|
+
['2024-11-63T21:34:45', null], // Invalid day (13) and missing Z
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
test.each(testCases)(
|
|
247
|
+
'suggestCorrectedDateFormat("%s") should return "%s"',
|
|
248
|
+
(inputDate, expectedOutput) => {
|
|
249
|
+
const correctedDate = suggestCorrectedDateFormat(inputDate);
|
|
250
|
+
expect(correctedDate).toBe(expectedOutput);
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
});
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2021 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
jest.mock('axios');
|
|
14
|
+
jest.mock('@adobe/aio-lib-env');
|
|
15
|
+
jest.mock('@adobe/aio-cli-lib-console');
|
|
16
|
+
jest.mock('fs/promises');
|
|
17
|
+
jest.mock('@adobe/aio-cli-lib-console', () => ({
|
|
18
|
+
init: jest.fn().mockResolvedValue(mockConsoleCLIInstance),
|
|
19
|
+
cleanStdOut: jest.fn(),
|
|
20
|
+
}));
|
|
21
|
+
jest.mock('@adobe/aio-lib-ims');
|
|
22
|
+
jest.mock('../../../helpers', () => ({
|
|
23
|
+
initSdk: jest.fn().mockResolvedValue({}),
|
|
24
|
+
initRequestId: jest.fn().mockResolvedValue({}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock('../../../lib/devConsole');
|
|
28
|
+
|
|
29
|
+
const mockConsoleCLIInstance = {};
|
|
30
|
+
const selectedOrg = { id: '1234', code: 'CODE1234@AdobeOrg', name: 'ORG01', type: 'entp' };
|
|
31
|
+
const selectedProject = { id: '5678', title: 'Project01' };
|
|
32
|
+
const selectedWorkspace = { id: '123456789', title: 'Workspace01' };
|
|
33
|
+
|
|
34
|
+
const { writeFile } = require('fs/promises');
|
|
35
|
+
const { initSdk } = require('../../../helpers');
|
|
36
|
+
const FetchLogsCommand = require('../log-get');
|
|
37
|
+
const { getLogsByRayId, getMeshId } = require('../../../lib/devConsole');
|
|
38
|
+
const os = require('os');
|
|
39
|
+
let logSpy = null;
|
|
40
|
+
let errorLogSpy = null;
|
|
41
|
+
let parseSpy = null;
|
|
42
|
+
let platformSpy = null;
|
|
43
|
+
|
|
44
|
+
const mockIgnoreCacheFlag = Promise.resolve(true);
|
|
45
|
+
|
|
46
|
+
describe('FetchLogsCommand tests', () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
initSdk.mockResolvedValue({
|
|
49
|
+
imsOrgId: selectedOrg.id,
|
|
50
|
+
projectId: selectedProject.id,
|
|
51
|
+
workspaceId: selectedWorkspace.id,
|
|
52
|
+
workspaceName: selectedWorkspace.title,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
global.requestId = 'dummy_request_id';
|
|
56
|
+
|
|
57
|
+
logSpy = jest.spyOn(FetchLogsCommand.prototype, 'log');
|
|
58
|
+
errorLogSpy = jest.spyOn(FetchLogsCommand.prototype, 'error');
|
|
59
|
+
platformSpy = jest.spyOn(os, 'platform');
|
|
60
|
+
|
|
61
|
+
writeFile.mockResolvedValue(true);
|
|
62
|
+
|
|
63
|
+
parseSpy = jest.spyOn(FetchLogsCommand.prototype, 'parse');
|
|
64
|
+
parseSpy.mockResolvedValue({
|
|
65
|
+
args: { rayId: 'ray1' },
|
|
66
|
+
flags: {
|
|
67
|
+
ignoreCache: mockIgnoreCacheFlag,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
getMeshId.mockResolvedValue('12345');
|
|
71
|
+
getLogsByRayId.mockResolvedValue({
|
|
72
|
+
'EventTimestampMs': 123456789,
|
|
73
|
+
'Exceptions': 'None',
|
|
74
|
+
'Logs': 'Log data',
|
|
75
|
+
'Outcome': 'Success',
|
|
76
|
+
'meshId': 'mesh1',
|
|
77
|
+
'rayId': 'ray1',
|
|
78
|
+
'URL': 'http://example.com',
|
|
79
|
+
'Request Method': 'GET',
|
|
80
|
+
'Response Status': 200,
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
platformSpy.mockRestore();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('snapshot FetchLogsCommand', () => {
|
|
89
|
+
expect(FetchLogsCommand.description).toMatchInlineSnapshot(
|
|
90
|
+
`"Get the Log of a given mesh by RayId"`,
|
|
91
|
+
);
|
|
92
|
+
expect(FetchLogsCommand.args).toMatchInlineSnapshot(`
|
|
93
|
+
[
|
|
94
|
+
{
|
|
95
|
+
"description": "Fetch a single log by rayID",
|
|
96
|
+
"name": "rayId",
|
|
97
|
+
"required": true,
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
`);
|
|
101
|
+
expect(FetchLogsCommand.flags).toMatchInlineSnapshot(`
|
|
102
|
+
{
|
|
103
|
+
"ignoreCache": {
|
|
104
|
+
"allowNo": false,
|
|
105
|
+
"char": "i",
|
|
106
|
+
"default": false,
|
|
107
|
+
"description": "Ignore cache and force manual org -> project -> workspace selection",
|
|
108
|
+
"parse": [Function],
|
|
109
|
+
"type": "boolean",
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
`);
|
|
113
|
+
expect(FetchLogsCommand.aliases).toMatchInlineSnapshot(`[]`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('should handle log not found error', async () => {
|
|
117
|
+
getLogsByRayId.mockRejectedValue(new Error('LogNotFound'));
|
|
118
|
+
|
|
119
|
+
const runResult = FetchLogsCommand.run();
|
|
120
|
+
|
|
121
|
+
return runResult.catch(err => {
|
|
122
|
+
expect(err.message).toMatchInlineSnapshot(`
|
|
123
|
+
"No logs found for RayID ray1. Check the RayID and try again. RequestId: dummy_request_id. Alternatively, you can use the following command to get all logs for a 30 minute time period:
|
|
124
|
+
aio api-mesh log-get-bulk --startTime YYYY-MM-DDTHH:MM:SSZ --endTime YYYY-MM-DDTHH:MM:SSZ --filename mesh_logs.csv"
|
|
125
|
+
`);
|
|
126
|
+
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
|
|
127
|
+
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
|
|
128
|
+
[
|
|
129
|
+
[
|
|
130
|
+
"No logs found for RayID ray1. Check the RayID and try again. RequestId: dummy_request_id. Alternatively, you can use the following command to get all logs for a 30 minute time period:
|
|
131
|
+
aio api-mesh log-get-bulk --startTime YYYY-MM-DDTHH:MM:SSZ --endTime YYYY-MM-DDTHH:MM:SSZ --filename mesh_logs.csv",
|
|
132
|
+
],
|
|
133
|
+
]
|
|
134
|
+
`);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('should handle server error', async () => {
|
|
139
|
+
getLogsByRayId.mockRejectedValue(new Error('ServerError'));
|
|
140
|
+
|
|
141
|
+
const runResult = FetchLogsCommand.run();
|
|
142
|
+
|
|
143
|
+
return runResult.catch(err => {
|
|
144
|
+
expect(err.message).toMatchInlineSnapshot(
|
|
145
|
+
`"Server error while fetching logs for RayId ray1. Please try again later. RequestId: dummy_request_id"`,
|
|
146
|
+
);
|
|
147
|
+
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
|
|
148
|
+
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
|
|
149
|
+
[
|
|
150
|
+
[
|
|
151
|
+
"Server error while fetching logs for RayId ray1. Please try again later. RequestId: dummy_request_id",
|
|
152
|
+
],
|
|
153
|
+
]
|
|
154
|
+
`);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('should handle generic error', async () => {
|
|
159
|
+
getLogsByRayId.mockRejectedValue({
|
|
160
|
+
response: { status: 503, statusText: 'Service Unavailable' },
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const runResult = FetchLogsCommand.run();
|
|
164
|
+
|
|
165
|
+
return runResult.catch(err => {
|
|
166
|
+
expect(err.message).toMatchInlineSnapshot(
|
|
167
|
+
`"Unable to get mesh logs. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id"`,
|
|
168
|
+
);
|
|
169
|
+
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
|
|
170
|
+
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
|
|
171
|
+
[
|
|
172
|
+
[
|
|
173
|
+
"Unable to get mesh logs. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id",
|
|
174
|
+
],
|
|
175
|
+
]
|
|
176
|
+
`);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('should handle mesh ID not found error', async () => {
|
|
181
|
+
getMeshId.mockResolvedValue(null);
|
|
182
|
+
|
|
183
|
+
const runResult = FetchLogsCommand.run();
|
|
184
|
+
|
|
185
|
+
return runResult.catch(err => {
|
|
186
|
+
expect(err.message).toMatchInlineSnapshot(
|
|
187
|
+
`"Unable to get mesh ID. Please check the details and try again. RequestId: dummy_request_id"`,
|
|
188
|
+
);
|
|
189
|
+
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
|
|
190
|
+
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
|
|
191
|
+
[
|
|
192
|
+
[
|
|
193
|
+
"Unable to get mesh ID. Please check the details and try again. RequestId: dummy_request_id",
|
|
194
|
+
],
|
|
195
|
+
]
|
|
196
|
+
`);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
test('should fetch logs successfully', async () => {
|
|
200
|
+
getMeshId.mockResolvedValue('mesh1');
|
|
201
|
+
getLogsByRayId.mockResolvedValue({
|
|
202
|
+
eventTimestampMs: 1724660420904,
|
|
203
|
+
exceptions: '[]',
|
|
204
|
+
logs:
|
|
205
|
+
'[{\'Level\': \'log\', \'Message\': [\'[object Object]\'], \'TimestampMs\': 1724660422580}, {\'Level\': \'log\', \'Message\': [\'{"sources":[{"name":"venia","handler":{"graphql":{"useGETForQueries":true,"endpoint":"https://venia.magento.com/graphql","operationHeaders":{"x-test-header":"{context.headers[\\\'x-test-header\\\']}"}}}}],"responseConfig":{"includeHTTPDetails":true},"additionalResolvers":[],"plugins":[{"httpDetailsExtensions":{}}]}\'], \'TimestampMs\': 1724660422580}]',
|
|
206
|
+
outcome: 'ok',
|
|
207
|
+
meshId: 'mesh1',
|
|
208
|
+
rayId: 'ray1',
|
|
209
|
+
url: 'https://edge-dev1-graph.adobe.io/api/REDACTED/graphql',
|
|
210
|
+
requestMethod: 'POST',
|
|
211
|
+
responseStatus: 200,
|
|
212
|
+
level: 'log',
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const command = new FetchLogsCommand(['ray1']);
|
|
216
|
+
await command.run();
|
|
217
|
+
|
|
218
|
+
expect(logSpy).toHaveBeenCalledWith('Event Timestamp : %s', 1724660420904);
|
|
219
|
+
expect(logSpy).toHaveBeenCalledWith('Exceptions : %s', '[]');
|
|
220
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
221
|
+
'Logs : %s',
|
|
222
|
+
'[{\'Level\': \'log\', \'Message\': [\'[object Object]\'], \'TimestampMs\': 1724660422580}, {\'Level\': \'log\', \'Message\': [\'{"sources":[{"name":"venia","handler":{"graphql":{"useGETForQueries":true,"endpoint":"https://venia.magento.com/graphql","operationHeaders":{"x-test-header":"{context.headers[\\\'x-test-header\\\']}"}}}}],"responseConfig":{"includeHTTPDetails":true},"additionalResolvers":[],"plugins":[{"httpDetailsExtensions":{}}]}\'], \'TimestampMs\': 1724660422580}]',
|
|
223
|
+
);
|
|
224
|
+
expect(logSpy).toHaveBeenCalledWith('Outcome : %s', 'ok');
|
|
225
|
+
expect(logSpy).toHaveBeenCalledWith('Mesh ID : %s', 'mesh1');
|
|
226
|
+
expect(logSpy).toHaveBeenCalledWith('RayId : %s', 'ray1');
|
|
227
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
228
|
+
'Mesh URL : %s',
|
|
229
|
+
'https://edge-dev1-graph.adobe.io/api/REDACTED/graphql',
|
|
230
|
+
);
|
|
231
|
+
expect(logSpy).toHaveBeenCalledWith('Request Method : %s', 'POST');
|
|
232
|
+
expect(logSpy).toHaveBeenCalledWith('Request Status : %s', 200);
|
|
233
|
+
});
|
|
234
|
+
});
|