From a99c3958016b288d642d8b5c38126f1cc46a38ee Mon Sep 17 00:00:00 2001 From: Andrew Molina <44038475+amolina-adobe@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:19:30 -0600 Subject: [PATCH] feat(legacy): CEXT-3832 - remove legacy mesh references (#190) * feat(legacy): CEXT-3832 - remove legacy mesh refs from create command * test(legacy): CEXT-3832 - update tests for create command * feat(legacy): CEXT-3832 - hide api key information from create command * feat(legacy): CEXT-3832 remove legacy mesh references from describe command * feat(legacy): CEXT-3832 update tests for describe command * feat(legacy): CEXT-3832 remove legacy mesh references from status command * refactor(legacy): CEXT-3832 apply code review suggestions to status messages * refactor(legacy): CEXT-3832 add unit tests for status command * feat(legacy)!: remove api key creation from create and delete commands * chore(legacy): bump major version following deprecation of legacy mesh and api key removal --------- Co-authored-by: Sumaiya <108254100+ajazsumaiya@users.noreply.github.com> --- package.json | 2 +- .../api-mesh/__tests__/create.test.js | 255 +----------------- .../api-mesh/__tests__/delete.test.js | 90 +------ .../api-mesh/__tests__/describe.test.js | 74 +---- .../api-mesh/__tests__/status.test.js | 97 +++++++ src/commands/api-mesh/create.js | 50 +--- src/commands/api-mesh/delete.js | 26 +- src/commands/api-mesh/describe.js | 35 +-- src/commands/api-mesh/status.js | 151 +++++------ src/constants.js | 8 +- src/lib/devConsole.js | 56 +--- src/urlBuilder.js | 45 +--- 12 files changed, 206 insertions(+), 683 deletions(-) create mode 100644 src/commands/api-mesh/__tests__/status.test.js diff --git a/package.json b/package.json index c3747935..21a141b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/aio-cli-plugin-api-mesh", - "version": "3.9.4", + "version": "4.0.0-beta", "description": "Adobe I/O CLI plugin to develop and manage API mesh sources", "keywords": [ "oclif-plugin" diff --git a/src/commands/api-mesh/__tests__/create.test.js b/src/commands/api-mesh/__tests__/create.test.js index 03653011..0eb94842 100644 --- a/src/commands/api-mesh/__tests__/create.test.js +++ b/src/commands/api-mesh/__tests__/create.test.js @@ -42,8 +42,6 @@ const { const { getMesh, createMesh, - createAPIMeshCredentials, - subscribeCredentialToMeshService, getTenantFeatures, getPublicEncryptionKey, } = require('../../../lib/devConsole'); @@ -122,17 +120,8 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: sampleCreateMeshConfig.meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); - createAPIMeshCredentials.mockResolvedValue({ - apiKey: 'dummy_api_key', - id: 'dummy_id', - }); - - subscribeCredentialToMeshService.mockResolvedValue(['dummy_service']); - getMesh.mockResolvedValue({ meshId: 'dummy_id', meshURL: '', @@ -171,11 +160,7 @@ describe('create command tests', () => { }); const output = await CreateCommand.run(); expect(output).toHaveProperty('mesh'); - expect(output).toHaveProperty('apiKey'); - expect(output).toHaveProperty('sdkList'); expect(output.mesh).toEqual(expect.objectContaining({ meshId: 'dummy_mesh_id' })); - expect(output.apiKey).toEqual('dummy_api_key'); - expect(output.sdkList).toEqual(['dummy_service']); }); test('snapshot create command description', () => { @@ -244,8 +229,6 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: meshConfigWithComposerFiles.meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); parseSpy.mockResolvedValueOnce({ @@ -259,7 +242,6 @@ describe('create command tests', () => { expect(output).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "files": [ @@ -303,9 +285,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -365,7 +344,6 @@ describe('create command tests', () => { `); expect(runResult).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "sources": [ @@ -381,9 +359,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); expect(logSpy.mock.calls).toMatchInlineSnapshot(` @@ -405,29 +380,7 @@ describe('create command tests', () => { "******************************************************************************************************", ], [ - "Successfully created API Key %s", - "dummy_api_key", - ], - [ - "Successfully subscribed API Key %s to API Mesh service", - "dummy_api_key", - ], - [ - " - API Mesh now runs at the edge and legacy mesh URLs will be deprecated. - Use the following link to find more information on how to migrate your mesh:", - ], - [ - "https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration - ", - ], - [ - "Legacy Mesh Endpoint: %s", - "https://graph.adobe.io/api/dummy_mesh_id/graphql?api_key=dummy_api_key", - ], - [ - "Edge Mesh Endpoint: %s - ", + "Mesh Endpoint: %s", "https://edge-sandbox-graph.adobe.io/api/dummy_mesh_id/graphql", ], ] @@ -471,7 +424,6 @@ describe('create command tests', () => { expect(runResult).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "sources": [ @@ -487,9 +439,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); expect(logSpy.mock.calls).toMatchInlineSnapshot(` @@ -511,29 +460,7 @@ describe('create command tests', () => { "******************************************************************************************************", ], [ - "Successfully created API Key %s", - "dummy_api_key", - ], - [ - "Successfully subscribed API Key %s to API Mesh service", - "dummy_api_key", - ], - [ - " - API Mesh now runs at the edge and legacy mesh URLs will be deprecated. - Use the following link to find more information on how to migrate your mesh:", - ], - [ - "https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration - ", - ], - [ - "Legacy Mesh Endpoint: %s", - "https://tigraph.adobe.io/dummy_mesh_id/graphql", - ], - [ - "Edge Mesh Endpoint: %s - ", + "Mesh Endpoint: %s", "https://edge-sandbox-graph.adobe.io/api/dummy_mesh_id/graphql", ], ] @@ -590,96 +517,6 @@ describe('create command tests', () => { `); }); - test.skip('should fail if create api credential api has failed', async () => { - createAPIMeshCredentials.mockRejectedValue(new Error('create api credential api failed')); - - const runResult = CreateCommand.run(); - - await expect(runResult).rejects.toEqual( - new Error( - 'Unable to create a mesh. Please check the mesh configuration file and try again. If the error persists please contact support. RequestId: dummy_request_id', - ), - ); - expect(logSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "******************************************************************************************************", - ], - [ - "Your mesh is being provisioned. Wait a few minutes before checking the status of your mesh %s", - "dummy_mesh_id", - ], - [ - "To check the status of your mesh, run:", - ], - [ - "aio api-mesh:status", - ], - [ - "******************************************************************************************************", - ], - [ - "create api credential api failed", - ], - ] - `); - expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "Unable to create a mesh. Please check the mesh configuration file and try again. If the error persists please contact support. RequestId: dummy_request_id", - ], - ] - `); - }); - - test.skip('should fail if subscribe credential to mesh service api has failed', async () => { - subscribeCredentialToMeshService.mockRejectedValueOnce( - new Error('subscribe credential to mesh service api failed'), - ); - - const runResult = CreateCommand.run(); - - await expect(runResult).rejects.toEqual( - new Error( - 'Unable to create a mesh. Please check the mesh configuration file and try again. If the error persists please contact support. RequestId: dummy_request_id', - ), - ); - expect(logSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "******************************************************************************************************", - ], - [ - "Your mesh is being provisioned. Wait a few minutes before checking the status of your mesh %s", - "dummy_mesh_id", - ], - [ - "To check the status of your mesh, run:", - ], - [ - "aio api-mesh:status", - ], - [ - "******************************************************************************************************", - ], - [ - "Successfully created API Key %s", - "dummy_api_key", - ], - [ - "subscribe credential to mesh service api failed", - ], - ] - `); - expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "Unable to create a mesh. Please check the mesh configuration file and try again. If the error persists please contact support. RequestId: dummy_request_id", - ], - ] - `); - }); - test('should not ask for confirmation if autoConfirmAction is provided', async () => { parseSpy.mockResolvedValueOnce({ args: { file: 'src/commands/__fixtures__/sample_mesh.json' }, @@ -715,29 +552,7 @@ describe('create command tests', () => { "******************************************************************************************************", ], [ - "Successfully created API Key %s", - "dummy_api_key", - ], - [ - "Successfully subscribed API Key %s to API Mesh service", - "dummy_api_key", - ], - [ - " - API Mesh now runs at the edge and legacy mesh URLs will be deprecated. - Use the following link to find more information on how to migrate your mesh:", - ], - [ - "https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration - ", - ], - [ - "Legacy Mesh Endpoint: %s", - "https://graph.adobe.io/api/dummy_mesh_id/graphql?api_key=dummy_api_key", - ], - [ - "Edge Mesh Endpoint: %s - ", + "Mesh Endpoint: %s", "https://edge-sandbox-graph.adobe.io/api/dummy_mesh_id/graphql", ], ] @@ -774,11 +589,7 @@ describe('create command tests', () => { }); const output = await CreateCommand.run(); expect(output).toHaveProperty('mesh'); - expect(output).toHaveProperty('apiKey'); - expect(output).toHaveProperty('sdkList'); expect(output.mesh).toEqual(expect.objectContaining({ meshId: 'dummy_mesh_id' })); - expect(output.apiKey).toEqual('dummy_api_key'); - expect(output.sdkList).toEqual(['dummy_service']); }); test('should return error if the mesh has placeholders and env file provided using --env flag is not found', async () => { @@ -946,7 +757,6 @@ describe('create command tests', () => { expect(promptConfirm).toHaveBeenCalledWith('Are you sure you want to create a mesh?'); expect(runResult).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "sources": [ @@ -962,9 +772,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -1026,8 +833,6 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); parseSpy.mockResolvedValueOnce({ @@ -1086,7 +891,6 @@ describe('create command tests', () => { expect(output).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "files": [ @@ -1117,9 +921,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -1295,8 +1096,6 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); parseSpy.mockResolvedValueOnce({ @@ -1348,7 +1147,6 @@ describe('create command tests', () => { `); expect(output).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "files": [ @@ -1379,9 +1177,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -1434,8 +1229,6 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); const output = await CreateCommand.run(); @@ -1483,7 +1276,6 @@ describe('create command tests', () => { expect(output).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "files": [ @@ -1514,9 +1306,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -1568,8 +1357,6 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); const output = await CreateCommand.run(); @@ -1616,7 +1403,6 @@ describe('create command tests', () => { `); expect(output).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "files": [ @@ -1647,9 +1433,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -1688,8 +1471,6 @@ describe('create command tests', () => { meshId: 'dummy_mesh_id', meshConfig: meshConfig, }, - apiKey: 'dummy_api_key', - sdkList: ['dummy_service'], }); parseSpy.mockResolvedValueOnce({ @@ -1747,7 +1528,6 @@ describe('create command tests', () => { `); expect(output).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "files": [ @@ -1778,9 +1558,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -1891,12 +1668,7 @@ describe('create command tests', () => { await CreateCommand.run(); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Legacy Mesh Endpoint:'), - 'https://graph.adobe.io/api/dummy_mesh_id/graphql?api_key=dummy_api_key', - ); - - expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Edge Mesh Endpoint:'), + expect.stringContaining('Mesh Endpoint:'), 'https://edge-graph.adobe.io/api/dummy_mesh_id/graphql', ); }); @@ -1916,12 +1688,7 @@ describe('create command tests', () => { await CreateCommand.run(); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Legacy Mesh Endpoint:'), - 'https://graph.adobe.io/api/dummy_mesh_id/graphql?api_key=dummy_api_key', - ); - - expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Edge Mesh Endpoint:'), + expect.stringContaining('Mesh Endpoint:'), 'https://edge-sandbox-graph.adobe.io/api/dummy_mesh_id/graphql', ); }); @@ -2001,7 +1768,6 @@ describe('create command tests', () => { const runResult = await CreateCommand.run(); expect(runResult).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "sources": [ @@ -2017,9 +1783,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -2075,7 +1838,6 @@ describe('create command tests', () => { const runResult = await CreateCommand.run(); expect(runResult).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "sources": [ @@ -2091,9 +1853,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); @@ -2116,7 +1875,6 @@ describe('create command tests', () => { const runResult = await CreateCommand.run(); expect(runResult).toMatchInlineSnapshot(` { - "apiKey": "dummy_api_key", "mesh": { "meshConfig": { "sources": [ @@ -2132,9 +1890,6 @@ describe('create command tests', () => { }, "meshId": "dummy_mesh_id", }, - "sdkList": [ - "dummy_service", - ], } `); }); diff --git a/src/commands/api-mesh/__tests__/delete.test.js b/src/commands/api-mesh/__tests__/delete.test.js index 00f96cba..ae6c3d0d 100644 --- a/src/commands/api-mesh/__tests__/delete.test.js +++ b/src/commands/api-mesh/__tests__/delete.test.js @@ -169,10 +169,6 @@ describe('delete command tests', () => { "Successfully deleted mesh %s", "mesh_id", ], - [ - "Successfully unsubscribed API Key %s", - "dummy_client_id", - ], ] `); expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`[]`); @@ -204,67 +200,7 @@ describe('delete command tests', () => { `); }); - test('should delete mesh but fail to unsubscribe if unable to get api key', async () => { - getApiKeyCredential.mockRejectedValueOnce(new Error('unable to get api key')); - - const runResult = DeleteCommand.run(); - - await expect(runResult).rejects.toEqual( - new Error( - 'Unable to delete mesh. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id', - ), - ); - expect(logSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "Successfully deleted mesh %s", - "mesh_id", - ], - [ - "unable to get api key", - ], - ] - `); - expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "Unable to delete mesh. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id", - ], - ] - `); - }); - - test('should delete mesh but fail to unsubscribe if unsubscribe api failed', async () => { - unsubscribeCredentialFromMeshService.mockRejectedValueOnce(new Error('unsubscribe api failed')); - - const runResult = DeleteCommand.run(); - - await expect(runResult).rejects.toEqual( - new Error( - 'Unable to delete mesh. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id', - ), - ); - expect(logSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "Successfully deleted mesh %s", - "mesh_id", - ], - [ - "unsubscribe api failed", - ], - ] - `); - expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - "Unable to delete mesh. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id", - ], - ] - `); - }); - - test('should delete mesh and unsubscribe if correct args are provided', async () => { + test('should delete mesh if correct args are provided', async () => { const runResult = await DeleteCommand.run(); expect(initRequestId).toHaveBeenCalled(); @@ -283,35 +219,13 @@ describe('delete command tests', () => { ], ] `); - expect(getApiKeyCredential.mock.calls).toMatchInlineSnapshot(` - [ - [ - "1234", - "5678", - "123456789", - ], - ] - `); - expect(unsubscribeCredentialFromMeshService.mock.calls).toMatchInlineSnapshot(` - [ - [ - "1234", - "5678", - "123456789", - "dummy_integration_id", - ], - ] - `); + expect(logSpy.mock.calls).toMatchInlineSnapshot(` [ [ "Successfully deleted mesh %s", "mesh_id", ], - [ - "Successfully unsubscribed API Key %s", - "dummy_client_id", - ], ] `); expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`[]`); diff --git a/src/commands/api-mesh/__tests__/describe.test.js b/src/commands/api-mesh/__tests__/describe.test.js index 0e656318..a1f48414 100644 --- a/src/commands/api-mesh/__tests__/describe.test.js +++ b/src/commands/api-mesh/__tests__/describe.test.js @@ -186,27 +186,9 @@ describe('describe command tests', () => { "dummy_meshId", ], [ - " - API Mesh now runs at the edge and legacy mesh URLs will be deprecated. - Use the following link to find more information on how to migrate your mesh:", - ], - [ - "https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration - ", - ], - [ - "Legacy Mesh Endpoint: %s", - "https://graph.adobe.io/api/dummy_meshId/graphql", - ], - [ - "Edge Mesh Endpoint: %s - ", + "Mesh Endpoint: %s", "https://edge-sandbox-graph.adobe.io/api/dummy_meshId/graphql", ], - [ - "Update your mesh before using the edge mesh endpoint. - You can validate your edge mesh status using the aio api-mesh status command.", - ], ] `); expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`[]`); @@ -251,27 +233,9 @@ describe('describe command tests', () => { "dummy_meshId", ], [ - " - API Mesh now runs at the edge and legacy mesh URLs will be deprecated. - Use the following link to find more information on how to migrate your mesh:", - ], - [ - "https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration - ", - ], - [ - "Legacy Mesh Endpoint: %s", - "https://graph.adobe.io/api/dummy_meshId/graphql?api_key=dummy_apiKey", - ], - [ - "Edge Mesh Endpoint: %s - ", + "Mesh Endpoint: %s", "https://edge-sandbox-graph.adobe.io/api/dummy_meshId/graphql", ], - [ - "Update your mesh before using the edge mesh endpoint. - You can validate your edge mesh status using the aio api-mesh status command.", - ], ] `); }); @@ -320,27 +284,9 @@ describe('describe command tests', () => { "dummy_meshId", ], [ - " - API Mesh now runs at the edge and legacy mesh URLs will be deprecated. - Use the following link to find more information on how to migrate your mesh:", - ], - [ - "https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration - ", - ], - [ - "Legacy Mesh Endpoint: %s", - "https://tigraph.adobe.io/dummy_meshId/graphql", - ], - [ - "Edge Mesh Endpoint: %s - ", + "Mesh Endpoint: %s", "https://edge-sandbox-graph.adobe.io/api/dummy_meshId/graphql", ], - [ - "Update your mesh before using the edge mesh endpoint. - You can validate your edge mesh status using the aio api-mesh status command.", - ], ] `); expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`[]`); @@ -361,12 +307,7 @@ describe('describe command tests', () => { await DescribeCommand.run(); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Legacy Mesh Endpoint:'), - 'https://graph.adobe.io/api/dummy_meshId/graphql?api_key=dummy_apiKey', - ); - - expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Edge Mesh Endpoint:'), + expect.stringContaining('Mesh Endpoint:'), 'https://edge-graph.adobe.io/api/dummy_meshId/graphql', ); }); @@ -386,12 +327,7 @@ describe('describe command tests', () => { await DescribeCommand.run(); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Legacy Mesh Endpoint:'), - 'https://graph.adobe.io/api/dummy_meshId/graphql?api_key=dummy_apiKey', - ); - - expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining('Edge Mesh Endpoint:'), + expect.stringContaining('Mesh Endpoint:'), 'https://edge-sandbox-graph.adobe.io/api/dummy_meshId/graphql', ); }); diff --git a/src/commands/api-mesh/__tests__/status.test.js b/src/commands/api-mesh/__tests__/status.test.js new file mode 100644 index 00000000..9d6da9fd --- /dev/null +++ b/src/commands/api-mesh/__tests__/status.test.js @@ -0,0 +1,97 @@ +const StatusCommand = require('../status'); + +// Initialize mock values +const mockOrg = { id: '1234', code: 'CODE1234@AdobeOrg', name: 'ORG01', type: 'entp' }; +const mockProject = { id: '5678', title: 'Project01' }; +const mockWorkspace = { id: '123456789', title: 'Workspace01' }; +const mockMeshId = '00000000-0000-0000-0000-000000000000'; +global.requestId = 'dummy_request_id'; + +// Create mock modules and functions +jest.mock('../../../lib/devConsole'); +jest.mock('../../../helpers'); +const parseSpy = jest.spyOn(StatusCommand.prototype, 'parse'); +const logSpy = jest.spyOn(StatusCommand.prototype, 'log'); +const errorLogSpy = jest.spyOn(StatusCommand.prototype, 'error'); + +// Prepare mocks +const { initSdk } = require('../../../helpers'); +const { getMeshId, getMesh, getMeshDeployments } = require('../../../lib/devConsole'); +initSdk.mockResolvedValue({ + imsOrgId: mockOrg.id, + imsOrgCode: mockOrg.code, + projectId: mockProject.id, + workspaceId: mockWorkspace.id, + workspaceName: mockWorkspace.title, + orgName: mockOrg.name, + projectName: mockProject.title, +}); +parseSpy.mockResolvedValue({ + flags: {}, +}); + +describe('status command tests', () => { + beforeEach(() => { + jest.clearAllMocks(); + getMeshId.mockResolvedValue(mockMeshId); + getMesh.mockResolvedValue({ meshStatus: null }); + }); + + describe('use mesh build status', () => { + test.each([ + ['pending', 'Mesh is awaiting processing.'], + ['building', 'Mesh is currently building. Wait a few minutes before checking again.'], + ['error', 'Mesh build has errors.'], + ['not-a-real-status', 'Mesh status is not available. Wait a few minutes and try again.'], + [null, 'Mesh status is not available. Wait a few minutes and try again.'], + ])( + 'should output correct message when mesh build status is "%s"', + async (meshBuildStatus, expectedMessage) => { + getMesh.mockResolvedValue({ meshStatus: meshBuildStatus }); + await StatusCommand.run(); + expect(logSpy).toHaveBeenCalledWith(expectedMessage); + }, + ); + }); + + describe('use mesh deployment status', () => { + beforeEach(() => { + getMesh.mockResolvedValue({ meshStatus: 'success' }); + getMeshDeployments.mockResolvedValue({ + status: null, + meshId: mockMeshId, + error: null, + }); + }); + + test.each([ + ['provisioning', 'Currently provisioning your mesh. Wait a few minutes and try again.'], + ['de-provisioning', 'Currently de-provisioning your mesh. Wait a few minutes and try again.'], + ['success', 'Mesh was built successfully.'], + ['error', 'Mesh build has errors.'], + ['not-a-real-status', 'Mesh status is not available. Wait a few minutes and try again.'], + [null, 'Mesh status is not available. Wait a few minutes and try again.'], + ])( + 'should output correct message when mesh deployment status is "%s"', + async (meshDeployStatus, expectedMessage) => { + getMeshDeployments.mockResolvedValue({ + status: meshDeployStatus, + meshId: mockMeshId, + error: null, + }); + await StatusCommand.run(); + expect(logSpy).toHaveBeenCalledWith(expectedMessage); + }, + ); + }); + + describe('unexpected error', () => { + test('should output mesh not found error when mesh does not exist', async () => { + getMeshId.mockResolvedValue(null); + const expectedMessage = `Unable to get mesh status. No mesh found for Org(${mockOrg.id}) -> Project(${mockProject.id}) -> Workspace(${mockWorkspace.id}). Please check the details and try again.`; + const runResult = StatusCommand.run(); + await expect(runResult).rejects.toEqual(new Error(expectedMessage)); + expect(errorLogSpy).toHaveBeenCalledWith(expectedMessage); + }); + }); +}); diff --git a/src/commands/api-mesh/create.js b/src/commands/api-mesh/create.js index 7629579c..87a1791d 100644 --- a/src/commands/api-mesh/create.js +++ b/src/commands/api-mesh/create.js @@ -10,7 +10,6 @@ governing permissions and limitations under the License. */ const { Command } = require('@oclif/core'); -const chalk = require('chalk'); const { initSdk, initRequestId, promptConfirm, importFiles } = require('../../helpers'); const logger = require('../../classes/logger'); const { @@ -28,7 +27,7 @@ const { encryptSecrets, } = require('../../utils'); const { createMesh, getPublicEncryptionKey } = require('../../lib/devConsole'); -const { buildEdgeMeshUrl, buildMeshUrl } = require('../../urlBuilder'); +const { buildMeshUrl } = require('../../urlBuilder'); class CreateCommand extends Command { static args = [{ name: 'file' }]; @@ -131,7 +130,7 @@ class CreateCommand extends Command { if (shouldContinue) { try { - const { mesh, apiKey, sdkList } = await createMesh( + const { mesh } = await createMesh( imsOrgId, projectId, workspaceId, @@ -155,47 +154,12 @@ class CreateCommand extends Command { '******************************************************************************************************', ); - if (apiKey) { - this.log('Successfully created API Key %s', apiKey); - - if (sdkList) { - this.log('Successfully subscribed API Key %s to API Mesh service', apiKey); - - const meshUrl = await buildMeshUrl( - imsOrgId, - projectId, - workspaceId, - workspaceName, - mesh.meshId, - apiKey, - ); - - const edgeMeshUrl = buildEdgeMeshUrl(mesh.meshId, workspaceName); - this.log( - chalk.bgYellow( - `\nAPI Mesh now runs at the edge and legacy mesh URLs will be deprecated.\nUse the following link to find more information on how to migrate your mesh:`, - ), - ); - this.log( - chalk.underline.blue( - 'https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration\n', - ), - ); - this.log('Legacy Mesh Endpoint: %s', meshUrl); - this.log(chalk.bold('Edge Mesh Endpoint: %s\n'), edgeMeshUrl); - } else { - this.log('Unable to subscribe API Key %s to API Mesh service', apiKey); - } - } else { - this.log('Unable to create API Key'); - } + const meshUrl = buildMeshUrl(mesh.meshId, workspaceName); + this.log('Mesh Endpoint: %s', meshUrl); + // When renaming the return values, make sure to make necessary changes to - // template adobe/generator-app-api-mesh since it relies on "mesh" & "apiKey" - return { - apiKey, - sdkList, - mesh, - }; + // template adobe/generator-app-api-mesh since it relies on "mesh" + return { mesh }; } else { this.error(`Unable to create a mesh. Please try again. RequestId: ${global.requestId}`, { exit: false, diff --git a/src/commands/api-mesh/delete.js b/src/commands/api-mesh/delete.js index d6aab52f..0636e25d 100644 --- a/src/commands/api-mesh/delete.js +++ b/src/commands/api-mesh/delete.js @@ -14,12 +14,7 @@ const { Command } = require('@oclif/command'); const logger = require('../../classes/logger'); const { initSdk, initRequestId, promptConfirm } = require('../../helpers'); const { ignoreCacheFlag, autoConfirmActionFlag } = require('../../utils'); -const { - getMeshId, - deleteMesh, - getApiKeyCredential, - unsubscribeCredentialFromMeshService, -} = require('../../lib/devConsole'); +const { getMeshId, deleteMesh } = require('../../lib/devConsole'); require('dotenv').config(); @@ -69,25 +64,6 @@ class DeleteCommand extends Command { if (deleteMeshResponse) { this.log('Successfully deleted mesh %s', meshId); - const credential = await getApiKeyCredential(imsOrgId, projectId, workspaceId); - - if (credential) { - const newSDKList = await unsubscribeCredentialFromMeshService( - imsOrgId, - projectId, - workspaceId, - credential.id_integration, - ); - - if (newSDKList) { - this.log('Successfully unsubscribed API Key %s', credential.client_id); - } else { - this.log('Unable to unsubscribe API Key %s', credential.client_id); - } - } else { - this.log('No API Key found to unsubscribe'); - } - return deleteMeshResponse; } else { throw new Error('Unable to delete mesh'); diff --git a/src/commands/api-mesh/describe.js b/src/commands/api-mesh/describe.js index fe63b85c..e4a1a3c3 100644 --- a/src/commands/api-mesh/describe.js +++ b/src/commands/api-mesh/describe.js @@ -10,13 +10,11 @@ governing permissions and limitations under the License. */ const { Command } = require('@oclif/command'); -const chalk = require('chalk'); - const logger = require('../../classes/logger'); const { initSdk, initRequestId } = require('../../helpers'); const { ignoreCacheFlag } = require('../../utils'); const { describeMesh } = require('../../lib/devConsole'); -const { buildMeshUrl, buildEdgeMeshUrl } = require('../../urlBuilder'); +const { buildMeshUrl } = require('../../urlBuilder'); require('dotenv').config(); @@ -40,42 +38,17 @@ class DescribeCommand extends Command { const meshDetails = await describeMesh(imsOrgId, projectId, workspaceId, workspaceName); if (meshDetails) { - const { meshId, apiKey } = meshDetails; + const { meshId } = meshDetails; if (meshId) { - const meshUrl = await buildMeshUrl( - imsOrgId, - projectId, - workspaceId, - workspaceName, - meshId, - apiKey, - ); - + const meshUrl = buildMeshUrl(meshId, workspaceName); this.log('Successfully retrieved mesh details \n'); this.log('Org ID: %s', imsOrgId); this.log('Project ID: %s', projectId); this.log('Workspace ID: %s', workspaceId); this.log('Mesh ID: %s', meshId); + this.log('Mesh Endpoint: %s', meshUrl); - const edgeMeshUrl = buildEdgeMeshUrl(meshId, workspaceName); - this.log( - chalk.bgYellow( - `\nAPI Mesh now runs at the edge and legacy mesh URLs will be deprecated.\nUse the following link to find more information on how to migrate your mesh:`, - ), - ); - this.log( - chalk.underline.blue( - 'https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration\n', - ), - ); - this.log('Legacy Mesh Endpoint: %s', meshUrl); - this.log(chalk.bold('Edge Mesh Endpoint: %s\n'), edgeMeshUrl); - this.log( - chalk.bgYellow( - 'Update your mesh before using the edge mesh endpoint.\nYou can validate your edge mesh status using the aio api-mesh status command.', - ), - ); return meshDetails; } else { logger.error( diff --git a/src/commands/api-mesh/status.js b/src/commands/api-mesh/status.js index 8d102d34..3568317e 100644 --- a/src/commands/api-mesh/status.js +++ b/src/commands/api-mesh/status.js @@ -1,5 +1,4 @@ const { Command } = require('@oclif/core'); -const chalk = require('chalk'); const logger = require('../../classes/logger'); const { initRequestId, initSdk } = require('../../helpers'); @@ -28,123 +27,111 @@ class StatusCommand extends Command { try { meshId = await getMeshId(imsOrgId, projectId, workspaceId, workspaceName); } catch (err) { + // API Request failed this.log(err.message); this.error( `Unable to get mesh ID. Please check the details and try again. RequestId: ${global.requestId}`, ); } - if (meshId) { - try { - const mesh = await getMesh(imsOrgId, projectId, workspaceId, workspaceName, meshId); - this.log( - chalk.bgYellow( - `\nAPI Mesh now runs at the edge and legacy mesh URLs will be deprecated.\nUse the following link to find more information on how to migrate your mesh:`, - ), - ); - this.log( - chalk.underline.blue( - 'https://developer.adobe.com/graphql-mesh-gateway/mesh/release/migration\n', - ), - ); - const meshLabel = chalk.bold(`Legacy Mesh:`); - - this.log(''.padEnd(102, '*')); - this.displayMeshStatus(mesh, meshLabel); - await this.displayEdgeMeshStatus(mesh, imsOrgCode, projectId, workspaceId); - this.log(''.padEnd(102, '*')); - } catch (err) { - this.log(err.message); - this.error( - `Unable to get the mesh status. If the error persists please contact support. RequestId: ${global.requestId}`, - ); - } - } else { + // API Request succeeded, but mesh could not be found + if (!meshId) { this.error( `Unable to get mesh status. No mesh found for Org(${imsOrgId}) -> Project(${projectId}) -> Workspace(${workspaceId}). Please check the details and try again.`, ); } + + try { + const mesh = await getMesh(imsOrgId, projectId, workspaceId, workspaceName, meshId); + this.log(''.padEnd(102, '*')); + await this.displayMeshStatus(mesh, imsOrgCode, projectId, workspaceId); + this.log(''.padEnd(102, '*')); + } catch (err) { + // Error occurred while fetching/displaying the mesh status + this.error( + `Unable to get mesh status. If this error persists, contact support. RequestId: ${global.requestId}`, + ); + } } /** * Display the status of the mesh. * + * While the mesh is not successfully built, display a message based on the build status. + * Once the build is successful, display a message based on the deployment status. + * @param mesh + * @param imsOrgCode + * @param projectId + * @param workspaceId + * @returns {Promise} + */ + async displayMeshStatus(mesh, imsOrgCode, projectId, workspaceId) { + if (mesh.meshStatus !== 'success') { + this.displayMeshBuildStatus(mesh); + } else { + await this.displayMeshDeploymentStatus(mesh, imsOrgCode, projectId, workspaceId); + } + } + + /** + * Display the status of the mesh build. + * * @param mesh - Mesh data - * @param meshLabel - Label to display for the mesh based on the mesh type */ - displayMeshStatus(mesh, meshLabel = 'Your mesh') { + displayMeshBuildStatus(mesh) { switch (mesh.meshStatus) { - case 'success': - this.log(`${meshLabel} has been successfully built.`); - break; case 'pending': - this.log(`${meshLabel} is awaiting processing.`); + this.log('Mesh is awaiting processing.'); break; case 'building': - this.log( - `${meshLabel} is currently being provisioned. Please wait a few minutes before checking again.`, - ); + this.log('Mesh is currently building. Wait a few minutes before checking again.'); break; case 'error': - this.log( - meshLabel === 'Your mesh' - ? `${meshLabel} errored out with the following error.` - : `${meshLabel} build has errors.`, - ); + this.log('Mesh build has errors.'); this.log(mesh.error); break; + default: + this.log('Mesh status is not available. Wait a few minutes and try again.'); + break; } } /** - * Display the status of the edge mesh. + * Display the status of the mesh deployment. * - * While the mesh is not successfully built, the edge mesh status will match the legacy mesh status. - * Once the build is successful, the edge mesh status will reflect the deployment status * @param mesh * @param imsOrgCode * @param projectId * @param workspaceId * @returns {Promise} */ - async displayEdgeMeshStatus(mesh, imsOrgCode, projectId, workspaceId) { - const edgeMeshLabel = chalk.bold(`Edge Mesh:`); - const buildStatus = mesh.meshStatus; - - if (buildStatus !== 'success') { - this.displayMeshStatus(mesh, edgeMeshLabel); - } else { - const meshDeployments = await getMeshDeployments( - imsOrgCode, - projectId, - workspaceId, - mesh.meshId, - ); - - const edgeDeploymentStatus = String(meshDeployments.status).toLowerCase(); - - switch (edgeDeploymentStatus) { - case 'success': - this.log(`${edgeMeshLabel} has been successfully built.`); - break; - case 'provisioning': - this.log( - `${edgeMeshLabel} is currently being provisioned. Please wait a few minutes before checking again.`, - ); - break; - case 'de-provisioning': - this.log( - `${edgeMeshLabel} is currently being de-provisioned. Please wait a few minutes before checking again.`, - ); - break; - case 'error': - this.log(`${edgeMeshLabel} ${meshDeployments.error}`); - break; - default: - this.log( - `${edgeMeshLabel} status is not available. Please wait for a while and try again.`, - ); - } + async displayMeshDeploymentStatus(mesh, imsOrgCode, projectId, workspaceId) { + const meshDeployments = await getMeshDeployments( + imsOrgCode, + projectId, + workspaceId, + mesh.meshId, + ); + + const meshDeploymentStatus = String(meshDeployments.status).toLowerCase(); + + switch (meshDeploymentStatus) { + case 'provisioning': + this.log('Currently provisioning your mesh. Wait a few minutes and try again.'); + break; + case 'de-provisioning': + this.log('Currently de-provisioning your mesh. Wait a few minutes and try again.'); + break; + case 'success': + this.log('Mesh was built successfully.'); + break; + case 'error': + this.log('Mesh build has errors.'); + this.log(meshDeployments.error); + break; + default: + this.log('Mesh status is not available. Wait a few minutes and try again.'); + break; } } } diff --git a/src/constants.js b/src/constants.js index 48346aed..8b5749d8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -3,24 +3,22 @@ const { getCliEnv } = require('@adobe/aio-lib-env'); const clientEnv = getCliEnv(); const StageConstants = { - MULTITENANT_GRAPHQL_SERVER_BASE_URL: 'https://graph-stage.adobe.io/api', DEV_CONSOLE_BASE_URL: 'https://developers-stage.adobe.io/console', DEV_CONSOLE_API_KEY: 'adobe-api-manager-sms-stage', DEV_CONSOLE_TRANSPORTER_API_KEY: 'UDPWeb1', AIO_CLI_API_KEY: 'aio-cli-console-auth-stage', SMS_BASE_URL: 'https://graph-stage.adobe.io/api-admin', - EDGE_MESH_BASE_URL: 'https://edge-stage-graph.adobe.io/api', + MESH_BASE_URL: 'https://edge-stage-graph.adobe.io/api', }; const ProdConstants = { - MULTITENANT_GRAPHQL_SERVER_BASE_URL: 'https://graph.adobe.io/api', DEV_CONSOLE_BASE_URL: 'https://developers.adobe.io/console', DEV_CONSOLE_API_KEY: 'adobe-graph-prod', DEV_CONSOLE_TRANSPORTER_API_KEY: 'UDPWeb1', AIO_CLI_API_KEY: 'aio-cli-console-auth', SMS_BASE_URL: 'https://graph.adobe.io/api-admin', - EDGE_MESH_BASE_URL: 'https://edge-graph.adobe.io/api', - EDGE_MESH_SANDBOX_BASE_URL: 'https://edge-sandbox-graph.adobe.io/api', + MESH_BASE_URL: 'https://edge-graph.adobe.io/api', + MESH_SANDBOX_BASE_URL: 'https://edge-sandbox-graph.adobe.io/api', }; const envConstants = clientEnv === 'stage' ? StageConstants : ProdConstants; diff --git a/src/lib/devConsole.js b/src/lib/devConsole.js index fe915f27..50b729cf 100644 --- a/src/lib/devConsole.js +++ b/src/lib/devConsole.js @@ -90,21 +90,11 @@ const describeMesh = async (organizationId, projectId, workspaceId, workspaceNam logger.info('Response from getMeshId %s', meshId); - if (meshId) { - const credential = await getApiKeyCredential(organizationId, projectId, workspaceId); - - if (credential) { - return { meshId, apiKey: credential.client_id }; - } else { - logger.error('API Key credential not found on workspace'); - - return { meshId, apiKey: null }; - } - } else { - logger.error(`Unable to retrieve meshId.`); - + if (!meshId) { throw new Error(`Unable to retrieve meshId.`); } + + return { meshId }; } catch (error) { logger.error(error); @@ -270,42 +260,8 @@ const createMesh = async ( if (response && response.status === 201) { logger.info(`Mesh Config : ${objToString(response, ['data'])}`); - let sdkList = []; - let credential; - let isApiKeyNew = false; - - credential = await getApiKeyCredential(organizationId, projectId, workspaceId); - if (!credential) { - logger.info('API Key credential not found on workspace'); - - // try to create a new API key credential - credential = await createAPIMeshCredentials(organizationId, projectId, workspaceId); - isApiKeyNew = true; - } - // subscribe the credential to API mesh service - sdkList = await subscribeCredentialToMeshService( - organizationId, - projectId, - workspaceId, - isApiKeyNew ? credential.id : credential.id_integration, - ); - const newlyCreatedOrExistingApiKey = isApiKeyNew ? credential.apiKey : credential.client_id; - - if (sdkList) { - logger.info( - 'Successfully subscribed API Key %s to API Mesh service', - isApiKeyNew ? credential.apiKey : credential.client_id, - ); - } else { - logger.error( - 'Unable to subscribe API Key %s to API Mesh service', - newlyCreatedOrExistingApiKey, - ); - } return { mesh: response.data, - apiKey: newlyCreatedOrExistingApiKey, - sdkList, }; } else { // Non 201 response received @@ -586,7 +542,7 @@ const getMeshId = async (organizationId, projectId, workspaceId, workspaceName) logger.info('Response from GET %s', response.status); if (response && response.status === 200) { - logger.info(`Mesh Config : ${objToString(response, ['data'])}`); + logger.debug(`Mesh response data : ${objToString(response, ['data'])}`); return response.data.meshId; } else { @@ -1000,9 +956,9 @@ const getMeshDeployments = async (organizationCode, projectId, workspaceId, mesh logger.error(`Error fetching deployments for mesh: ${meshId}`); return { - status: 'ERROR', + status: null, meshId: meshId, - error: 'Mesh status is not available.', + error: null, }; } }; diff --git a/src/urlBuilder.js b/src/urlBuilder.js index 50ea4776..75e34dbd 100644 --- a/src/urlBuilder.js +++ b/src/urlBuilder.js @@ -1,39 +1,6 @@ const CONSTANTS = require('./constants'); -const { getMesh } = require('./lib/devConsole'); -const { - MULTITENANT_GRAPHQL_SERVER_BASE_URL, - EDGE_MESH_BASE_URL, - EDGE_MESH_SANDBOX_BASE_URL, -} = CONSTANTS; - -/** - * Build the mesh url for the multitenant mesh. - * - * Gets the mesh details to checks for a custom domain in the case of a TI mesh. - * @param imsOrgId - * @param projectId - * @param workspaceId - * @param workspaceName - * @param meshId - * @param apiKey - * @returns {Promise} - */ -async function buildMeshUrl(imsOrgId, projectId, workspaceId, workspaceName, meshId, apiKey) { - const { meshURL: customBaseUrl } = await getMesh( - imsOrgId, - projectId, - workspaceId, - workspaceName, - meshId, - ); - - return customBaseUrl - ? `${customBaseUrl}/${meshId}/graphql` - : `${MULTITENANT_GRAPHQL_SERVER_BASE_URL}/${meshId}/graphql${ - apiKey ? `?api_key=${apiKey}` : '' - }`; -} +const { MESH_BASE_URL, MESH_SANDBOX_BASE_URL } = CONSTANTS; /** * Builds the mesh url for the edge mesh. @@ -43,16 +10,16 @@ async function buildMeshUrl(imsOrgId, projectId, workspaceId, workspaceName, mes * @param workspaceName * @returns {string} */ -function buildEdgeMeshUrl(meshId, workspaceName) { +function buildMeshUrl(meshId, workspaceName) { let baseUrl; - if (EDGE_MESH_BASE_URL.includes('stage')) { - baseUrl = EDGE_MESH_BASE_URL; + if (MESH_BASE_URL.includes('stage')) { + baseUrl = MESH_BASE_URL; } else { - baseUrl = workspaceName === 'Production' ? EDGE_MESH_BASE_URL : EDGE_MESH_SANDBOX_BASE_URL; + baseUrl = workspaceName === 'Production' ? MESH_BASE_URL : MESH_SANDBOX_BASE_URL; } return `${baseUrl}/${meshId}/graphql`; } -module.exports = { buildMeshUrl, buildEdgeMeshUrl }; +module.exports = { buildMeshUrl };