Skip to content

[DevTools] PR2: Backend Services, Console Viewer, and Resources #2879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
e994339
PR1: Dependencies, Basic React App Facade, and Barebones Devtools Com…
Jun 25, 2025
02a7289
made timeout longer AND temporary workaround for license issues
Jun 26, 2025
776fd8a
PR2: Basic Sandbox Manager and Resource Console PRELIM
Jun 26, 2025
7bd6b1b
Fixing Lint and Tests
Jun 27, 2025
093c130
fixed lint issues and tests
Jun 27, 2025
c9d0917
updating for structure changes, printer
Jul 2, 2025
ce447ae
merging with pr-1 as base
Jul 2, 2025
8ff3baf
update API
Jul 2, 2025
3085faf
updating package_lock (based on pr_1)
Jul 2, 2025
d9d10c9
update default_packages
Jul 2, 2025
9955896
added changeset
Jul 3, 2025
1a0849a
fix lint
Jul 3, 2025
ce3a461
reconcile status types
Jul 3, 2025
134e83a
removed profile from start sandbox modal as it can't do anything
Jul 8, 2025
52d92e0
fixed loading of start sandbox when modal dismissed
Jul 8, 2025
77b7f60
removed aws-sdk upgrade, removed dependencies in root package
Jul 8, 2025
87fa132
got rid of cleanAnsiCodes (and duplicate), added stripAnsi instead, a…
Jul 8, 2025
740271c
moved server starting from portchecker to devtools command
Jul 8, 2025
68c9ab7
fix lint
Jul 8, 2025
73f32e0
pull out normalizeCDKConstructPath utility and refactor usage in CfnD…
Jul 8, 2025
dc33887
fix api for cli-core (added normalizeCDK constructPath)
Jul 8, 2025
4f2ed23
Refactor socket communication and service structure
Jul 9, 2025
11c9684
make AWS console links visible, remove unused log note
Jul 9, 2025
fa14390
Update sandbox state management, add initialization error handling
Jul 9, 2025
0d1c15f
get resources if sandbox is stopped
Jul 9, 2025
e5c9925
fix API for initialization error
Jul 9, 2025
61e3a47
inject dependencies with sandbox devtools command factory
Jul 9, 2025
1e47844
making socket handlers use resource service (eliminating duplicate code)
Jul 9, 2025
732c99d
Implement socket event constants instead of strings
Jul 9, 2025
3f95603
add changeset
Jul 9, 2025
126366d
add socket_handler tests
Jul 9, 2025
152f5c4
add unit tests for resource console functions
Jul 9, 2025
b3455b3
refactor ResourceService to use backendClient and RegionFetcher throu…
Jul 9, 2025
724ff1a
add resource_service tests
Jul 9, 2025
740e08c
update api for regionfetcher
Jul 9, 2025
050928b
move shutdown_service import in socket handlers
Jul 9, 2025
7d12cb5
added auto-scroll disable if you scroll up in console logs
Jul 9, 2025
e289446
enhance friendly name generation and add template metadata fetching
Jul 9, 2025
97c3b99
changeset
Jul 9, 2025
e53ec95
fixed search bar disappearing issue.
Jul 9, 2025
0fdfae9
refactor shutdown process to remove unnecessary client notifications …
Jul 9, 2025
66e7255
api update for metadata
Jul 10, 2025
4708052
updated deployed_resources_enumerator test to include metadata
Jul 10, 2025
af2e6ac
Implement logging functionality without global printer override, fixe…
Jul 10, 2025
33570fd
manually tested additional resources, added them to tests
Jul 10, 2025
b96a7d1
more friendly name updates + testing
Jul 10, 2025
72b1a55
updated resource service test for new friendly name
Jul 11, 2025
ead3ee3
Add random delay on initial load to prevent thundering herd problem; …
Jul 11, 2025
41ba348
replace local type definitions with centralized SandboxStatus type fr…
Jul 11, 2025
219e3cd
api update for sandbox status
Jul 11, 2025
1671e6d
refactored sandbox devtools command to make it more testable, and fix…
Jul 11, 2025
88ed395
fixed SandboxStatus updates
Jul 11, 2025
75e5b1d
misc comment updates
Jul 11, 2025
ede0865
remove unused index.css file (which was added for PR1, and visually d…
Jul 11, 2025
df5e7b5
Remove useEffect in ConsoleViewer
megha-narayanan Jul 16, 2025
a47c32d
Minor output and naming changes in sandbox_devtools command
megha-narayanan Jul 16, 2025
6c2a488
finish fixing renaming in sandbox_devtools_command
Jul 16, 2025
9c49385
remove aws-sdk upgrades
Jul 16, 2025
1089978
remove deprecated private method in cfn_deployment_progress_logger
Jul 16, 2025
9153373
remove unneeded variable in Console_Viewer
Jul 16, 2025
417b73e
Made confirmation dialogues a custom modal instead of browser window
Jul 16, 2025
24c704c
Fixed potential double space in Header status
Jul 16, 2025
76b21a3
combined useEffects in App.tsx
Jul 16, 2025
64c0c4d
remove resourceConfigChanged
Jul 16, 2025
109f154
added error for failed emit
Jul 16, 2025
b8fc07b
added unit tests for state management
Jul 16, 2025
c9d2288
Update packages/deployed-backend-client/src/deployed-backend-client/d…
megha-narayanan Jul 16, 2025
9416275
get rid of unused fields in StartSandboxModal, fix internal typing fo…
Jul 16, 2025
9b8693a
Merge branch 'pr-2-backend-services' of https://github.com/megha-nara…
Jul 16, 2025
8db171f
expand issue comment
Jul 16, 2025
b271bad
fix error in deployed_resources_enumerator
Jul 16, 2025
f569654
throw error on null socket
Jul 16, 2025
1004c78
inject logger factory as dependency
Jul 16, 2025
5786485
header status text fix
Jul 16, 2025
4861a0d
fix lint
Jul 16, 2025
1c6d9c0
fixed unsubscribe naming.
Jul 17, 2025
e5d1da0
handle undefined response on describestacks command
Jul 17, 2025
422058c
rate limiting comment
Jul 17, 2025
e3c1c9b
header minor fixes
Jul 18, 2025
aaa7a5a
Update packages/cli/src/commands/sandbox/sandbox-devtools/react-app/s…
megha-narayanan Jul 18, 2025
f55e43c
change fn name
Jul 18, 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
6 changes: 6 additions & 0 deletions .changeset/dull-towns-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@aws-amplify/deployed-backend-client': patch
'@aws-amplify/platform-core': patch
---

made regionfetcher public, added metadata to deployedbackendresource
5 changes: 5 additions & 0 deletions .changeset/loose-wings-stop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@aws-amplify/cli-core': patch
---

pulled out normalizeCDKconstructPath
8 changes: 8 additions & 0 deletions .changeset/thin-paths-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@aws-amplify/sandbox': minor
'@aws-amplify/backend-cli': minor
'@aws-amplify/backend-deployer': patch
'@aws-amplify/ai-constructs': patch
---

Devtools PR2
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ packages/integration-tests/src/e2e-tests

# Frontend code
packages/cli/src/commands/sandbox/sandbox-devtools/react-app/**
!packages/cli/src/commands/sandbox/sandbox-devtools/react-app/package.json
5,659 changes: 3,194 additions & 2,465 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
"@aws-sdk/client-iam": "^3.750.0",
"@aws-sdk/client-s3": "^3.750.0",
"@aws-sdk/client-ssm": "^3.750.0",
"@aws-sdk/eventstream-handler-node": "^3.821.0",
"@aws-sdk/middleware-eventstream": "^3.821.0",
"@changesets/cli": "^2.26.1",
"@changesets/get-release-plan": "^4.0.0",
"@changesets/types": "^6.0.0",
Expand Down Expand Up @@ -97,6 +99,7 @@
"glob": "^11.0.2",
"husky": "^9.1.7",
"lint-staged": "^15.2.10",
"npm-force-resolutions": "^0.0.10",
"prettier": "^3.5.3",
"rimraf": "^6.0.1",
"semver": "^7.5.4",
Expand Down
3 changes: 3 additions & 0 deletions packages/cli-core/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export enum LogLevel {
// @public (undocumented)
export const minimumLogLevel: LogLevel;

// @public
export const normalizeCDKConstructPath: (constructPath: string) => string;

// @public (undocumented)
export type Notice = z.infer<typeof noticeSchema>;

Expand Down
22 changes: 22 additions & 0 deletions packages/cli-core/src/formatters/cdk_path_formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Utilities for formatting CDK paths and constructs
*/

/**
* Normalizes a CDK construct path to create a more readable friendly name
* @param constructPath The CDK construct path
* @returns A normalized construct path
*/
export const normalizeCDKConstructPath = (constructPath: string): string => {
// Don't process very long paths to avoid performance issues
if (constructPath.length > 1000) return constructPath;

// Handle nested stack paths
const nestedStackRegex =
/(?<nestedStack>[a-zA-Z0-9_]+)\.NestedStack\/\1\.NestedStackResource$/;

return constructPath
.replace(nestedStackRegex, '$<nestedStack>')
.replace('/amplifyAuth/', '/')
.replace('/amplifyData/', '/');
};
1 change: 1 addition & 0 deletions packages/cli-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './package-manager-controller/package_manager_controller_factory.j
export * from './loggers/amplify_io_events_bridge_singleton_factory.js';
export * from './notices/notices.js';
export * from './notices/notices_manifest_validator.js';
export { normalizeCDKConstructPath } from './formatters/cdk_path_formatter.js';
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { StackEvent } from '@aws-sdk/client-cloudformation';
import { RewritableBlock } from './rewritable_block.js';
import { ColorName, format } from '../../format/format.js';
import { EOL } from 'os';
import { normalizeCDKConstructPath } from '../../formatters/cdk_path_formatter.js';

/**
* Collects events from CDK Toolkit about cfn deployment and structures them
Expand Down Expand Up @@ -103,7 +104,7 @@ export class CfnDeploymentProgressLogger {
if (metadata && metadata.constructPath) {
if (!(event.LogicalResourceId in this.resourceNameCache)) {
this.resourceNameCache[event.LogicalResourceId] =
this.normalizeCDKConstructPath(metadata.constructPath);
normalizeCDKConstructPath(metadata.constructPath);
}
}
// Hydrate friendly name resource cache
Expand Down Expand Up @@ -231,21 +232,6 @@ export class CfnDeploymentProgressLogger {
await this.block.displayLines(lines);
}

/**
* Extract nested stack names
*/
private normalizeCDKConstructPath = (constructPath: string): string => {
// Don't run regex on long strings, they are most likely not valid and could cause DOS attach. See CodeQL's js/polynomial-redos
if (constructPath.length > 1000) return constructPath;
const nestedStackRegex =
/(?<nestedStack>[a-zA-Z0-9_]+)\.NestedStack\/\1\.NestedStackResource$/;

return constructPath
.replace(nestedStackRegex, '$<nestedStack>')
.replace('/amplifyAuth/', '/')
.replace('/amplifyData/', '/');
};

/**
* Extract the failure reason from stack events
*/
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"semver": "^7.6.3",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1",
"strip-ansi": "^7.1.0",
"typescript": "^5.8.3",
"ws": "^8.18.2",
"yargs": "^17.7.2",
Expand Down
8 changes: 6 additions & 2 deletions packages/cli/src/commands/sandbox/port_checker.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import net from 'net';

/**
* Port checker class. Provides utilities for checking if ports are in use
* and if specific services are running.
* Port checker class. Provides utilities for checking if ports are in use.
*/
export class PortChecker {
/**
* Default DevTools port
*/
private static readonly defaultDevtoolsPort = 3333;

/**
* Checks if a port is in use
* @param port The port to check
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* eslint-disable spellcheck/spell-checker */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one of future PR, we should remove all file level eslint suppression.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, will do.

import { describe, it } from 'node:test';
import assert from 'node:assert';
import { createFriendlyName } from './cloudformation_format.js';

void describe('createFriendlyName function', () => {
void it('handles empty string by returning the original ID', () => {
const emptyId = '';
assert.strictEqual(createFriendlyName(emptyId), emptyId);
});

void it('uses CDK metadata construct path when available', () => {
const logicalId = 'amplifyFunction123ABC45';
const metadata = { constructPath: 'MyStack/MyFunction/Resource' };
assert.strictEqual(createFriendlyName(logicalId, metadata), 'My Function');
});

void it('removes amplify prefix and formats camel case', () => {
const logicalId = 'amplifyDataTable123ABC45';
assert.strictEqual(createFriendlyName(logicalId), 'Data Table');
});

void it('removes Amplify prefix (capitalized) and formats camel case', () => {
const logicalId = 'AmplifyDataTable123ABC45';
assert.strictEqual(createFriendlyName(logicalId), 'Data Table');
});

void it('handles IDs with only numeric characters', () => {
const numericId = '12345';
assert.strictEqual(createFriendlyName(numericId), numericId);
});

void it('normalizes CDK construct paths', () => {
const logicalId = 'amplifyFunction';
const metadata = {
constructPath: 'MyStack/auth.NestedStack/auth.NestedStackResource',
};
assert.strictEqual(createFriendlyName(logicalId, metadata), 'auth');
});

void it('skips Resource and Default in construct paths', () => {
const logicalId = 'amplifyFunction';
const metadata = { constructPath: 'MyStack/Auth/Resource' };
assert.strictEqual(createFriendlyName(logicalId, metadata), 'Auth');
});

void it('handles multiple levels of Resource and Default', () => {
const logicalId = 'amplifyFunction';
const metadata = { constructPath: 'MyStack/Auth/Default/Resource' };
assert.strictEqual(createFriendlyName(logicalId, metadata), 'Auth');
});

void it('formats camel case with multiple uppercase letters', () => {
const logicalId = 'amplifyGraphQLAPI123ABC45';
assert.strictEqual(createFriendlyName(logicalId), 'Graph QLAPI');
});

void it('handles complex CloudFormation resource IDs', () => {
const logicalId = 'TodoIAMRole2DA8E66E';
assert.strictEqual(createFriendlyName(logicalId), 'Todo IAM Role');
});

void it('handles empty construct path', () => {
const logicalId = 'amplifyFunction';
const metadata = { constructPath: '' };
assert.strictEqual(createFriendlyName(logicalId, metadata), 'Function');
});

// Examples from documentation
void it('formats TodoTable correctly', () => {
const logicalId = 'TodoTable';
assert.strictEqual(createFriendlyName(logicalId), 'Todo Table');
});

void it('formats TodoIAMRole with ID correctly', () => {
const logicalId = 'TodoIAMRole2DA8E66E';
assert.strictEqual(createFriendlyName(logicalId), 'Todo IAM Role');
});

void it('formats amplifyDataGraphQLAPI with ID correctly', () => {
const logicalId = 'amplifyDataGraphQLAPI42A6FA33';
assert.strictEqual(createFriendlyName(logicalId), 'Data Graph QLAPI');
});

void it('formats testNameBucketPolicy with ID correctly', () => {
const logicalId = 'testNameBucketPolicyA5C458BB';
assert.strictEqual(
createFriendlyName(logicalId),
'test Name Bucket Policy',
);
});

void it('handles CDK construct path example', () => {
const logicalId = 'somelogicalId';
const metadata = {
constructPath:
'amplify-amplifyvitereacttemplate-meghabit-sandbox-83e297d0db/data/modelIntrospectionSchemaBucket/Resource',
};
assert.strictEqual(
createFriendlyName(logicalId, metadata),
'model Introspection Schema Bucket',
);
});

void it('handles CDK construct path example', () => {
const logicalId = 'someLogicalId';
const metadata = {
constructPath:
'amplify-amplifyvitereacttemplate-meghabit-sandbox-83e297d0db/data/GraphQLAPI/DefaultApiKey',
};
assert.strictEqual(
createFriendlyName(logicalId, metadata),
'Default Api Key',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { normalizeCDKConstructPath } from '@aws-amplify/cli-core';

/**
* Creates a friendly name for a resource, using CDK metadata when available.
* @param logicalId The logical ID of the resource
* @param metadata Optional CDK metadata that may contain construct path
* @param metadata.constructPath Optional construct path from CDK metadata
* @returns A user-friendly name for the resource
*
* Examples of friendly names:
* - "TodoTable" → "Todo Table"
* - "TodoIAMRole2DA8E66E" → "Todo IAM Role"
* - "amplifyDataGraphQLAPI42A6FA33" → "Data GraphQLAPI"
* - "testNameBucketPolicyA5C458BB" → "test Name Bucket Policy"
*
* For construct paths:
* - amplify-amplify-identifier-sandbox-83e297d0db/data/GraphQLAPI/DefaultApiKey → "Default Api Key"
* - amplify-amplify-identifier-sandbox-83e297d0db/auth/amplifyAuth/authenticatedUserRole/Resource → "authenticated User Role"
*/
export const createFriendlyName = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Borderline arcane incantations herein. Partially cp-ing a comment from below. Examples in docstrings will help future maintainers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank-you! Probably goes without saying, but explicit test coverage would also be ideal. Unless I'm overlooking it, just a small set of examples like you have here in a test loop would be good.

logicalId: string,
metadata?: { constructPath?: string },
): string => {
let name = logicalId;
if (metadata?.constructPath) {
const normalizedPath = normalizeCDKConstructPath(metadata.constructPath);
const parts = normalizedPath.split('/');
let resourceName = parts.pop();
while (
(resourceName === 'Resource' || resourceName === 'Default') &&
parts.length > 0
) {
resourceName = parts.pop();
}

name = resourceName || logicalId;
}

// Fall back to the basic transformation
name = name.replace(/^amplify/, '').replace(/^Amplify/, '');

name = name.replace(/([a-z])([A-Z])/g, '$1 $2');

name = name.replace(/([A-Z])([A-Z][a-z])/g, '$1 $2');

// Remove CloudFormation resource IDs (alphanumeric suffixes)
name = name.replace(/[0-9A-F]{6,}$/g, '');

name = name.replace(/\s+/g, ' ').trim();

const result = name || logicalId;
return result;
};

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading