Skip to content

Commit 31be592

Browse files
committed
Fix fetching nextLink to allow iteration across all pages
1 parent 62d6deb commit 31be592

File tree

4 files changed

+119
-46
lines changed

4 files changed

+119
-46
lines changed

src/main/java/com/microsoft/graph/core/tasks/PageIterator.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
/**
1919
* A class for iterating through pages of a collection
20-
* Uses to automatically pages through result sets across multiple calls and process each item in the result set.
20+
* Use to automatically page through result sets across multiple calls and process each item in the result set.
2121
* @param <TEntity> The type of the entity returned in the collection. This type must implement {@link Parsable}
2222
* @param <TCollectionPage> The Microsoft Graph collection response type returned in the collection response. This type must implement {@link Parsable} and {@link AdditionalDataHolder}
2323
*/
@@ -302,16 +302,14 @@ protected static <TEntity extends Parsable, TCollectionPage extends Parsable & A
302302
}
303303
private static <TCollectionPage extends Parsable & AdditionalDataHolder> String extractNextLinkFromParsable(@Nonnull TCollectionPage parsableCollection, @Nullable String getNextLinkMethodName) throws ReflectiveOperationException {
304304
String methodName = getNextLinkMethodName == null ? CoreConstants.CollectionResponseMethods.GET_ODATA_NEXT_LINK : getNextLinkMethodName;
305-
Method[] methods = parsableCollection.getClass().getDeclaredMethods();
305+
Method[] methods = parsableCollection.getClass().getMethods();
306306
String nextLink;
307-
if(Arrays.stream(methods).anyMatch(m -> m.getName().equals(methodName))) {
308-
try {
309-
nextLink = (String) parsableCollection.getClass().getDeclaredMethod(methodName).invoke(parsableCollection);
310-
if(!Compatibility.isBlank(nextLink)) {
311-
return nextLink;
312-
}
313-
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
314-
throw new ReflectiveOperationException("Could not extract nextLink from parsableCollection.");
307+
Optional<Method> nextLinkOptional = Arrays.stream(methods).filter(m -> m.getName().equals(methodName)).findFirst();
308+
if (nextLinkOptional.isPresent()) {
309+
Method nextLinkMethod = nextLinkOptional.get();
310+
nextLink = (String) nextLinkMethod.invoke(parsableCollection);
311+
if(!Compatibility.isBlank(nextLink)) {
312+
return nextLink;
315313
}
316314
}
317315
nextLink = (String) parsableCollection.getAdditionalData().get(CoreConstants.OdataInstanceAnnotations.NEXT_LINK);

src/test/java/com/microsoft/graph/core/tasks/PageIteratorTest.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.HashMap;
1919
import java.util.LinkedList;
2020
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicBoolean;
22+
import java.util.concurrent.atomic.AtomicInteger;
2123
import java.util.function.Function;
2224
import java.util.function.UnaryOperator;
2325

@@ -152,13 +154,10 @@ void given_CollectionPage_Without_NextLink_Property_It_Iterates_Across_Pages() t
152154
secondPage.getValue().add(testEventItem);
153155
}
154156

155-
final Boolean[] reachedNextPage = {false};
157+
AtomicInteger totalItemsProcessed = new AtomicInteger(0);
156158

157159
Function<TestEventItem, Boolean> processPageItemCallback = item -> {
158-
if(item.getSubject().contains("Second Page Test Event")) {
159-
reachedNextPage[0] = true;
160-
return false;
161-
}
160+
totalItemsProcessed.incrementAndGet();
162161
return true;
163162
};
164163

@@ -172,10 +171,9 @@ void given_CollectionPage_Without_NextLink_Property_It_Iterates_Across_Pages() t
172171

173172
pageIterator.iterate();
174173

175-
assertTrue(reachedNextPage[0]);
176-
assertEquals(PageIterator.PageIteratorState.PAUSED, pageIterator.getPageIteratorState());
177-
assertEquals("http://localhost/events?$skip=11", pageIterator.getNextLink());
178-
174+
assertEquals(PageIterator.PageIteratorState.COMPLETE, pageIterator.getPageIteratorState());
175+
assertEquals("", pageIterator.getNextLink());
176+
assertEquals(inputEventCount + secondPageEventCount, totalItemsProcessed.get());
179177
}
180178
@Test
181179
void given_CollectionPage_Delta_Link_Property_It_Iterates_Across_Pages() throws ReflectiveOperationException, ApiException {
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.microsoft.graph.core.testModels;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.Objects;
6+
7+
import com.microsoft.kiota.serialization.AdditionalDataHolder;
8+
import com.microsoft.kiota.serialization.Parsable;
9+
import com.microsoft.kiota.serialization.ParseNode;
10+
import com.microsoft.kiota.serialization.SerializationWriter;
11+
12+
public class BaseCollectionPaginationCountResponse implements AdditionalDataHolder, Parsable {
13+
public Map<String, Object> additionalData;
14+
private String odataNextLink;
15+
private Long odataCount;
16+
/**
17+
* Instantiates a new BaseCollectionPaginationCountResponse and sets the default values.
18+
*/
19+
public BaseCollectionPaginationCountResponse() {
20+
this.setAdditionalData(new HashMap<>());
21+
}
22+
/**
23+
* Creates a new instance of the appropriate class based on discriminator value
24+
* @param parseNode The parse node to use to read the discriminator value and create the object
25+
* @return a BaseCollectionPaginationCountResponse
26+
*/
27+
@jakarta.annotation.Nonnull
28+
public static BaseCollectionPaginationCountResponse createFromDiscriminatorValue(@jakarta.annotation.Nonnull final ParseNode parseNode) {
29+
Objects.requireNonNull(parseNode);
30+
return new BaseCollectionPaginationCountResponse();
31+
}
32+
/**
33+
* Gets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.
34+
* @return a Map<String, Object>
35+
*/
36+
@jakarta.annotation.Nonnull
37+
public Map<String, Object> getAdditionalData() {
38+
return this.additionalData;
39+
}
40+
/**
41+
* The deserialization information for the current model
42+
* @return a Map<String, java.util.function.Consumer<ParseNode>>
43+
*/
44+
@jakarta.annotation.Nonnull
45+
public Map<String, java.util.function.Consumer<ParseNode>> getFieldDeserializers() {
46+
final HashMap<String, java.util.function.Consumer<ParseNode>> deserializerMap = new HashMap<String, java.util.function.Consumer<ParseNode>>(2);
47+
deserializerMap.put("@odata.count", (n) -> { this.setOdataCount(n.getLongValue()); });
48+
deserializerMap.put("@odata.nextLink", (n) -> { this.setOdataNextLink(n.getStringValue()); });
49+
return deserializerMap;
50+
}
51+
/**
52+
* Gets the @odata.count property value. The OdataCount property
53+
* @return a Long
54+
*/
55+
@jakarta.annotation.Nullable
56+
public Long getOdataCount() {
57+
return this.odataCount;
58+
}
59+
/**
60+
* Gets the @odata.nextLink property value. The OdataNextLink property
61+
* @return a String
62+
*/
63+
@jakarta.annotation.Nullable
64+
public String getOdataNextLink() {
65+
return this.odataNextLink;
66+
}
67+
/**
68+
* Serializes information the current object
69+
* @param writer Serialization writer to use to serialize this model
70+
*/
71+
public void serialize(@jakarta.annotation.Nonnull final SerializationWriter writer) {
72+
Objects.requireNonNull(writer);
73+
writer.writeLongValue("@odata.count", this.getOdataCount());
74+
writer.writeStringValue("@odata.nextLink", this.getOdataNextLink());
75+
writer.writeAdditionalData(this.getAdditionalData());
76+
}
77+
/**
78+
* Sets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.
79+
* @param value Value to set for the AdditionalData property.
80+
*/
81+
public void setAdditionalData(@jakarta.annotation.Nullable final Map<String, Object> value) {
82+
this.additionalData = value;
83+
}
84+
/**
85+
* Sets the @odata.count property value. The OdataCount property
86+
* @param value Value to set for the @odata.count property.
87+
*/
88+
public void setOdataCount(@jakarta.annotation.Nullable final Long value) {
89+
this.odataCount = value;
90+
}
91+
/**
92+
* Sets the @odata.nextLink property value. The OdataNextLink property
93+
* @param value Value to set for the @odata.nextLink property.
94+
*/
95+
public void setOdataNextLink(@jakarta.annotation.Nullable final String value) {
96+
this.odataNextLink = value;
97+
}
98+
}

src/test/java/com/microsoft/graph/core/testModels/TestEventsResponse.java

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,11 @@
1313
import java.util.Objects;
1414
import java.util.function.Consumer;
1515

16-
public class TestEventsResponse implements Parsable, AdditionalDataHolder {
17-
public Map<String, Object> additionalData;
18-
private String odataNextLink;
16+
public class TestEventsResponse extends BaseCollectionPaginationCountResponse {
1917
public List<TestEventItem> value;
2018

2119
public TestEventsResponse() {
22-
additionalData = new HashMap<>();
23-
}
24-
25-
@Nonnull
26-
public Map<String, Object> getAdditionalData() {
27-
return additionalData;
28-
}
29-
30-
public void setAdditionalData(Map<String, Object> additionalData) {
31-
this.additionalData = additionalData;
32-
}
33-
34-
public String getOdataNextLink() {
35-
return odataNextLink;
36-
}
37-
38-
public void setOdataNextLink(String odataNextLink) {
39-
this.odataNextLink = odataNextLink;
20+
super();
4021
}
4122

4223
public List<TestEventItem> getValue() {
@@ -48,17 +29,15 @@ public void setValue(List<TestEventItem> value) {
4829
}
4930
@Nonnull
5031
public Map<String, Consumer<ParseNode>> getFieldDeserializers() {
51-
HashMap<String, Consumer<ParseNode>> fieldDeserializers = new HashMap<>();
52-
fieldDeserializers.put("@odata.nextLink", (n) -> setOdataNextLink(n.getStringValue()));
53-
fieldDeserializers.put("value", (n) -> setValue(n.getCollectionOfObjectValues(TestEventItem::createFromDiscriminatorValue)));
54-
return fieldDeserializers;
32+
final HashMap<String, java.util.function.Consumer<ParseNode>> deserializerMap = new HashMap<String, java.util.function.Consumer<ParseNode>>(super.getFieldDeserializers());
33+
deserializerMap.put("value", (n) -> { this.setValue(n.getCollectionOfObjectValues(TestEventItem::createFromDiscriminatorValue)); });
34+
return deserializerMap;
5535
}
5636

5737
public void serialize(@Nonnull SerializationWriter writer) {
5838
Objects.requireNonNull(writer);
59-
writer.writeStringValue("@odata.nextLink", getOdataNextLink());
39+
super.serialize(writer);
6040
writer.writeCollectionOfObjectValues("value", getValue());
61-
writer.writeAdditionalData(getAdditionalData());
6241
}
6342

6443
public static TestEventsResponse createFromDiscriminatorValue(ParseNode parseNode) {

0 commit comments

Comments
 (0)