99package org .opensearch .security .spi .resources .sharing ;
1010
1111import java .io .IOException ;
12+ import java .util .EnumMap ;
1213import java .util .HashMap ;
1314import java .util .Map ;
1415import java .util .Set ;
@@ -82,16 +83,15 @@ public ShareWith updateSharingInfo(String accessLevel, Recipients target) {
8283 }
8384
8485 @ Override
85- public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
86- builder .startObject ();
87-
88- for ( String accessLevel : sharingInfo . keySet ()) {
89- builder . field ( accessLevel );
90- Recipients recipients = sharingInfo . get ( accessLevel );
91- recipients .toXContent (builder , params );
86+ public XContentBuilder toXContent (XContentBuilder b , Params params ) throws IOException {
87+ b .startObject ();
88+ for ( Map . Entry < String , Recipients > e : this . sharingInfo . entrySet ()) {
89+ Recipients norm = pruneRecipients ( e . getValue ());
90+ if ( norm == null ) continue ; // skip empty level
91+ b . field ( e . getKey () );
92+ norm .toXContent (b , params ); // TODO ensure this skips empty arrays too
9293 }
93-
94- return builder .endObject ();
94+ return b .endObject ();
9595 }
9696
9797 public static ShareWith fromXContent (XContentParser parser ) throws IOException {
@@ -148,7 +148,7 @@ public ShareWith add(ShareWith other) {
148148 return orig ;
149149 });
150150 }
151- return new ShareWith (updated );
151+ return new ShareWith (updated ). prune () ;
152152 }
153153
154154 /**
@@ -164,9 +164,39 @@ public ShareWith revoke(ShareWith other) {
164164 Recipients revokeRecipients = entry .getValue ();
165165 updated .computeIfPresent (level , (lvl , orig ) -> {
166166 orig .revoke (revokeRecipients );
167- return orig ;
167+ return pruneRecipients ( orig ); // removes any null levels
168168 });
169169 }
170- return new ShareWith (updated );
170+ return new ShareWith (updated ). prune () ;
171171 }
172+
173+ /** Return a normalized ShareWith with no empty buckets and no empty action-groups. */
174+ public ShareWith prune () {
175+ Map <String , Recipients > cleaned = new HashMap <>();
176+ for (Map .Entry <String , Recipients > e : this .sharingInfo .entrySet ()) {
177+ Recipients prunedRecipients = pruneRecipients (e .getValue ());
178+ if (prunedRecipients != null ) {
179+ cleaned .put (e .getKey (), prunedRecipients );
180+ }
181+ }
182+ return new ShareWith (cleaned );
183+ }
184+
185+ private static Recipients pruneRecipients (Recipients r ) {
186+ if (r == null ) return null ;
187+ Map <Recipient , Set <String >> src = r .getRecipients ();
188+ if (src == null || src .isEmpty ()) return null ;
189+
190+ Map <Recipient , Set <String >> cleaned = new EnumMap <>(Recipient .class );
191+ for (Map .Entry <Recipient , Set <String >> e : src .entrySet ()) {
192+ Set <String > vals = e .getValue ();
193+ if (vals == null ) continue ;
194+ Set <String > filtered = vals .stream ()
195+ .filter (s -> s != null && !s .isBlank ())
196+ .collect (java .util .stream .Collectors .toCollection (java .util .LinkedHashSet ::new ));
197+ if (!filtered .isEmpty ()) cleaned .put (e .getKey (), filtered );
198+ }
199+ return cleaned .isEmpty () ? null : new Recipients (cleaned ); // use your builder/ctor
200+ }
201+
172202}
0 commit comments