diff --git a/.changeset/lovely-shirts-play.md b/.changeset/lovely-shirts-play.md new file mode 100644 index 0000000000000..3760e7c5c653e --- /dev/null +++ b/.changeset/lovely-shirts-play.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': patch +'@rocket.chat/meteor': patch +--- + +Fix an issue where the report exported in the App logs page would not consider the instance id filter diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.spec.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.spec.tsx index f7c84846694c0..f36881642a18a 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.spec.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.spec.tsx @@ -10,6 +10,10 @@ const testCases = Object.values(composeStories(stories)).map((Story) => [Story.s const onConfirm = jest.fn(); const { Default } = composeStories(stories); +afterEach(() => { + jest.clearAllMocks(); +}); + test.each(testCases)(`renders without crashing`, async (_storyname, Story) => { const view = render(, { wrapper: mockAppRoot().build(), @@ -35,3 +39,22 @@ it('should send the correct payload to the endpoint', async () => { expect(onConfirm).toHaveBeenCalledTimes(1); expect(onConfirm).toHaveBeenCalledWith('/api/apps/undefined/export-logs?count=2000&type=json'); }); + +it('should include instance filter in the payload to endpoint', async () => { + render( + , + { + wrapper: mockAppRoot().build(), + }, + ); + + expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument(); + await userEvent.click(screen.getByRole('button', { name: 'Download' })); + expect(onConfirm).toHaveBeenCalledTimes(1); + expect(onConfirm).toHaveBeenCalledWith('/api/apps/undefined/export-logs?instanceId=123&count=2000&type=json'); +}); diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.tsx index 87b44b584af8c..0adece3e2d905 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/ExportLogsModal.tsx @@ -57,6 +57,7 @@ export const ExportLogsModal = ({ onClose, filterValues, onConfirm }: ExportLogs const getFileUrl = ({ severity, event, + instance, startDate, endDate, count, @@ -71,6 +72,9 @@ export const ExportLogsModal = ({ onClose, filterValues, onConfirm }: ExportLogs if (event && event !== 'all') { baseUrl += `method=${event}&`; } + if (instance && instance !== 'all') { + baseUrl += `instanceId=${instance}&`; + } if (startDate) { baseUrl += `startDate=${new Date(`${startDate}T${startTime}`).toISOString()}&`; } diff --git a/apps/meteor/tests/data/api-data.ts b/apps/meteor/tests/data/api-data.ts index 4b936d5c6558c..6bfb97756ec73 100644 --- a/apps/meteor/tests/data/api-data.ts +++ b/apps/meteor/tests/data/api-data.ts @@ -63,6 +63,8 @@ export function log(res: Response) { }); } +let instanceId: string | undefined; + export function getCredentials(done?: CallbackHandler) { void request .post(api('login')) @@ -75,6 +77,11 @@ export function getCredentials(done?: CallbackHandler) { .expect((res) => { credentials['X-Auth-Token'] = res.body.data.authToken; credentials['X-User-Id'] = res.body.data.userId; + instanceId = res.headers['x-instance-id']; }) .end(done); } + +export function getInstanceId() { + return instanceId; +} diff --git a/apps/meteor/tests/end-to-end/apps/app-logs-export.ts b/apps/meteor/tests/end-to-end/apps/app-logs-export.ts index 59ed69ff591b4..0d8b1fc088a44 100644 --- a/apps/meteor/tests/end-to-end/apps/app-logs-export.ts +++ b/apps/meteor/tests/end-to-end/apps/app-logs-export.ts @@ -3,7 +3,7 @@ import type { App } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; -import { getCredentials, request, credentials } from '../../data/api-data'; +import { getCredentials, request, credentials, getInstanceId } from '../../data/api-data'; import { apps } from '../../data/apps/apps-data'; import { installTestApp, cleanupApps } from '../../data/apps/helper'; import { IS_EE } from '../../e2e/config/constants'; @@ -168,6 +168,25 @@ import { IS_EE } from '../../e2e/config/constants'; .end(done); }); + it('should export app logs filtered by instance id', (done) => { + const instanceId = getInstanceId(); + void request + .get(apps(`/${app.id}/export-logs`)) + .query({ instanceId, type: 'json' }) + .set(credentials) + .expect('Content-Type', 'text/plain') + .expect(200) + .expect((res) => { + const logs = JSON.parse(res.text); + expect(logs).to.be.an('array'); + + logs.forEach((log: ILoggerStorageEntry) => { + expect(log.instanceId).to.equal(instanceId); + }); + }) + .end(done); + }); + it('should export app logs filtered by date range', (done) => { const startDate = new Date(); startDate.setDate(startDate.getDate() - 1); // 1 day ago diff --git a/packages/rest-typings/src/apps/appLogsExportProps.ts b/packages/rest-typings/src/apps/appLogsExportProps.ts index 9c1ecd6c9a33e..ff66255e4992b 100644 --- a/packages/rest-typings/src/apps/appLogsExportProps.ts +++ b/packages/rest-typings/src/apps/appLogsExportProps.ts @@ -10,6 +10,7 @@ const AppLogsExportPropsSchema = { properties: { logLevel: { type: 'string', enum: ['0', '1', '2'], nullable: true }, method: { type: 'string', nullable: true }, + instanceId: { type: 'string', nullable: true }, startDate: { type: 'string', format: 'date-time', nullable: true }, endDate: { type: 'string', format: 'date-time', nullable: true }, type: { type: 'string', enum: ['json', 'csv'] },