@@ -57,7 +57,8 @@ public isolated function createProject(types:ProjectInput project, types:UserCon
5757 // Check for duplicate handler within the same org
5858 sql : ParameterizedQuery handlerCheckQuery = ` SELECT COUNT(*) as cnt FROM projects WHERE org_id = ${project .orgId } AND handler = ${handler }` ;
5959 stream < record {| int cnt;| }, sql : Error ?> handlerCheckStream = dbClient -> query (handlerCheckQuery );
60- record {| int cnt;| }[] handlerCheckResult = check from record {| int cnt;| } r in handlerCheckStream select r ;
60+ record {| int cnt;| }[] handlerCheckResult = check from record {| int cnt;| } r in handlerCheckStream
61+ select r ;
6162 if handlerCheckResult .length () > 0 && handlerCheckResult [0 ].cnt > 0 {
6263 return error (string ` Project handler '${handler }' is already taken in this organization` );
6364 }
@@ -98,8 +99,8 @@ public isolated function createProject(types:ProjectInput project, types:UserCon
9899
99100 // 1. Create project admin group
100101 string adminGroupId = uuid : createType1AsString ();
101- string groupName = string ` ${project . name } Admins` ;
102- string groupDescription = string ` Admin group for project: ${project . name }` ;
102+ string groupName = string ` ${handler } Admins` ;
103+ string groupDescription = string ` Admin group for project: ${handler }` ;
103104
104105 sql : ExecutionResult | sql :Error groupResult = dbClient -> execute (`
105106 INSERT INTO user_groups (group_id, group_name, org_uuid, description)
@@ -154,10 +155,18 @@ public isolated function createProject(types:ProjectInput project, types:UserCon
154155 // RBAC setup errors arrive as plain errors with their own messages via `fail`.
155156 if e is sql : Error {
156157 match classifySqlError (e ) {
157- DUPLICATE_KEY => { return error (" A project with this name or handler already exists in this organization" , e ); }
158- VALUE_TOO_LONG => { return error (" The provided value exceeds the maximum allowed length" , e ); }
159- FOREIGN_KEY_VIOLATION => { return error (" Cannot complete the operation due to a dependency constraint" , e ); }
160- _ => { return error (" An unexpected error occurred. Please contact your administrator." , e ); }
158+ DUPLICATE_KEY => {
159+ return error (" A project with this name or handler already exists in this organization" , e );
160+ }
161+ VALUE_TOO_LONG => {
162+ return error (" The provided value exceeds the maximum allowed length" , e );
163+ }
164+ FOREIGN_KEY_VIOLATION => {
165+ return error (" Cannot complete the operation due to a dependency constraint" , e );
166+ }
167+ _ => {
168+ return error (" An unexpected error occurred. Please contact your administrator." , e );
169+ }
161170 }
162171 }
163172 return e ;
@@ -336,9 +345,15 @@ public isolated function updateProjectWithInput(types:ProjectUpdateInput project
336345 log : printError (string ` Failed to update project ${project .id }` , 'error = e );
337346 if e is sql : Error {
338347 match classifySqlError (e ) {
339- DUPLICATE_KEY => { return error (" A project with this name already exists in this organization" , e ); }
340- VALUE_TOO_LONG => { return error (" The provided value exceeds the maximum allowed length" , e ); }
341- _ => { return error (" An unexpected error occurred. Please contact your administrator." , e ); }
348+ DUPLICATE_KEY => {
349+ return error (" A project with this name already exists in this organization" , e );
350+ }
351+ VALUE_TOO_LONG => {
352+ return error (" The provided value exceeds the maximum allowed length" , e );
353+ }
354+ _ => {
355+ return error (" An unexpected error occurred. Please contact your administrator." , e );
356+ }
342357 }
343358 }
344359 return error (" An unexpected error occurred while updating the project. Please contact your administrator." , e );
@@ -347,17 +362,60 @@ public isolated function updateProjectWithInput(types:ProjectUpdateInput project
347362 return ();
348363}
349364
350- // Delete a project by ID (this will cascade delete all components, runtimes, roles, and user role assignments)
365+ // Delete a project by ID and clean up the auto-created project admin group.
351366public isolated function deleteProject(string projectId ) returns error ? {
352- sql : ParameterizedQuery deleteQuery = ` DELETE FROM projects WHERE project_id = ${projectId }` ;
353- var result = dbClient -> execute (deleteQuery );
354- if result is sql : Error {
355- log : printError (string ` Failed to delete project ${projectId }` , 'error = result );
356- match classifySqlError (result ) {
357- FOREIGN_KEY_VIOLATION => { return error (" Cannot delete project because it has dependent resources" , result ); }
358- _ => { return error (" An unexpected error occurred. Please contact your administrator." , result ); }
367+ do {
368+ transaction {
369+ record {| string name; string handler; int org_id;| }| sql : Error projectRecord = dbClient -> queryRow (
370+ ` SELECT name, handler, org_id FROM projects WHERE project_id = ${projectId }`
371+ );
372+ if projectRecord is sql : NoRowsError {
373+ fail error (string ` Project with ID ${projectId } not found` );
374+ }
375+ if projectRecord is sql : Error {
376+ fail projectRecord ;
377+ }
378+
379+ string projectAdminRoleId = check getProjectAdminRoleId ();
380+ string handlerBasedAdminGroup = string ` ${projectRecord .handler } Admins` ;
381+
382+ sql : ExecutionResult | sql :Error groupDeleteResult = dbClient -> execute (`
383+ DELETE FROM user_groups
384+ WHERE group_name = ${handlerBasedAdminGroup }
385+ AND org_uuid = ${projectRecord .org_id }
386+ AND group_id IN (
387+ SELECT group_id FROM group_role_mapping
388+ WHERE project_uuid = ${projectId } AND role_id = ${projectAdminRoleId }
389+ )
390+ ` );
391+ if groupDeleteResult is sql : Error {
392+ fail groupDeleteResult ;
393+ }
394+
395+ sql : ExecutionResult | sql :Error projectDeleteResult = dbClient -> execute (
396+ ` DELETE FROM projects WHERE project_id = ${projectId }`
397+ );
398+ if projectDeleteResult is sql : Error {
399+ fail projectDeleteResult ;
400+ }
401+
402+ check commit ;
359403 }
404+ } on fail error e {
405+ log : printError (string ` Failed to delete project ${projectId }` , 'error = e );
406+ if e is sql : Error {
407+ match classifySqlError (e ) {
408+ FOREIGN_KEY_VIOLATION => {
409+ return error (" Cannot delete project because it has dependent resources" , e );
410+ }
411+ _ => {
412+ return error (" An unexpected error occurred. Please contact your administrator." , e );
413+ }
414+ }
415+ }
416+ return e ;
360417 }
418+
361419 log : printInfo (string ` Successfully deleted project ${projectId }` );
362420 return ();
363421}
@@ -424,4 +482,4 @@ public isolated function checkProjectHandlerAvailability(int orgId, string proje
424482 alternateHandlerCandidate : alternateCandidate
425483 };
426484}
427-
485+
0 commit comments