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 13 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
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
5,318 changes: 3,080 additions & 2,238 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,15 @@
"@actions/github": "^6.0.0",
"@aws-amplify/eslint-plugin-amplify-backend-rules": "^0.0.2",
"@aws-sdk/client-amplify": "^3.750.0",
"@aws-sdk/client-cloudformation": "^3.750.0",
"@aws-sdk/client-cloudwatch-logs": "^3.750.0",
"@aws-sdk/client-cloudformation": "^3.828.0",
"@aws-sdk/client-cloudwatch-logs": "^3.835.0",
"@aws-sdk/client-cognito-identity-provider": "^3.750.0",
"@aws-sdk/client-dynamodb": "^3.750.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

I still see these upgrades for some aws-sdk clients. We should avoid this

Copy link
Author

Choose a reason for hiding this comment

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

oops. missed that. fixed now!

"@aws-sdk/client-iam": "^3.750.0",
"@aws-sdk/client-iam": "^3.835.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need aws-sdk upgrade as part of this? If not, we should decouple.

Copy link
Author

Choose a reason for hiding this comment

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

reverting.

"@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 All @@ -118,6 +121,16 @@
"*.yml": "prettier --write",
"*.md": "prettier --write"
},
"dependencies": {
"@aws-sdk/client-lambda": "^3.835.0",
"cookie-signature": "1.0.6",
"debounce-promise": "^3.1.2",
"esbuild": "0.25.5",
"express": "^5.1.0",
"express-rate-limit": "^7.5.0",
"jszip": "^3.10.1",
"ws": "^8.18.2"
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need all these dependencies, and in the root package?

Copy link
Author

Choose a reason for hiding this comment

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

Nope. Removing

"overrides": {
"minimatch": "10.0.3"
}
Expand Down
50 changes: 50 additions & 0 deletions packages/cli/src/commands/sandbox/port_checker.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,60 @@
import net from 'net';
import { createServer } from 'node:http';
import { LogLevel, printer } from '@aws-amplify/cli-core';

/**
* Port checker class. Provides utilities for checking if ports are in use
* and if specific services are running.
*/
export class PortChecker {
/**
* Default DevTools port
*/
private static readonly defaultDevtoolsPort = 3333;
/**
* Attempts to start the server on the specified port
* @param server The HTTP server
* @param port The port to use
* @returns A promise that resolves with the port when the server starts
* @throws Error if the port is already in use
*/
async findAvailablePort(
Copy link
Contributor

Choose a reason for hiding this comment

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

The method name is not matching the documentation and what it is doing. And why are we starting a server in PortChecker class?

Copy link
Author

Choose a reason for hiding this comment

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

moving the server starting to devtools command -- this is a relic from when we tried multiple ports.

server: ReturnType<typeof createServer>,
port: number,
): Promise<number> {
let serverStarted = false;

try {
await new Promise<void>((resolve, reject) => {
server.listen(port, () => {
serverStarted = true;
resolve();
});

server.once('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EADDRINUSE') {
reject(
new Error(
`Port ${port} is already in use. Please close any applications using this port and try again.`,
),
);
} else {
reject(err);
}
});
});
} catch (error) {
printer.log(`Failed to start server: ${String(error)}`, LogLevel.ERROR);
throw error;
}

if (!serverStarted) {
throw new Error(`Failed to start server on port ${port}`);
}

return port;
}

/**
* 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,118 @@
/**
* 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
*/
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 => {
// If we have CDK metadata with a construct path, use it
if (metadata?.constructPath) {
return normalizeCDKConstructPath(metadata.constructPath);
}

// For CloudFormation stacks, try to extract a friendly name
if (
logicalId.includes('NestedStack') ||
logicalId.endsWith('StackResource')
) {
const nestedStackName = getFriendlyNameFromNestedStackName(logicalId);
if (nestedStackName) {
return nestedStackName;
}
}

// Fall back to the basic transformation
let name = logicalId.replace(/^amplify/, '').replace(/^Amplify/, '');
name = name.replace(/([A-Z])/g, ' $1').trim();
name = name.replace(/[0-9]+[A-Z]*[0-9]*/, '');

return name || logicalId;
};

/**
* Normalizes a CDK construct path to create a more readable friendly name
* @param constructPath The CDK construct path
* @returns A normalized construct path
*/
const normalizeCDKConstructPath = (constructPath: string): string => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems duplicated from here

private normalizeCDKConstructPath = (constructPath: string): string => {

We should see if we can refactor cfn_deployment_progress_logger.ts such that it can be used for both CLI and DevConsole instead of duplicating the logic

Copy link
Author

Choose a reason for hiding this comment

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

pulled that function out and exported it to get rid of duplication.

// 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/', '/');
};

/**
* Extracts a friendly name from a nested stack logical ID
* @param stackName The stack name to process
* @returns A friendly name or undefined if no match
*/
const getFriendlyNameFromNestedStackName = (
stackName: string,
): string | undefined => {
const parts = stackName.split('-');

if (parts && parts.length === 7 && parts[3] === 'sandbox') {
return parts[5].slice(0, -10) + ' stack';
} else if (parts && parts.length === 5 && parts[3] === 'sandbox') {
return 'root stack';
}

return undefined;
};
Copy link
Member

Choose a reason for hiding this comment

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

A good habit when baking arcane splits and magic numbers into a function like this is to show an example or two of the before/after in the docstring. If there is a good name you can give to the magic constants here, give them names. Else, it's helpful to indicate where they come from in the docstring examples.

In this case, the naming patterns might be completely obvious to others in the codebase. But, if someone new (it's always Day 1, right?) jumps in at this point, examples will help.

Leaving this comment on this particularly arcane looking set of manipulations. But, please apply anywhere you feel arcane incantations are being performed.

Copy link
Author

Choose a reason for hiding this comment

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

got it. actually figured out how to actually get non-null metadata which eliminates the need for some of these functions. but examples have been added in the remaining functions where I felt it was needed.


/**
* Clean ANSI escape codes from text
* @param text The text to clean
* @returns The cleaned text
*/
export const cleanAnsiCodes = (text: string): string => {
// Split the regex into parts to avoid control characters
const ansiEscapeCodesPattern = new RegExp(
[
// ESC [ n ; n ; ... m
String.fromCharCode(27) + '\\[\\d+(?:;\\d+)*m',
// Other common ANSI sequences
'\\[2m',
'\\[22m',
'\\[1m',
'\\[36m',
'\\[39m',
].join('|'),
'g',
);

return text.replace(ansiEscapeCodesPattern, '');
};
Copy link
Member

Choose a reason for hiding this comment

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

What's functionally different about this cleaner and the one below?

Copy link
Author

Choose a reason for hiding this comment

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

already resolved in commit 87fa132 (duplicate from trimming down PR)


/**
* Check if a message is a deployment progress message
* @param message The message to check
* @returns True if the message is a deployment progress message
*/
export const isDeploymentProgressMessage = (message: string): boolean => {
const cleanedMessage = cleanAnsiCodes(message);
return (
cleanedMessage.includes('_IN_PROGRESS') ||
cleanedMessage.includes('CREATE_') ||
cleanedMessage.includes('DELETE_') ||
cleanedMessage.includes('UPDATE_') ||
cleanedMessage.includes('Deployment in progress') ||
cleanedMessage.includes('COMPLETE') ||
cleanedMessage.includes('FAILED') ||
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't mind seeing something like this if it speaks to you:

[
  '_IN_PROGRESS',
  'CREATE_',
  'DELETE_',
  'UPDATE_',
  'Deployment in progress',
  'COMPLETE',
  'FAILED'
].some(pattern => cleanedMessage.includes(pattern))

But, with room for opinion here, so adopt only if it really speaks to you.

Copy link
Author

Choose a reason for hiding this comment

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

will do.

// Match CloudFormation resource status patterns
/\d+:\d+:\d+\s+[AP]M\s+\|\s+[A-Z_]+\s+\|\s+.+\s+\|\s+.+/.test(
Copy link
Member

Choose a reason for hiding this comment

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

This one's a bit arcane for me. Examples please.

Copy link
Author

Choose a reason for hiding this comment

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

got it. this actually belongs in a different PR, so I'm getting rid of it here.

cleanedMessage,
)
);
};
Loading
Loading