Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
01a92be
Merge pull request #270 from adobe/main
amolina-adobe Jun 25, 2025
c6b812c
feat: added active flag
AjazSumaiya Jul 18, 2025
080fb87
fix: address review comment
AjazSumaiya Jul 21, 2025
ff4ff4c
Added support for .graphql files
revanth0212 Jul 22, 2025
2414971
Updated snapshots
revanth0212 Jul 22, 2025
3defc2a
Minor
revanth0212 Jul 22, 2025
515f32f
fix: node version & address review comments
AjazSumaiya Jul 23, 2025
86de09d
fix: linting
AjazSumaiya Jul 23, 2025
4bab3a2
refactor: update getMesh to use lookup API
AjazSumaiya Jul 23, 2025
6de7ced
chore: update getMesh references in the code
AjazSumaiya Jul 23, 2025
6bc726c
Update build.yml to use Node 24
revanth0212 Jul 23, 2025
d99a9eb
Update publish-to-npm.yml to use Node 24
revanth0212 Jul 23, 2025
7d42cc3
Update build.yml to use Node 22
revanth0212 Jul 23, 2025
bc7059e
Update publish-to-npm.yml to use Node 22
revanth0212 Jul 23, 2025
fb88115
fix: error message and restore return statement
AjazSumaiya Jul 23, 2025
95b2684
fix: tests
AjazSumaiya Jul 23, 2025
dbd4a46
fix: node version
AjazSumaiya Jul 23, 2025
c5ddd6a
Merge pull request #273 from adobe/fix/graphqlFilesSupport
revanth0212 Jul 23, 2025
e0c9665
fix: update get test rejected values to match code
amolina-adobe Jul 24, 2025
cb47abc
Merge remote-tracking branch 'origin/develop' into feat/4873-addActiv…
amolina-adobe Jul 24, 2025
ca3d4b2
Merge pull request #275 from adobe/main
amolina-adobe Jul 24, 2025
1033ef6
Merge branch 'develop' into feat/4873-addActiveFlag
amolina-adobe Jul 24, 2025
667e02c
Merge pull request #271 from adobe/feat/4873-addActiveFlag
amolina-adobe Jul 24, 2025
795643e
chore: bump version for 5.5.0 release
amolina-adobe Jul 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/aio-cli-plugin-api-mesh",
"version": "5.4.2",
"version": "5.5.0",
"description": "Adobe I/O CLI plugin to develop and manage API mesh sources",
"keywords": [
"oclif-plugin"
Expand Down
187 changes: 136 additions & 51 deletions src/commands/api-mesh/__tests__/get.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const { writeFile } = require('fs/promises');
const { initSdk } = require('../../../helpers');
const GetCommand = require('../get');
const mockGetMeshConfig = require('../../__fixtures__/sample_mesh.json');
const { getMeshId, getMesh } = require('../../../lib/smsClient');
const { getMesh } = require('../../../lib/smsClient');

let logSpy = null;
let errorLogSpy = null;
Expand All @@ -47,6 +47,7 @@ describe('get command tests', () => {
beforeEach(() => {
initSdk.mockResolvedValue({
imsOrgId: selectedOrg.id,
imsOrgCode: selectedOrg.code,
projectId: selectedProject.id,
workspaceId: selectedWorkspace.id,
workspaceName: selectedWorkspace.title,
Expand All @@ -59,7 +60,6 @@ describe('get command tests', () => {

writeFile.mockResolvedValue(true);

getMeshId.mockResolvedValue('dummy_meshId');
getMesh.mockResolvedValue({
meshId: 'dummy_meshId',
mesh: mockGetMeshConfig,
Expand All @@ -79,7 +79,9 @@ describe('get command tests', () => {
});

test('snapshot get command', () => {
expect(GetCommand.description).toMatchInlineSnapshot(`"Get the config of a given mesh"`);
expect(GetCommand.description).toMatchInlineSnapshot(
`"Get the config of a specified mesh. Use the --active flag to retrieve the last successfully deployed mesh config"`,
);
expect(GetCommand.args).toMatchInlineSnapshot(`
[
{
Expand All @@ -89,6 +91,14 @@ describe('get command tests', () => {
`);
expect(GetCommand.flags).toMatchInlineSnapshot(`
{
"active": {
"allowNo": false,
"char": "a",
"default": false,
"description": "Retrieve the last successfully deployed mesh config",
"parse": [Function],
"type": "boolean",
},
"ignoreCache": {
"allowNo": false,
"char": "i",
Expand All @@ -109,52 +119,14 @@ describe('get command tests', () => {
expect(GetCommand.aliases).toMatchInlineSnapshot(`[]`);
});

test('should fail if mesh id is missing', async () => {
getMeshId.mockResolvedValueOnce(null);
const runResult = GetCommand.run();

return runResult.catch(err => {
expect(err.message).toMatchInlineSnapshot(
`"Unable to get mesh config. No mesh found for Org(1234) -> Project(5678) -> Workspace(123456789). Check the details and try again."`,
);
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
[
[
"Unable to get mesh config. No mesh found for Org(1234) -> Project(5678) -> Workspace(123456789). Check the details and try again.",
],
]
`);
});
});

test('should fail if getMeshId failed', async () => {
getMeshId.mockRejectedValueOnce(new Error('getMeshId failed'));
const runResult = GetCommand.run();

return runResult.catch(err => {
expect(err.message).toMatchInlineSnapshot(
`"Unable to get mesh ID. Check the details and try again. RequestId: dummy_request_id"`,
);
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
[
[
"Unable to get mesh ID. Check the details and try again. RequestId: dummy_request_id",
],
]
`);
});
});

test('should fail if mesh id is not found', async () => {
getMesh.mockResolvedValueOnce(null);
test('should fail if mesh is missing', async () => {
getMesh.mockRejectedValueOnce(new Error('MeshNotFound'));
await GetCommand.run();

await GetCommand.run().catch(err => {
expect(err.message).toContain(
'Unable to get mesh with the ID dummy_meshId. Please check the mesh ID and try again.',
);
});
expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
expect(errorLogSpy.mock.calls[0][0]).toBe(
'Unable to get mesh config. No mesh found for Org(CODE1234@AdobeOrg) -> Project(5678) -> Workspace(123456789). Please check the details and try again.',
);
});

test('should fail if get mesh method failed', async () => {
Expand Down Expand Up @@ -183,9 +155,7 @@ describe('get command tests', () => {
`);
});

test('should pass if mesh id is valid', async () => {
const meshId = 'dummy_meshId';
getMeshId.mockResolvedValueOnce(meshId);
test('should pass if mesh is found', async () => {
const runResult = await GetCommand.run();

expect(initSdk).toHaveBeenCalledWith({
Expand Down Expand Up @@ -402,4 +372,119 @@ describe('get command tests', () => {
`);
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`[]`);
});

// Active flag test cases
test('should get last successfully deployed mesh config with --active flag', async () => {
getMesh.mockResolvedValueOnce({
meshId: 'dummy_meshId',
mesh: mockGetMeshConfig,
});

parseSpy.mockResolvedValueOnce({
args: {},
flags: {
ignoreCache: mockIgnoreCacheFlag,
active: true,
},
});

const runResult = await GetCommand.run();

expect(getMesh).toHaveBeenCalledWith(
selectedOrg.code,
selectedProject.id,
selectedWorkspace.id,
selectedWorkspace.title,
true,
);

expect(runResult).toBeDefined();
expect(runResult.meshId).toBe('dummy_meshId');
});

test('should get last successfully deployed mesh config with shorthand -a flag', async () => {
getMesh.mockResolvedValueOnce({
meshId: 'dummy_meshId',
mesh: mockGetMeshConfig,
});

parseSpy.mockResolvedValueOnce({
args: {},
flags: {
ignoreCache: mockIgnoreCacheFlag,
active: true, // -a flag also sets active to true
},
});

const runResult = await GetCommand.run();

expect(getMesh).toHaveBeenCalledWith(
selectedOrg.code,
selectedProject.id,
selectedWorkspace.id,
selectedWorkspace.title,
true,
);

expect(runResult).toBeDefined();
expect(runResult.meshId).toBe('dummy_meshId');
});

test('should handle NoActiveDeploymentFound error when using --active flag', async () => {
getMesh.mockRejectedValueOnce(new Error('NoActiveDeploymentFound'));

parseSpy.mockResolvedValueOnce({
args: {},
flags: {
ignoreCache: mockIgnoreCacheFlag,
active: true,
},
});

const runResult = GetCommand.run();

await expect(runResult).rejects.toEqual(
new Error(
'No active deployment found for mesh. Check the details and try again or try without the --active flag. RequestId: dummy_request_id',
),
);

expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
expect(errorLogSpy.mock.calls[0][0]).toBe(
'No active deployment found for mesh. Check the details and try again or try without the --active flag. RequestId: dummy_request_id',
);
expect(getMesh).toHaveBeenCalledWith(
selectedOrg.code,
selectedProject.id,
selectedWorkspace.id,
selectedWorkspace.title,
true,
);
});

test('should handle mesh not found when using --active flag', async () => {
getMesh.mockRejectedValueOnce(new Error('MeshNotFound'));

parseSpy.mockResolvedValueOnce({
args: {},
flags: {
ignoreCache: mockIgnoreCacheFlag,
active: true,
},
});

await GetCommand.run();

expect(logSpy.mock.calls).toMatchInlineSnapshot(`[]`);
expect(errorLogSpy.mock.calls[0][0]).toBe(
'Unable to get mesh config. No mesh found for Org(CODE1234@AdobeOrg) -> Project(5678) -> Workspace(123456789). Please check the details and try again.',
);
expect(getMesh).toHaveBeenCalledWith(
selectedOrg.code,
selectedProject.id,
selectedWorkspace.id,
selectedWorkspace.title,
true,
);
});
});
78 changes: 37 additions & 41 deletions src/commands/api-mesh/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const { writeFile } = require('fs/promises');

const logger = require('../../classes/logger');
const { initSdk } = require('../../helpers');
const { ignoreCacheFlag, jsonFlag } = require('../../utils');
const { getMeshId, getMesh } = require('../../lib/smsClient');
const { ignoreCacheFlag, jsonFlag, activeFlag } = require('../../utils');
const { getMesh } = require('../../lib/smsClient');
const { buildMeshUrl } = require('../../urlBuilder');

require('dotenv').config();
Expand All @@ -24,6 +24,7 @@ class GetCommand extends Command {
static flags = {
ignoreCache: ignoreCacheFlag,
json: jsonFlag,
active: activeFlag,
};

static enableJsonFlag = true;
Expand All @@ -35,67 +36,62 @@ class GetCommand extends Command {

const ignoreCache = await flags.ignoreCache;
const json = await flags.json;
const active = await flags.active;

const { imsOrgId, imsOrgCode, projectId, workspaceId, workspaceName } = await initSdk({
ignoreCache,
verbose: !json,
});

let meshId = null;

try {
meshId = await getMeshId(imsOrgCode, projectId, workspaceId, workspaceName);
} catch (err) {
this.error(
`Unable to get mesh ID. Check the details and try again. RequestId: ${global.requestId}`,
);
}
const mesh = await getMesh(imsOrgCode, projectId, workspaceId, workspaceName, active);

if (meshId) {
try {
const mesh = await getMesh(imsOrgCode, projectId, workspaceId, workspaceName, meshId);
if (mesh) {
this.log('Successfully retrieved mesh %s', JSON.stringify(mesh, null, 2));

if (mesh) {
this.log('Successfully retrieved mesh %s', JSON.stringify(mesh, null, 2));
const meshUrl = buildMeshUrl(mesh.meshId, workspaceName);

const meshUrl = buildMeshUrl(meshId, workspaceName);
if (args.file) {
try {
const { meshConfig } = mesh;
await writeFile(args.file, JSON.stringify({ meshConfig }, null, 2));

if (args.file) {
try {
const { meshConfig } = mesh;
await writeFile(args.file, JSON.stringify({ meshConfig }, null, 2));
this.log('Successfully wrote mesh to file %s', args.file);
} catch (error) {
this.log('Unable to write mesh to file %s', args.file);

this.log('Successfully wrote mesh to file %s', args.file);
} catch (error) {
this.log('Unable to write mesh to file %s', args.file);

logger.error(error);
}
logger.error(error);
}

return { ...mesh, meshUrl, imsOrgId, projectId, workspaceId, workspaceName };
} else {
logger.error(
`Unable to get mesh with the ID ${meshId}. Check the mesh ID and try again. RequestId: ${global.requestId}`,
{ exit: false },
);
}
} catch (error) {
this.log(error.message);

return { ...mesh, meshUrl, imsOrgId, projectId, workspaceId, workspaceName };
} else {
this.error(
`Unable to get mesh config. No mesh found for Org(${imsOrgCode}) -> Project(${projectId}) -> Workspace(${workspaceId}). Please check the details and try again.`,
{ exit: false },
);
}
} catch (error) {
if (error.message === 'MeshNotFound') {
this.error(
`Unable to get mesh config. No mesh found for Org(${imsOrgCode}) -> Project(${projectId}) -> Workspace(${workspaceId}). Please check the details and try again.`,
{ exit: false },
);
} else if (error.message === 'NoActiveDeploymentFound') {
this.error(
`No active deployment found for mesh. Check the details and try again or try without the --active flag. RequestId: ${global.requestId}`,
);
} else {
this.log(error.message);
this.error(
`Unable to get mesh. Check the details and try again. If the error persists please contact support. RequestId: ${global.requestId}`,
);
}
} else {
this.error(
`Unable to get mesh config. No mesh found for Org(${imsOrgCode}) -> Project(${projectId}) -> Workspace(${workspaceId}). Please check the details and try again.`,
{ exit: false },
);
}
}
}

GetCommand.description = 'Get the config of a given mesh';
GetCommand.description =
'Get the config of a specified mesh. Use the --active flag to retrieve the last successfully deployed mesh config';

module.exports = GetCommand;
2 changes: 1 addition & 1 deletion src/commands/api-mesh/source/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class InstallCommand extends Command {
}

try {
const mesh = await getMesh(imsOrgCode, projectId, workspaceId, meshId, workspaceName);
const mesh = await getMesh(imsOrgCode, projectId, workspaceId, workspaceName);

if (!mesh) {
this.error(
Expand Down
2 changes: 1 addition & 1 deletion src/commands/api-mesh/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class StatusCommand extends Command {
}

try {
const mesh = await getMesh(imsOrgCode, projectId, workspaceId, workspaceName, meshId);
const mesh = await getMesh(imsOrgCode, projectId, workspaceId, workspaceName);
this.log(''.padEnd(102, '*'));
await this.displayMeshStatus(mesh, imsOrgCode, projectId, workspaceId);
this.log(''.padEnd(102, '*'));
Expand Down
Loading
Loading