@@ -43,14 +43,17 @@ abstract class Launch {
4343 );
4444
4545 await selectProjectId (
46+ cloudApiClient,
4647 logger,
4748 projectSetup,
4849 );
4950
50- await selectEnableDb (
51- logger,
52- projectSetup,
53- );
51+ if (projectSetup.preexistingProject != true ) {
52+ await selectEnableDb (
53+ logger,
54+ projectSetup,
55+ );
56+ }
5457
5558 await selectPerformDeploy (
5659 logger,
@@ -144,6 +147,7 @@ abstract class Launch {
144147 }
145148
146149 static Future <void > selectProjectId (
150+ final Client cloudApiClient,
147151 final CommandLogger logger,
148152 final ProjectLaunch projectSetup,
149153 ) async {
@@ -162,6 +166,13 @@ abstract class Launch {
162166 logger.error (invalidProjectIdMessage);
163167 }
164168
169+ final selectedId = await _selectExistingProject (cloudApiClient, logger);
170+ if (selectedId != null ) {
171+ projectSetup.projectId = selectedId;
172+ projectSetup.preexistingProject = true ;
173+ return ;
174+ }
175+
165176 final defaultProjectId = _getDefaultProjectId (projectSetup.projectDir);
166177
167178 logger.raw (
@@ -176,7 +187,7 @@ The default API domain will be: <project-id>.api.serverpod.space
176187 final defaultValue =
177188 defaultProjectId != null ? '$defaultPrefix $defaultProjectId ' : null ;
178189 var projectId = await logger.input (
179- 'Choose project id' ,
190+ 'Enter a new project id' ,
180191 defaultValue: defaultValue,
181192 );
182193
@@ -198,6 +209,85 @@ The default API domain will be: <project-id>.api.serverpod.space
198209 } while (true );
199210 }
200211
212+ static Future <String ?> _selectExistingProject (
213+ final Client cloudApiClient,
214+ final CommandLogger logger,
215+ ) async {
216+ final projects = await _fetchExistingProjects (cloudApiClient);
217+ if (projects.isEmpty) {
218+ return null ;
219+ }
220+ if (projects.length == 1 ) {
221+ return _confirmSingleExistingProject (logger, projects.single);
222+ }
223+ return _selectFromSeveralExistingProjects (logger, projects);
224+ }
225+
226+ static Future <List <Project >> _fetchExistingProjects (
227+ final Client cloudApiClient,
228+ ) async {
229+ try {
230+ final projects = await cloudApiClient.projects.listProjects ();
231+ final activeProjects = projects.where ((final p) => p.archivedAt == null );
232+ return activeProjects.toList ();
233+ } on Exception catch (e, s) {
234+ throw FailureException .nested (e, s, 'Request to list projects failed' );
235+ }
236+ }
237+
238+ static Future <String ?> _confirmSingleExistingProject (
239+ final CommandLogger logger,
240+ final Project project,
241+ ) async {
242+ logger.info (
243+ 'Found an existing Cloud project: ${project .cloudProjectId }' ,
244+ newParagraph: true ,
245+ );
246+
247+ final confirm = await logger.confirm (
248+ 'Continue with ${project .cloudProjectId }?' ,
249+ );
250+ logger.info (' ' );
251+ return confirm ? project.cloudProjectId : null ;
252+ }
253+
254+ static Future <String ?> _selectFromSeveralExistingProjects (
255+ final CommandLogger logger,
256+ final List <Project > projects,
257+ ) async {
258+ final existingIds = projects.map ((final p) => p.cloudProjectId).toList ();
259+ logger.info (
260+ 'Found existing Cloud projects.\n '
261+ 'Do you want to deploy to one of them instead of creating a new one?' ,
262+ newParagraph: true ,
263+ );
264+ for (int i = 0 ; i < existingIds.length; i++ ) {
265+ logger.info ('${i + 1 }. ${existingIds [i ]}' );
266+ }
267+ logger.info ('(blank - create a new project)' );
268+
269+ do {
270+ final projectNum = await logger.input (
271+ 'Enter a project number from the list, or blank' ,
272+ );
273+
274+ if (projectNum.isEmpty || projectNum == 'q' ) {
275+ logger.info (' ' );
276+ return null ;
277+ }
278+
279+ final projectIx = int .tryParse (projectNum);
280+ if (projectIx != null &&
281+ projectIx >= 1 &&
282+ projectIx <= existingIds.length) {
283+ logger.info (' ' );
284+ return existingIds[projectIx - 1 ];
285+ }
286+
287+ logger.error ('Value must be a number from the list, or empty to skip.' );
288+ } while (true );
289+ }
290+
201291 static String ? _getDefaultProjectId (final String ? projectDir) {
202292 if (projectDir != null ) {
203293 final pubspec = TenantProjectPubspec .fromProjectDir (
@@ -277,20 +367,22 @@ The default API domain will be: <project-id>.api.serverpod.space
277367
278368 final projectId = projectSetup.projectId! ;
279369 final projectDir = projectSetup.projectDir! ;
280- final enableDb = projectSetup.enableDb! ;
281370 final performDeploy = projectSetup.performDeploy! ;
282371
283- await ProjectCommands .createProject (
284- cloudApiClient,
285- logger: logger,
286- projectId: projectId,
287- enableDb: enableDb,
288- projectDir: projectDir,
289- configFilePath: p.join (
290- projectDir,
291- ProjectConfigFileConstants .defaultFileName,
292- ),
293- );
372+ if (projectSetup.preexistingProject != true ) {
373+ final enableDb = projectSetup.enableDb! ;
374+ await ProjectCommands .createProject (
375+ cloudApiClient,
376+ logger: logger,
377+ projectId: projectId,
378+ enableDb: enableDb,
379+ projectDir: projectDir,
380+ configFilePath: p.join (
381+ projectDir,
382+ ProjectConfigFileConstants .defaultFileName,
383+ ),
384+ );
385+ }
294386
295387 if (! performDeploy) {
296388 logger.terminalCommand (
@@ -343,23 +435,31 @@ class ProjectLaunch {
343435 String ? projectDir;
344436 String ? projectId;
345437 bool ? enableDb;
438+ bool ? preexistingProject;
346439 bool ? performDeploy;
347440
348441 ProjectLaunch ({
349442 this .projectDir,
350443 this .projectId,
351444 this .enableDb,
445+ this .preexistingProject,
352446 this .performDeploy,
353447 });
354448
355449 @override
356450 String toString () {
357- final text = TablePrinter .columns (rows: [
358- ['Project directory' , projectDir],
359- ['Project Id' , projectId],
360- ['Enable DB' , enableDb == true ? 'yes' : 'no' ],
361- ['Perform deploy' , performDeploy == true ? 'yes' : 'no' ],
362- ]).toString ();
451+ final text = TablePrinter .columns (
452+ rows: [
453+ ['Project directory' , projectDir],
454+ if (preexistingProject != true ) ...[
455+ ['Project id' , projectId],
456+ ['Enable DB' , enableDb == true ? 'yes' : 'no' ],
457+ ] else
458+ ['Existing project id' , projectId],
459+ ['Perform deploy' , performDeploy == true ? 'yes' : 'no' ],
460+ ],
461+ columnSeparator: ' ' ,
462+ ).toString ();
363463 return text.substring (0 , text.length - 1 ); // trims last newline
364464 }
365465}
0 commit comments