From f01edd0f1a68df833aaadb684a9c62b37a409a3d Mon Sep 17 00:00:00 2001 From: ajaz Date: Thu, 3 Apr 2025 07:52:31 -0500 Subject: [PATCH 1/6] chore: update --past to have integer values --- package.json | 1 - .../api-mesh/__tests__/log-get-bulk.test.js | 42 +++++++------------ src/utils.js | 14 ++----- 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 5d886749..055b49ad 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "jsmin": "1.0.1", "json-interpolate": "^1.0.3", "lru-cache": "^7.14.1", - "ms": "^2.1.3", "node-clipboardy": "^1.0.3", "node-fetch": "2.6.1", "pino": "^9.5.0", diff --git a/src/commands/api-mesh/__tests__/log-get-bulk.test.js b/src/commands/api-mesh/__tests__/log-get-bulk.test.js index f8e32c26..8dc056b2 100644 --- a/src/commands/api-mesh/__tests__/log-get-bulk.test.js +++ b/src/commands/api-mesh/__tests__/log-get-bulk.test.js @@ -307,7 +307,7 @@ describe('GetBulkLogCommand with --past and --from flags', () => { fromDate.setDate(fromDate.getDate() - 29); // Set fromDate to 29 days ago parseSpy = jest.spyOn(GetBulkLogCommand.prototype, 'parse').mockResolvedValue({ flags: { - past: '20mins', + past: '20', from: fromDate.toISOString().slice(0, 10) + ':12:00:00', filename: 'test.csv', ignoreCache: false, @@ -376,7 +376,7 @@ describe('GetBulkLogCommand with --past and --from flags', () => { test('throws an error with invalid --from date components', async () => { parseSpy.mockResolvedValueOnce({ flags: { - past: '20mins', + past: '20', from: fromDate.toISOString().slice(0, 10) + ':25:61:61', filename: 'test.csv', ignoreCache: false, @@ -392,7 +392,7 @@ describe('GetBulkLogCommand with --past and --from flags', () => { test('throws an error with invalid --from date format', async () => { parseSpy.mockResolvedValueOnce({ flags: { - past: '15mins', + past: '15', from: fromDate.toISOString().slice(0, 10).replace(/-/g, ':') + ':15:00:00', filename: 'test.csv', ignoreCache: false, @@ -408,7 +408,7 @@ describe('GetBulkLogCommand with --past and --from flags', () => { test('runs with valid --past flag without --from', async () => { parseSpy.mockResolvedValueOnce({ flags: { - past: '15mins', + past: '15', filename: 'test.csv', ignoreCache: false, }, @@ -436,7 +436,7 @@ describe('GetBulkLogCommand with --past and --from flags', () => { test('throws an error with edge case for --past duration', async () => { parseSpy.mockResolvedValueOnce({ flags: { - past: '0s', + past: '0', from: fromDate.toISOString().slice(0, 10) + ':12:00:00', filename: 'test.csv', ignoreCache: false, @@ -445,14 +445,14 @@ describe('GetBulkLogCommand with --past and --from flags', () => { const command = new GetBulkLogCommand([], {}); await expect(command.run()).rejects.toThrow( - 'Invalid format. The past time window should be in minutes, for example, "20 mins", "15 minutes".', + 'The minimum duration is 1 minutes. The current duration is 0 minutes.', ); }); test('runs with edge case for --from date', async () => { parseSpy.mockResolvedValueOnce({ flags: { - past: '15mins', + past: '15', from: fromDate.toISOString().slice(0, 10) + ':00:00:00', filename: 'test.csv', ignoreCache: false, @@ -528,16 +528,9 @@ describe('validateDateTimeRange', () => { describe('parsePastDuration', () => { const validDurations = [ - ['20m', 20 * 60 * 1000], - ['20 m', 20 * 60 * 1000], - ['20min', 20 * 60 * 1000], - ['20 min', 20 * 60 * 1000], - ['20mins', 20 * 60 * 1000], - ['20 mins', 20 * 60 * 1000], - ['20minute', 20 * 60 * 1000], - ['20 minute', 20 * 60 * 1000], - ['20minutes', 20 * 60 * 1000], - ['20 minutes', 20 * 60 * 1000], + ['20', 20 * 60 * 1000], + ['30', 30 * 60 * 1000], + ['15', 15 * 60 * 1000], ]; test.each(validDurations)( @@ -548,14 +541,11 @@ describe('parsePastDuration', () => { }, ); - const invalidDurations = ['20h', '20 hours', '20s', '20 seconds']; + const invalidDurations = ['minutes', 'NaN', 'abc', '']; - test.each(invalidDurations)( - 'throws an error for invalid past duration format "%s"', - invalidPastDuration => { - expect(() => parsePastDuration(invalidPastDuration)).toThrow( - 'Invalid format. The past time window should be in minutes, for example, "20 mins", "15 minutes".', - ); - }, - ); + test.each(invalidDurations)('throws an error for non-numeric input "%s"', invalidPastDuration => { + expect(() => parsePastDuration(invalidPastDuration)).toThrow( + 'Invalid format. The past time window should be integer, for example, "20", "15".', + ); + }); }); diff --git a/src/utils.js b/src/utils.js index d989dff0..d8e21acb 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,7 +6,6 @@ const { readFile } = require('fs/promises'); const { interpolateMesh } = require('./helpers'); const dotenv = require('dotenv'); const YAML = require('yaml'); -const ms = require('ms'); const parseEnv = require('envsub/js/envsub-parser'); const os = require('os'); const chalk = require('chalk'); @@ -658,23 +657,18 @@ function suggestCorrectedDateFormat(inputDate) { /** * Parses a duration string representing a past time window and converts it to milliseconds. * - * @param {string} pastTimeWindow - The past time duration to parse, e.g., "20 mins", "15 minutes". + * @param {string} pastTimeWindow - The past time duration to parse, e.g., "20", "15". * @returns {number} The duration in milliseconds. */ function parsePastDuration(pastTimeWindow) { - // Regular expression to match various formats of minute abbreviations - const pastDurationRegex = /^(\d+)\s*(m|mins?|minutes?)$/i; - const match = pastTimeWindow.match(pastDurationRegex); + const durationInMs = parseInt(pastTimeWindow) * 60 * 1000; - if (!match) { + if (isNaN(durationInMs)) { throw new Error( - 'Invalid format. The past time window should be in minutes, for example, "20 mins", "15 minutes".', + 'Invalid format. The past time window should be integer, for example, "20", "15".', ); } - // Convert the matched duration to milliseconds - const durationInMs = ms(pastTimeWindow); - return durationInMs; } From 05fef1819880df27d320c4f0b5bc36b8d0ad34f5 Mon Sep 17 00:00:00 2001 From: ajaz Date: Thu, 3 Apr 2025 08:04:48 -0500 Subject: [PATCH 2/6] updated past TimeWindow duration check --- src/utils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils.js b/src/utils.js index d8e21acb..8eaf90fc 100644 --- a/src/utils.js +++ b/src/utils.js @@ -661,9 +661,12 @@ function suggestCorrectedDateFormat(inputDate) { * @returns {number} The duration in milliseconds. */ function parsePastDuration(pastTimeWindow) { + // Check if pastTimeWindow contains non-numeric characters + const match = pastTimeWindow.match(/^(\d+)$/); + const durationInMs = parseInt(pastTimeWindow) * 60 * 1000; - if (isNaN(durationInMs)) { + if (isNaN(durationInMs) || !match) { throw new Error( 'Invalid format. The past time window should be integer, for example, "20", "15".', ); From 24662d7a25ec3c40a3068bbca747a35086ff93a2 Mon Sep 17 00:00:00 2001 From: ajaz Date: Mon, 7 Apr 2025 15:28:53 +0530 Subject: [PATCH 3/6] fix: removed --from flag --- .../api-mesh/__tests__/log-get-bulk.test.js | 183 ------------------ src/commands/api-mesh/log-get-bulk.js | 28 +-- src/utils.js | 6 +- 3 files changed, 5 insertions(+), 212 deletions(-) diff --git a/src/commands/api-mesh/__tests__/log-get-bulk.test.js b/src/commands/api-mesh/__tests__/log-get-bulk.test.js index 8dc056b2..cab1d2c2 100644 --- a/src/commands/api-mesh/__tests__/log-get-bulk.test.js +++ b/src/commands/api-mesh/__tests__/log-get-bulk.test.js @@ -296,189 +296,6 @@ describe('GetBulkLogCommand startTime and endTime validation', () => { ); }); -describe('GetBulkLogCommand with --past and --from flags', () => { - let parseSpy; - - let now; - let fromDate; - beforeEach(() => { - now = new Date(); - fromDate = new Date(now); - fromDate.setDate(fromDate.getDate() - 29); // Set fromDate to 29 days ago - parseSpy = jest.spyOn(GetBulkLogCommand.prototype, 'parse').mockResolvedValue({ - flags: { - past: '20', - from: fromDate.toISOString().slice(0, 10) + ':12:00:00', - filename: 'test.csv', - ignoreCache: false, - }, - }); - - initSdk.mockResolvedValue({ - imsOrgId: 'orgId', - imsOrgCode: 'orgCode', - projectId: 'projectId', - workspaceId: 'workspaceId', - workspaceName: 'workspaceName', - }); - getMeshId.mockResolvedValue('meshId'); - getPresignedUrls.mockResolvedValue({ - presignedUrls: [{ key: 'log1.csv', url: 'http://example.com/someHash' }], - totalSize: 2048, - }); - promptConfirm.mockResolvedValue(true); - global.requestId = 'dummy_request_id'; - }); - - afterEach(() => { - jest.clearAllMocks(); - // clear the date objects - now = null; - fromDate = null; - }); - - test('runs with valid --past and --from flags', async () => { - fs.existsSync.mockReturnValue(true); - fs.statSync.mockReturnValue({ size: 0 }); - - const mockWriteStream = { - write: jest.fn(), - end: jest.fn(), - on: jest.fn((event, callback) => { - if (event === 'finish') { - callback(); - } - }), - }; - fs.createWriteStream.mockReturnValue(mockWriteStream); - - const command = new GetBulkLogCommand([], {}); - await command.run(); - - expect(initRequestId).toHaveBeenCalled(); - expect(initSdk).toHaveBeenCalled(); - expect(getMeshId).toHaveBeenCalledWith('orgCode', 'projectId', 'workspaceId', 'workspaceName'); - expect(getPresignedUrls).toHaveBeenCalledWith( - 'orgCode', - 'projectId', - 'workspaceId', - 'meshId', - expect.any(String), - expect.any(String), - ); - expect(fs.createWriteStream).toHaveBeenCalledWith(path.resolve(process.cwd(), 'test.csv'), { - flags: 'a', - }); - expect(mockWriteStream.write).toHaveBeenCalled(); - expect(mockWriteStream.end).toHaveBeenCalled(); - }); - - test('throws an error with invalid --from date components', async () => { - parseSpy.mockResolvedValueOnce({ - flags: { - past: '20', - from: fromDate.toISOString().slice(0, 10) + ':25:61:61', - filename: 'test.csv', - ignoreCache: false, - }, - }); - - const command = new GetBulkLogCommand([], {}); - await expect(command.run()).rejects.toThrow( - 'Invalid date components passed in --from. Correct the date.', - ); - }); - - test('throws an error with invalid --from date format', async () => { - parseSpy.mockResolvedValueOnce({ - flags: { - past: '15', - from: fromDate.toISOString().slice(0, 10).replace(/-/g, ':') + ':15:00:00', - filename: 'test.csv', - ignoreCache: false, - }, - }); - - const command = new GetBulkLogCommand([], {}); - await expect(command.run()).rejects.toThrow( - 'Invalid format. Use the format YYYY-MM-DD:HH:MM:SS for --from.', - ); - }); - - test('runs with valid --past flag without --from', async () => { - parseSpy.mockResolvedValueOnce({ - flags: { - past: '15', - filename: 'test.csv', - ignoreCache: false, - }, - }); - - fs.existsSync.mockReturnValue(true); - fs.statSync.mockReturnValue({ size: 0 }); - - const command = new GetBulkLogCommand([], {}); - await command.run(); - - expect(initRequestId).toHaveBeenCalled(); - expect(initSdk).toHaveBeenCalled(); - expect(getMeshId).toHaveBeenCalledWith('orgCode', 'projectId', 'workspaceId', 'workspaceName'); - expect(getPresignedUrls).toHaveBeenCalledWith( - 'orgCode', - 'projectId', - 'workspaceId', - 'meshId', - expect.any(String), - expect.any(String), - ); - }); - - test('throws an error with edge case for --past duration', async () => { - parseSpy.mockResolvedValueOnce({ - flags: { - past: '0', - from: fromDate.toISOString().slice(0, 10) + ':12:00:00', - filename: 'test.csv', - ignoreCache: false, - }, - }); - - const command = new GetBulkLogCommand([], {}); - await expect(command.run()).rejects.toThrow( - 'The minimum duration is 1 minutes. The current duration is 0 minutes.', - ); - }); - - test('runs with edge case for --from date', async () => { - parseSpy.mockResolvedValueOnce({ - flags: { - past: '15', - from: fromDate.toISOString().slice(0, 10) + ':00:00:00', - filename: 'test.csv', - ignoreCache: false, - }, - }); - - fs.existsSync.mockReturnValue(true); - fs.statSync.mockReturnValue({ size: 0 }); - - const command = new GetBulkLogCommand([], {}); - await command.run(); - - expect(initRequestId).toHaveBeenCalled(); - expect(initSdk).toHaveBeenCalled(); - expect(getMeshId).toHaveBeenCalledWith('orgCode', 'projectId', 'workspaceId', 'workspaceName'); - expect(getPresignedUrls).toHaveBeenCalledWith( - 'orgCode', - 'projectId', - 'workspaceId', - 'meshId', - expect.any(String), - expect.any(String), - ); - }); -}); - describe('validateDateTimeRange', () => { const testCases = [ { diff --git a/src/commands/api-mesh/log-get-bulk.js b/src/commands/api-mesh/log-get-bulk.js index 2de68e68..69dd23b8 100644 --- a/src/commands/api-mesh/log-get-bulk.js +++ b/src/commands/api-mesh/log-get-bulk.js @@ -11,12 +11,10 @@ const { endTimeFlag, logFilenameFlag, pastFlag, - fromFlag, suggestCorrectedDateFormat, parsePastDuration, validateDateTimeRange, validateDateTimeFormat, - localToUTCTime, } = require('../../utils'); require('dotenv').config(); @@ -28,7 +26,6 @@ class GetBulkLogCommand extends Command { endTime: endTimeFlag, filename: logFilenameFlag, past: pastFlag, - from: fromFlag, }; async run() { @@ -90,26 +87,9 @@ class GetBulkLogCommand extends Command { formattedEndTime = flags.endTime.replace(/-|:|Z/g, '').replace('T', 'T'); } else if (flags.past) { const pastTimeWindow = parsePastDuration(flags.past); - if (flags.from) { - let convertedTime; - const dateTimeRegex = /^\d{4}-\d{2}-\d{2}:\d{2}:\d{2}:\d{2}$/; - if (!dateTimeRegex.test(flags.from)) { - this.error('Invalid format. Use the format YYYY-MM-DD:HH:MM:SS for --from.'); - } else { - try { - convertedTime = await localToUTCTime(flags.from.toString()); - } catch (error) { - this.error(`Invalid date components passed in --from. Correct the date.`); - } - } - // add the past window to the converted time to get the end time to fetch logs from the past - calculatedStartTime = new Date(convertedTime); - calculatedEndTime = new Date(calculatedStartTime.getTime() + pastTimeWindow); - } else { - // subtract the past window from the current time to get the start time to fetch recent logs from now - calculatedEndTime = new Date(); - calculatedStartTime = new Date(calculatedEndTime.getTime() - pastTimeWindow); - } + // Subtract the past window from the current time to get the start time to fetch recent logs from now + calculatedEndTime = new Date(); + calculatedStartTime = new Date(calculatedEndTime.getTime() - pastTimeWindow); // Validate the calculated start and end times range validateDateTimeRange(calculatedStartTime, calculatedEndTime); @@ -121,7 +101,7 @@ class GetBulkLogCommand extends Command { return; } else { this.error( - 'Missing required flags. Provide at least one flag --startTime, --endTime, or --past --from or type `mesh log:get-bulk --help` for more information.', + 'Missing required flags. Provide at least one flag --startTime, --endTime, or --past or type `mesh log:get-bulk --help` for more information.', ); return; } diff --git a/src/utils.js b/src/utils.js index 8eaf90fc..57c85e0b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -89,10 +89,6 @@ const pastFlag = Flags.string({ description: 'Past time window in mins', }); -const fromFlag = Flags.string({ - description: `The from time in YYYY-MM-DD:HH:MM:SS format based on your system's time zone. It is used to fetch logs from the past and is the starting time for the past time duration.`, -}); - const logFilenameFlag = Flags.string({ description: 'Path to the output file for logs', required: true, @@ -759,6 +755,7 @@ function validateDateTimeFormat(time) { return timeString.replace(/-|:|Z/g, '').replace('T', 'T'); } +// can be used later if we want to take --startTime and --endTime in local time /** * Convert a given local time string to UTC time string * @param {string} timeString - The time string in the format YYYY-MM-DD:HH:MM:SS @@ -811,7 +808,6 @@ module.exports = { endTimeFlag, logFilenameFlag, pastFlag, - fromFlag, suggestCorrectedDateFormat, parsePastDuration, validateDateTimeRange, From 2f1f44bc340ff348415cf0528becb568953560ab Mon Sep 17 00:00:00 2001 From: Sumaiya <108254100+AjazSumaiya@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:00:20 +0530 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Jared Hoover <98363870+jhadobe@users.noreply.github.com> --- src/commands/api-mesh/log-get-bulk.js | 2 +- src/utils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/api-mesh/log-get-bulk.js b/src/commands/api-mesh/log-get-bulk.js index 69dd23b8..3eb00a13 100644 --- a/src/commands/api-mesh/log-get-bulk.js +++ b/src/commands/api-mesh/log-get-bulk.js @@ -101,7 +101,7 @@ class GetBulkLogCommand extends Command { return; } else { this.error( - 'Missing required flags. Provide at least one flag --startTime, --endTime, or --past or type `mesh log:get-bulk --help` for more information.', + 'Missing required flags. Provide a time range with --startTime and --endTime flags, or use the --past flag for more recent logs. Use the `mesh log:get-bulk --help` command for more information.', ); return; } diff --git a/src/utils.js b/src/utils.js index 57c85e0b..092f9cef 100644 --- a/src/utils.js +++ b/src/utils.js @@ -664,7 +664,7 @@ function parsePastDuration(pastTimeWindow) { if (isNaN(durationInMs) || !match) { throw new Error( - 'Invalid format. The past time window should be integer, for example, "20", "15".', + 'Invalid format. The time window must be an integer, for example "20" or "15".', ); } From 8746fe23adc19f2355ebede281df66704e1d178f Mon Sep 17 00:00:00 2001 From: Sumaiya <108254100+AjazSumaiya@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:52:19 +0530 Subject: [PATCH 5/6] address code review Co-authored-by: Andrew Molina <44038475+amolina-adobe@users.noreply.github.com> --- src/commands/api-mesh/__tests__/log-get-bulk.test.js | 2 +- src/utils.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/api-mesh/__tests__/log-get-bulk.test.js b/src/commands/api-mesh/__tests__/log-get-bulk.test.js index cab1d2c2..01a523c3 100644 --- a/src/commands/api-mesh/__tests__/log-get-bulk.test.js +++ b/src/commands/api-mesh/__tests__/log-get-bulk.test.js @@ -358,7 +358,7 @@ describe('parsePastDuration', () => { }, ); - const invalidDurations = ['minutes', 'NaN', 'abc', '']; + const invalidDurations = ['20h', '20 hours', '20s', '20 seconds','minutes', 'NaN', 'abc', '']; test.each(invalidDurations)('throws an error for non-numeric input "%s"', invalidPastDuration => { expect(() => parsePastDuration(invalidPastDuration)).toThrow( diff --git a/src/utils.js b/src/utils.js index 092f9cef..8f7efd43 100644 --- a/src/utils.js +++ b/src/utils.js @@ -86,7 +86,7 @@ const endTimeFlag = Flags.string({ }); const pastFlag = Flags.string({ - description: 'Past time window in mins', + description: 'Past time window in minutes', }); const logFilenameFlag = Flags.string({ @@ -653,14 +653,14 @@ function suggestCorrectedDateFormat(inputDate) { /** * Parses a duration string representing a past time window and converts it to milliseconds. * - * @param {string} pastTimeWindow - The past time duration to parse, e.g., "20", "15". + * @param {string} pastTimeWindow - The past time duration in minutes, e.g., "20", "15". * @returns {number} The duration in milliseconds. */ function parsePastDuration(pastTimeWindow) { // Check if pastTimeWindow contains non-numeric characters const match = pastTimeWindow.match(/^(\d+)$/); - const durationInMs = parseInt(pastTimeWindow) * 60 * 1000; + const durationInMs = Number(pastTimeWindow) * 60 * 1000; if (isNaN(durationInMs) || !match) { throw new Error( From 0002284899c4fbeecab57304fe433c45596b21d9 Mon Sep 17 00:00:00 2001 From: ajaz Date: Wed, 9 Apr 2025 16:01:59 +0530 Subject: [PATCH 6/6] fix: lint and test --- src/commands/api-mesh/__tests__/log-get-bulk.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/api-mesh/__tests__/log-get-bulk.test.js b/src/commands/api-mesh/__tests__/log-get-bulk.test.js index 01a523c3..6ba5d028 100644 --- a/src/commands/api-mesh/__tests__/log-get-bulk.test.js +++ b/src/commands/api-mesh/__tests__/log-get-bulk.test.js @@ -358,11 +358,11 @@ describe('parsePastDuration', () => { }, ); - const invalidDurations = ['20h', '20 hours', '20s', '20 seconds','minutes', 'NaN', 'abc', '']; + const invalidDurations = ['20h', '20 hours', '20s', '20 seconds', 'minutes', 'NaN', 'abc', '']; test.each(invalidDurations)('throws an error for non-numeric input "%s"', invalidPastDuration => { expect(() => parsePastDuration(invalidPastDuration)).toThrow( - 'Invalid format. The past time window should be integer, for example, "20", "15".', + 'Invalid format. The time window must be an integer, for example "20" or "15".', ); }); });