Skip to content

Commit 2c576e6

Browse files
aalejjoehan
andauthored
Add validation to check if project ID exists (#8726)
* Add validation to check if project ID exists * remove /test/storage.rules file * clean up and changelog entry --------- Co-authored-by: joehan <[email protected]>
1 parent 3229f4d commit 2c576e6

File tree

2 files changed

+52
-3
lines changed

2 files changed

+52
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
- Added validation to check if project ID exists during project creation. (#5233)
12
- Add `generate_dataconnect_schema`, `dataconnect_generate_operation`, `firebase_consult_assistant` MCP tools. (#8647)
23
- `firebase init dataconnect` is now integrated with Gemini in Firebase API to generate Schema based on description. (#8596)

src/management/projects.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const TIMEOUT_MILLIS = 30000;
1616
const MAXIMUM_PROMPT_LIST = 100;
1717
const PROJECT_LIST_PAGE_SIZE = 1000;
1818
const CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS = 15000;
19+
const CHECK_PROJECT_ID_API_REQUEST_TIMEOUT_MILLIS = 15000;
1920

2021
export enum ProjectParentResourceType {
2122
ORGANIZATION = "organization",
@@ -39,14 +40,26 @@ export async function promptProjectCreation(
3940
message:
4041
"Please specify a unique project id " +
4142
`(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
42-
validate: (projectId: string) => {
43+
validate: async (projectId: string) => {
4344
if (projectId.length < 6) {
4445
return "Project ID must be at least 6 characters long";
4546
} else if (projectId.length > 30) {
4647
return "Project ID cannot be longer than 30 characters";
47-
} else {
48-
return true;
4948
}
49+
50+
try {
51+
// Best effort. We should still allow project creation even if this fails.
52+
const { isAvailable, suggestedProjectId } = await checkAndRecommendProjectId(projectId);
53+
if (!isAvailable && suggestedProjectId) {
54+
return `Project ID is taken or unavailable. Try ${clc.bold(suggestedProjectId)}.`;
55+
}
56+
} catch (error: any) {
57+
logger.debug(
58+
`Couldn't check if project ID ${projectId} is available. Original error: ${error}`,
59+
);
60+
}
61+
62+
return true;
5063
},
5164
}));
5265

@@ -75,6 +88,12 @@ const firebaseAPIClient = new Client({
7588
apiVersion: "v1beta1",
7689
});
7790

91+
const firebaseV1APIClient = new Client({
92+
urlPrefix: api.firebaseApiOrigin(),
93+
auth: true,
94+
apiVersion: "v1",
95+
});
96+
7897
const resourceManagerClient = new Client({
7998
urlPrefix: api.resourceManagerOrigin(),
8099
apiVersion: "v1",
@@ -432,6 +451,35 @@ export async function listFirebaseProjects(pageSize?: number): Promise<FirebaseP
432451
return projects;
433452
}
434453

454+
export async function checkAndRecommendProjectId(
455+
projectId: String,
456+
): Promise<{ isAvailable: boolean; suggestedProjectId?: string }> {
457+
try {
458+
const res = await firebaseV1APIClient.request<
459+
any,
460+
{ projectIdStatus: string; suggestedProjectId?: string }
461+
>({
462+
method: "POST",
463+
path: "/projects:checkProjectId",
464+
body: {
465+
proposedId: projectId,
466+
},
467+
timeout: CHECK_PROJECT_ID_API_REQUEST_TIMEOUT_MILLIS,
468+
});
469+
470+
const { projectIdStatus, suggestedProjectId } = res.body;
471+
return {
472+
isAvailable: projectIdStatus === "PROJECT_ID_AVAILABLE",
473+
suggestedProjectId,
474+
};
475+
} catch (err: any) {
476+
throw new FirebaseError(
477+
"Failed to check if project ID is available. See firebase-debug.log for more info.",
478+
{ exit: 2, original: err },
479+
);
480+
}
481+
}
482+
435483
/**
436484
* Gets the Firebase project information identified by the specified project ID
437485
*/

0 commit comments

Comments
 (0)