From 1a1b0111c4bbe18da2d9c3cb328a8a8191ec5d89 Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:11:31 +0800 Subject: [PATCH 1/8] fix: include URL in api list --json output Fixes #388 - the --json flag was outputting only `apidoc` (missing the gateway URL). Now outputs the same processed array as the table view, which includes the URL built from `gwApiUrl`. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/runtime/api/list.js | 10 +++++----- test/commands/runtime/api/list.test.js | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/commands/runtime/api/list.js b/src/commands/runtime/api/list.js index 646c9f78..6f902bec 100644 --- a/src/commands/runtime/api/list.js +++ b/src/commands/runtime/api/list.js @@ -56,11 +56,6 @@ class ApiList extends RuntimeBaseCommand { const result = await ow.routes.list(options) - if (shouldOutputJson) { - this.logJSON('', result.apis[0].value.apidoc) - return - } - let data = [] result.apis.forEach(api => { // join the two arrays by reduce @@ -70,6 +65,11 @@ class ApiList extends RuntimeBaseCommand { }, data) }) + if (shouldOutputJson) { + this.logJSON('', data) + return + } + table(data, { Action: { minWidth: 10 }, Verb: { minWidth: 10 }, diff --git a/test/commands/runtime/api/list.test.js b/test/commands/runtime/api/list.test.js index e716991b..733b7497 100644 --- a/test/commands/runtime/api/list.test.js +++ b/test/commands/runtime/api/list.test.js @@ -102,9 +102,13 @@ describe('instance methods', () => { .then(() => { const expectedJson = fixtureJson('api/list.json') const output = stdout.output.trim() - const jsonMatch = output.match(/\{[\s\S]*\}$/) + const jsonMatch = output.match(/\[[\s\S]*\]$/) const jsonOutput = jsonMatch ? jsonMatch[0] : output - expect(JSON.parse(jsonOutput)).toMatchObject(expectedJson.apis[0].value.apidoc) + const parsed = JSON.parse(jsonOutput) + // should include URL field built from gwApiUrl + expect(parsed).toBeInstanceOf(Array) + expect(parsed[0]).toHaveProperty('URL') + expect(parsed[0].URL).toContain(expectedJson.apis[0].value.gwApiUrl) }) .finally(() => { stdout.stop() From 9ef0ca1108f75947c2ac9931e03d8042ffbf8994 Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 13 Apr 2026 18:15:19 +0800 Subject: [PATCH 2/8] fix: populate x-openwhisk.url in api list --json output Fixes #388 - keeps the apidoc JSON structure unchanged but updates the x-openwhisk.url field (previously always "not-used") to the actual gateway URL built from gwApiUrl + path. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/runtime/api/list.js | 22 +++++++++++++++++----- test/commands/runtime/api/list.test.js | 14 +++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/commands/runtime/api/list.js b/src/commands/runtime/api/list.js index 6f902bec..b0cbfc80 100644 --- a/src/commands/runtime/api/list.js +++ b/src/commands/runtime/api/list.js @@ -56,6 +56,23 @@ class ApiList extends RuntimeBaseCommand { const result = await ow.routes.list(options) + if (shouldOutputJson) { + const api = result.apis[0] + const apidoc = api.value.apidoc + const gwApiUrl = api.value.gwApiUrl + Object.keys(apidoc.paths).forEach(path => { + if (!path.startsWith('/')) return + Object.keys(apidoc.paths[path]).forEach(verb => { + const operation = apidoc.paths[path][verb] + if (operation['x-openwhisk']) { + operation['x-openwhisk'].url = `${gwApiUrl}${path}` + } + }) + }) + this.logJSON('', apidoc) + return + } + let data = [] result.apis.forEach(api => { // join the two arrays by reduce @@ -65,11 +82,6 @@ class ApiList extends RuntimeBaseCommand { }, data) }) - if (shouldOutputJson) { - this.logJSON('', data) - return - } - table(data, { Action: { minWidth: 10 }, Verb: { minWidth: 10 }, diff --git a/test/commands/runtime/api/list.test.js b/test/commands/runtime/api/list.test.js index 733b7497..fa848127 100644 --- a/test/commands/runtime/api/list.test.js +++ b/test/commands/runtime/api/list.test.js @@ -102,13 +102,17 @@ describe('instance methods', () => { .then(() => { const expectedJson = fixtureJson('api/list.json') const output = stdout.output.trim() - const jsonMatch = output.match(/\[[\s\S]*\]$/) + const jsonMatch = output.match(/\{[\s\S]*\}$/) const jsonOutput = jsonMatch ? jsonMatch[0] : output const parsed = JSON.parse(jsonOutput) - // should include URL field built from gwApiUrl - expect(parsed).toBeInstanceOf(Array) - expect(parsed[0]).toHaveProperty('URL') - expect(parsed[0].URL).toContain(expectedJson.apis[0].value.gwApiUrl) + const { apis } = expectedJson + const gwApiUrl = apis[0].value.gwApiUrl + // apidoc structure preserved + expect(parsed.basePath).toEqual(apis[0].value.apidoc.basePath) + expect(parsed.info).toMatchObject(apis[0].value.apidoc.info) + expect(parsed.swagger).toEqual(apis[0].value.apidoc.swagger) + // x-openwhisk.url updated with actual gateway URL (was "not-used") + expect(parsed.paths['/mypath'].get['x-openwhisk'].url).toEqual(`${gwApiUrl}/mypath`) }) .finally(() => { stdout.stop() From 5432232504c69563dac03cb84fe781e5204404d4 Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 13 Apr 2026 18:18:18 +0800 Subject: [PATCH 3/8] test: add coverage for operation without x-openwhisk in api list --json Co-Authored-By: Claude Sonnet 4.6 --- test/commands/runtime/api/list.test.js | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/commands/runtime/api/list.test.js b/test/commands/runtime/api/list.test.js index fa848127..0a075872 100644 --- a/test/commands/runtime/api/list.test.js +++ b/test/commands/runtime/api/list.test.js @@ -119,6 +119,44 @@ describe('instance methods', () => { }) }) + test('--json flag, operation without x-openwhisk leaves url unchanged', () => { + rtLib.mockResolved(rtAction, { + apis: [{ + value: { + gwApiUrl: 'https://example.com/api', + apidoc: { + basePath: '/test', + info: { title: 'test', version: '1.0.0' }, + swagger: '2.0', + paths: { + '/mypath': { + get: { + operationId: 'testOp', + responses: { default: { description: 'Default response' } } + // no x-openwhisk field + } + } + } + } + } + }] + }) + stdout.stop() + stdout.start() + const cmd = new TheCommand(['--json']) + return cmd.run() + .then(() => { + const output = stdout.output.trim() + const jsonMatch = output.match(/\{[\s\S]*\}$/) + const parsed = JSON.parse(jsonMatch ? jsonMatch[0] : output) + // operation without x-openwhisk should be left intact + expect(parsed.paths['/mypath'].get).not.toHaveProperty('x-openwhisk') + }) + .finally(() => { + stdout.stop() + }) + }) + test('error, throws exception', async () => { rtLib.mockRejected(rtAction, new Error('an error')) const error = ['failed to list the api', new Error('an error')] From d7200681d86b9efdfd1db0b50d9cefc76aad6d85 Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:02:32 +0800 Subject: [PATCH 4/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/commands/runtime/api/list.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/commands/runtime/api/list.js b/src/commands/runtime/api/list.js index b0cbfc80..302deeed 100644 --- a/src/commands/runtime/api/list.js +++ b/src/commands/runtime/api/list.js @@ -57,6 +57,11 @@ class ApiList extends RuntimeBaseCommand { const result = await ow.routes.list(options) if (shouldOutputJson) { + if (!result.apis || result.apis.length === 0) { + this.logJSON('', {}) + return + } + const api = result.apis[0] const apidoc = api.value.apidoc const gwApiUrl = api.value.gwApiUrl From 1a8927698f97b0b36ddf2f080ecb4a20bc5a057e Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:04:10 +0800 Subject: [PATCH 5/8] Update src/commands/runtime/api/list.js Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/commands/runtime/api/list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/runtime/api/list.js b/src/commands/runtime/api/list.js index 302deeed..f79b06c3 100644 --- a/src/commands/runtime/api/list.js +++ b/src/commands/runtime/api/list.js @@ -59,8 +59,8 @@ class ApiList extends RuntimeBaseCommand { if (shouldOutputJson) { if (!result.apis || result.apis.length === 0) { this.logJSON('', {}) - return - } + const api = result.apis[0] + if (!api || !api.value) { const api = result.apis[0] const apidoc = api.value.apidoc From 354b3486ebaa7ff7af83cf9ca9146852d3ac48fb Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:04:20 +0800 Subject: [PATCH 6/8] Update src/commands/runtime/api/list.js Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/commands/runtime/api/list.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/runtime/api/list.js b/src/commands/runtime/api/list.js index f79b06c3..9537985c 100644 --- a/src/commands/runtime/api/list.js +++ b/src/commands/runtime/api/list.js @@ -60,9 +60,13 @@ class ApiList extends RuntimeBaseCommand { if (!result.apis || result.apis.length === 0) { this.logJSON('', {}) const api = result.apis[0] + const api = result.apis && result.apis[0] if (!api || !api.value) { - - const api = result.apis[0] + this.logJSON('', {}) + return + } + const apidoc = api.value.apidoc + const gwApiUrl = api.value.gwApiUrl const apidoc = api.value.apidoc const gwApiUrl = api.value.gwApiUrl Object.keys(apidoc.paths).forEach(path => { From 9262b84b48889b406cfe1e28044c803a389421b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:06:52 +0000 Subject: [PATCH 7/8] test: add coverage for --json flag with empty apis result Agent-Logs-Url: https://github.com/adobe/aio-cli-plugin-runtime/sessions/4e07e478-6839-4941-897b-fe9cf27235e3 Co-authored-by: shazron <36107+shazron@users.noreply.github.com> --- test/commands/runtime/api/list.test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/commands/runtime/api/list.test.js b/test/commands/runtime/api/list.test.js index 0a075872..dcfadf7e 100644 --- a/test/commands/runtime/api/list.test.js +++ b/test/commands/runtime/api/list.test.js @@ -119,6 +119,23 @@ describe('instance methods', () => { }) }) + test('--json flag, empty apis array returns empty object', () => { + rtLib.mockResolved(rtAction, { apis: [] }) + stdout.stop() + stdout.start() + const cmd = new TheCommand(['--json']) + return cmd.run() + .then(() => { + const output = stdout.output.trim() + const jsonMatch = output.match(/\{[\s\S]*\}$/) + const parsed = JSON.parse(jsonMatch ? jsonMatch[0] : output) + expect(parsed).toEqual({}) + }) + .finally(() => { + stdout.stop() + }) + }) + test('--json flag, operation without x-openwhisk leaves url unchanged', () => { rtLib.mockResolved(rtAction, { apis: [{ From c564b4f740feffc3d60a0db5fb473a8d1e0cecbf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:08:43 +0000 Subject: [PATCH 8/8] fix: clean up duplicate declarations in --json branch of api list Agent-Logs-Url: https://github.com/adobe/aio-cli-plugin-runtime/sessions/4e07e478-6839-4941-897b-fe9cf27235e3 Co-authored-by: shazron <36107+shazron@users.noreply.github.com> --- src/commands/runtime/api/list.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/commands/runtime/api/list.js b/src/commands/runtime/api/list.js index 9537985c..091dcceb 100644 --- a/src/commands/runtime/api/list.js +++ b/src/commands/runtime/api/list.js @@ -57,9 +57,6 @@ class ApiList extends RuntimeBaseCommand { const result = await ow.routes.list(options) if (shouldOutputJson) { - if (!result.apis || result.apis.length === 0) { - this.logJSON('', {}) - const api = result.apis[0] const api = result.apis && result.apis[0] if (!api || !api.value) { this.logJSON('', {}) @@ -67,8 +64,6 @@ class ApiList extends RuntimeBaseCommand { } const apidoc = api.value.apidoc const gwApiUrl = api.value.gwApiUrl - const apidoc = api.value.apidoc - const gwApiUrl = api.value.gwApiUrl Object.keys(apidoc.paths).forEach(path => { if (!path.startsWith('/')) return Object.keys(apidoc.paths[path]).forEach(verb => {