@@ -424,122 +424,52 @@ const organizationsPlugin: FastifyPluginAsync = async (fastify, _options) => {
424424 const grpDisplayName = `${ request . params . orgId } Admin` ;
425425 const grpShortName = `${ OrganizationShortIdentifierMapping [ request . params . orgId as keyof typeof OrganizationShortIdentifierMapping ] } -adm` ;
426426
427- // Create external groups
428- if ( shouldCreateNewEntraGroup || ! githubTeamId ) {
429- const timestamp = new Date ( ) . toISOString ( ) ;
430- const updates : Record < string , any > = { updatedAt : timestamp } ;
431- const logStatements : TransactWriteItem [ ] = [ ] ;
432-
433- // Create Entra group if needed
434- if ( shouldCreateNewEntraGroup ) {
435- request . log . info (
436- `No Entra group exists for ${ request . params . orgId } . Creating new group...` ,
437- ) ;
427+ // Create Entra group if needed
428+ if ( shouldCreateNewEntraGroup ) {
429+ request . log . info (
430+ `No Entra group exists for ${ request . params . orgId } . Creating new group...` ,
431+ ) ;
438432
439- try {
440- const memberUpns = add . map ( ( u ) =>
441- u . username . replace ( "@illinois.edu" , "@acm.illinois.edu" ) ,
442- ) ;
443-
444- entraGroupId = await createM365Group (
445- entraIdToken ,
446- grpDisplayName ,
447- grpShortName ,
448- memberUpns ,
449- fastify . runEnvironment ,
450- ) ;
451-
452- request . log . info (
453- `Created Entra group ${ entraGroupId } for ${ request . params . orgId } ` ,
454- ) ;
455-
456- updates . leadsEntraGroupId = entraGroupId ;
457- const logStatement = buildAuditLogTransactPut ( {
458- entry : {
459- module : Modules . ORG_INFO ,
460- message : "Created Entra group for organization leads." ,
461- actor : request . username ! ,
462- target : request . params . orgId ,
463- } ,
464- } ) ;
465- if ( logStatement ) {
466- logStatements . push ( logStatement ) ;
467- }
468-
469- // Update dynamic membership query
470- const newQuery = await getLeadsM365DynamicQuery ( {
471- dynamoClient : fastify . dynamoClient ,
472- includeGroupIds : [ entraGroupId ] ,
473- } ) ;
474- if ( newQuery ) {
475- const groupToUpdate =
476- fastify . runEnvironment === "prod"
477- ? execCouncilGroupId
478- : execCouncilTestingGroupId ;
479- request . log . info (
480- "Changing Exec group membership dynamic query..." ,
481- ) ;
482- await setGroupMembershipRule (
483- entraIdToken ,
484- groupToUpdate ,
485- newQuery ,
486- ) ;
487- request . log . info ( "Changed Exec group membership dynamic query!" ) ;
488- }
489- } catch ( e ) {
490- request . log . error ( e , "Failed to create Entra group" ) ;
491- throw new InternalServerError ( {
492- message : "Failed to create Entra group for organization leads." ,
493- } ) ;
494- }
495- }
433+ try {
434+ const memberUpns = add . map ( ( u ) =>
435+ u . username . replace ( "@illinois.edu" , "@acm.illinois.edu" ) ,
436+ ) ;
496437
497- // Create GitHub team if needed
498- if ( ! githubTeamId ) {
499- request . log . info (
500- `No GitHub team exists for ${ request . params . orgId } . Creating new team...` ,
438+ entraGroupId = await createM365Group (
439+ entraIdToken ,
440+ grpDisplayName ,
441+ grpShortName ,
442+ memberUpns ,
443+ fastify . runEnvironment ,
501444 ) ;
502- const suffix = fastify . environmentConfig . GroupEmailSuffix ;
503- githubTeamId = await createGithubTeam ( {
504- orgId : fastify . environmentConfig . GithubOrgName ,
505- githubToken : fastify . secretConfig . github_pat ,
506- parentTeamId : fastify . environmentConfig . ExecGithubTeam ,
507- name : `${ grpShortName } ${ suffix === "" ? "" : `-${ suffix } ` } ` ,
508- description : grpDisplayName ,
509- logger : request . log ,
510- } ) ;
445+
511446 request . log . info (
512- `Created GitHub team " ${ githubTeamId } " for ${ request . params . orgId } leads. ` ,
447+ `Created Entra group ${ entraGroupId } for ${ request . params . orgId } ` ,
513448 ) ;
514- createdGithubTeam = true ;
515- updates . leadsGithubTeamId = githubTeamId ;
449+
450+ // Store Entra group ID immediately
516451 const logStatement = buildAuditLogTransactPut ( {
517452 entry : {
518453 module : Modules . ORG_INFO ,
519- message : ` Created GitHub team " ${ githubTeamId } " for organization leads.` ,
454+ message : " Created Entra group for organization leads." ,
520455 actor : request . username ! ,
521456 target : request . params . orgId ,
522457 } ,
523458 } ) ;
524- if ( logStatement ) {
525- logStatements . push ( logStatement ) ;
526- }
527- }
528459
529- // Store external group IDs if they were created
530- if ( Object . keys ( updates ) . length > 0 ) {
531- const storeIdsOperation = async ( ) => {
460+ const storeEntraIdOperation = async ( ) => {
532461 const commandTransaction = new TransactWriteItemsCommand ( {
533462 TransactItems : [
534- ...logStatements ,
463+ ...( logStatement ? [ logStatement ] : [ ] ) ,
535464 {
536465 Put : {
537466 TableName : genericConfig . SigInfoTableName ,
538467 Item : marshall (
539468 {
540469 primaryKey : `DEFINE#${ request . params . orgId } ` ,
541470 entryId : "0" ,
542- ...updates ,
471+ leadsEntraGroupId : entraGroupId ,
472+ updatedAt : new Date ( ) . toISOString ( ) ,
543473 } ,
544474 { removeUndefinedValues : true } ,
545475 ) ,
@@ -551,13 +481,92 @@ const organizationsPlugin: FastifyPluginAsync = async (fastify, _options) => {
551481 } ;
552482
553483 await retryDynamoTransactionWithBackoff (
554- storeIdsOperation ,
484+ storeEntraIdOperation ,
555485 request . log ,
556- `Store group IDs for ${ request . params . orgId } ` ,
486+ `Store Entra group ID for ${ request . params . orgId } ` ,
557487 ) ;
488+
489+ // Update dynamic membership query
490+ const newQuery = await getLeadsM365DynamicQuery ( {
491+ dynamoClient : fastify . dynamoClient ,
492+ includeGroupIds : [ entraGroupId ] ,
493+ } ) ;
494+ if ( newQuery ) {
495+ const groupToUpdate =
496+ fastify . runEnvironment === "prod"
497+ ? execCouncilGroupId
498+ : execCouncilTestingGroupId ;
499+ request . log . info ( "Changing Exec group membership dynamic query..." ) ;
500+ await setGroupMembershipRule ( entraIdToken , groupToUpdate , newQuery ) ;
501+ request . log . info ( "Changed Exec group membership dynamic query!" ) ;
502+ }
503+ } catch ( e ) {
504+ request . log . error ( e , "Failed to create Entra group" ) ;
505+ throw new InternalServerError ( {
506+ message : "Failed to create Entra group for organization leads." ,
507+ } ) ;
558508 }
559509 }
560510
511+ // Create GitHub team if needed
512+ if ( ! githubTeamId ) {
513+ request . log . info (
514+ `No GitHub team exists for ${ request . params . orgId } . Creating new team...` ,
515+ ) ;
516+ const suffix = fastify . environmentConfig . GroupEmailSuffix ;
517+ githubTeamId = await createGithubTeam ( {
518+ orgId : fastify . environmentConfig . GithubOrgName ,
519+ githubToken : fastify . secretConfig . github_pat ,
520+ parentTeamId : fastify . environmentConfig . ExecGithubTeam ,
521+ name : `${ grpShortName } ${ suffix === "" ? "" : `-${ suffix } ` } ` ,
522+ description : grpDisplayName ,
523+ logger : request . log ,
524+ } ) ;
525+ request . log . info (
526+ `Created GitHub team "${ githubTeamId } " for ${ request . params . orgId } leads.` ,
527+ ) ;
528+ createdGithubTeam = true ;
529+
530+ // Store GitHub team ID immediately
531+ const logStatement = buildAuditLogTransactPut ( {
532+ entry : {
533+ module : Modules . ORG_INFO ,
534+ message : `Created GitHub team "${ githubTeamId } " for organization leads.` ,
535+ actor : request . username ! ,
536+ target : request . params . orgId ,
537+ } ,
538+ } ) ;
539+
540+ const storeGithubIdOperation = async ( ) => {
541+ const commandTransaction = new TransactWriteItemsCommand ( {
542+ TransactItems : [
543+ ...( logStatement ? [ logStatement ] : [ ] ) ,
544+ {
545+ Put : {
546+ TableName : genericConfig . SigInfoTableName ,
547+ Item : marshall (
548+ {
549+ primaryKey : `DEFINE#${ request . params . orgId } ` ,
550+ entryId : "0" ,
551+ leadsGithubTeamId : githubTeamId ,
552+ updatedAt : new Date ( ) . toISOString ( ) ,
553+ } ,
554+ { removeUndefinedValues : true } ,
555+ ) ,
556+ } ,
557+ } ,
558+ ] ,
559+ } ) ;
560+ return await clients . dynamoClient . send ( commandTransaction ) ;
561+ } ;
562+
563+ await retryDynamoTransactionWithBackoff (
564+ storeGithubIdOperation ,
565+ request . log ,
566+ `Store GitHub team ID for ${ request . params . orgId } ` ,
567+ ) ;
568+ }
569+
561570 const commonArgs = {
562571 orgId : request . params . orgId ,
563572 actorUsername : request . username ! ,
@@ -574,26 +583,10 @@ const organizationsPlugin: FastifyPluginAsync = async (fastify, _options) => {
574583 removeLead ( { ...commonArgs , username } ) ,
575584 ) ;
576585
577- // Execute all add/remove operations sequentially to avoid transaction conflicts
578- const results : PromiseSettledResult < SQSMessage | null > [ ] = [ ] ;
579-
580- // Process adds
581- for ( const promise of addPromises ) {
582- const result = await promise . then (
583- ( value ) => ( { status : "fulfilled" as const , value } ) ,
584- ( reason ) => ( { status : "rejected" as const , reason } ) ,
585- ) ;
586- results . push ( result ) ;
587- }
588-
589- // Process removes
590- for ( const promise of removePromises ) {
591- const result = await promise . then (
592- ( value ) => ( { status : "fulfilled" as const , value } ) ,
593- ( reason ) => ( { status : "rejected" as const , reason } ) ,
594- ) ;
595- results . push ( result ) ;
596- }
586+ const results = await Promise . allSettled ( [
587+ ...addPromises ,
588+ ...removePromises ,
589+ ] ) ;
597590
598591 const failures = results . filter ( ( r ) => r . status === "rejected" ) ;
599592 if ( failures . length > 0 ) {
0 commit comments