@@ -13,15 +13,12 @@ export interface MemberRoleRecord {
1313
1414type RoleSubjectRecord = {
1515 subjectID ?: string | number ;
16+ subjectId ?: string | number ;
1617 userId ?: string | number ;
1718 handle ?: string ;
1819 email ?: string ;
1920} ;
2021
21- type RoleDetailResponse = {
22- subjects ?: RoleSubjectRecord [ ] ;
23- } ;
24-
2522/**
2623 * Member and identity enrichment service.
2724 *
@@ -213,9 +210,9 @@ export class MemberService {
213210 * Looks up role members from Identity API by role name.
214211 *
215212 * Resolves `/roles?filter=roleName=<roleName>` and then
216- * `/roles/:id?selector=subjects&perPage=200` , returning the merged
217- * `subjects` list
218- * normalized to `MemberDetail` shape .
213+ * `/roles/:id` with subject expansion , returning the merged `subjects` list
214+ * normalized to `MemberDetail` shape. Supports both direct array responses
215+ * and the legacy `{ result: { content } }` Identity response wrapper .
219216 *
220217 * Role-detail lookups are tolerant to partial failures; one failing role id
221218 * does not prevent subjects from other matching roles from being returned.
@@ -250,9 +247,9 @@ export class MemberService {
250247 } ) ,
251248 ) ;
252249
253- const roles = Array . isArray ( rolesResponse . data )
254- ? ( rolesResponse . data as MemberRoleRecord [ ] )
255- : [ ] ;
250+ const roles = this . extractArrayPayload < MemberRoleRecord > (
251+ rolesResponse . data ,
252+ ) ;
256253
257254 const roleIds = roles
258255 . filter (
@@ -276,14 +273,14 @@ export class MemberService {
276273 'Content-Type' : 'application/json' ,
277274 } ,
278275 params : {
276+ fields : 'subjects' ,
279277 selector : 'subjects' ,
280278 perPage : 200 ,
281279 } ,
282280 } ) ,
283281 ) ;
284282
285- const payload = roleResponse . data as RoleDetailResponse ;
286- return Array . isArray ( payload ?. subjects ) ? payload . subjects : [ ] ;
283+ return this . extractRoleSubjects ( roleResponse . data ) ;
287284 } ) ,
288285 ) ;
289286
@@ -310,7 +307,7 @@ export class MemberService {
310307 . trim ( )
311308 . toLowerCase ( ) ;
312309 const subjectId = String (
313- subject . subjectID || subject . userId || '' ,
310+ subject . subjectID || subject . subjectId || subject . userId || '' ,
314311 ) . trim ( ) ;
315312
316313 const key = email || subjectId ;
@@ -319,7 +316,8 @@ export class MemberService {
319316 }
320317
321318 uniqueSubjects . set ( key , {
322- userId : subject . subjectID || subject . userId || null ,
319+ userId :
320+ subject . subjectID || subject . subjectId || subject . userId || null ,
323321 handle : subject . handle || null ,
324322 email,
325323 } ) ;
@@ -337,4 +335,90 @@ export class MemberService {
337335 async getRoleSubjectsByRoleName ( roleName : string ) : Promise < MemberDetail [ ] > {
338336 return this . getRoleSubjects ( roleName ) ;
339337 }
338+
339+ /**
340+ * Extracts an array payload from Identity responses.
341+ *
342+ * @param payload Identity API response body in direct or legacy wrapped form.
343+ * @returns Extracted array or an empty array when the shape is unsupported.
344+ */
345+ private extractArrayPayload < T > ( payload : unknown ) : T [ ] {
346+ if ( Array . isArray ( payload ) ) {
347+ return payload as T [ ] ;
348+ }
349+
350+ if ( ! payload || typeof payload !== 'object' ) {
351+ return [ ] ;
352+ }
353+
354+ const response = payload as {
355+ content ?: unknown ;
356+ result ?: {
357+ content ?: unknown ;
358+ } ;
359+ } ;
360+
361+ if ( Array . isArray ( response . result ?. content ) ) {
362+ return response . result . content as T [ ] ;
363+ }
364+
365+ if ( Array . isArray ( response . content ) ) {
366+ return response . content as T [ ] ;
367+ }
368+
369+ return [ ] ;
370+ }
371+
372+ /**
373+ * Extracts role subjects from Identity role-detail responses.
374+ *
375+ * @param payload Identity role-detail body in direct, legacy wrapped, or list form.
376+ * @returns Role subject records or an empty array when no subjects are present.
377+ */
378+ private extractRoleSubjects ( payload : unknown ) : RoleSubjectRecord [ ] {
379+ const directSubjects = this . extractSubjectsFromObject ( payload ) ;
380+ if ( directSubjects . length > 0 ) {
381+ return directSubjects ;
382+ }
383+
384+ const listPayload = this . extractArrayPayload < RoleSubjectRecord > ( payload ) ;
385+ if ( listPayload . length > 0 ) {
386+ return listPayload ;
387+ }
388+
389+ if ( ! payload || typeof payload !== 'object' ) {
390+ return [ ] ;
391+ }
392+
393+ const response = payload as {
394+ content ?: unknown ;
395+ result ?: {
396+ content ?: unknown ;
397+ } ;
398+ } ;
399+
400+ const resultSubjects = this . extractSubjectsFromObject (
401+ response . result ?. content ,
402+ ) ;
403+ if ( resultSubjects . length > 0 ) {
404+ return resultSubjects ;
405+ }
406+
407+ return this . extractSubjectsFromObject ( response . content ) ;
408+ }
409+
410+ /**
411+ * Extracts a `subjects` array from an object-like response fragment.
412+ *
413+ * @param payload Response fragment that may contain a `subjects` property.
414+ * @returns Subjects array or an empty array when absent.
415+ */
416+ private extractSubjectsFromObject ( payload : unknown ) : RoleSubjectRecord [ ] {
417+ if ( ! payload || typeof payload !== 'object' || Array . isArray ( payload ) ) {
418+ return [ ] ;
419+ }
420+
421+ const subjects = ( payload as { subjects ?: unknown } ) . subjects ;
422+ return Array . isArray ( subjects ) ? ( subjects as RoleSubjectRecord [ ] ) : [ ] ;
423+ }
340424}
0 commit comments