diff --git a/CHANGELOG.md b/CHANGELOG.md index c13be799f..1412bdec5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +## [3.0.6] - 2023-07-11 + +### Added + +- Added the PageIterator functionality for Kiota generated service libraries. + ## [3.0.5] - 2023-06-15 ### Added diff --git a/android/build.gradle b/android/build.gradle index 20fe6e9d4..f71044274 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,9 +2,6 @@ buildscript { repositories { google() gradlePluginPortal() - maven { - url "https://plugins.gradle.org/m2/" - } } dependencies { diff --git a/gradle.properties b/gradle.properties index 8aeef0b5d..7923893cb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,7 +25,7 @@ mavenGroupId = com.microsoft.graph mavenArtifactId = microsoft-graph-core mavenMajorVersion = 3 mavenMinorVersion = 0 -mavenPatchVersion = 5 +mavenPatchVersion = 6 mavenArtifactSuffix = #These values are used to run functional tests diff --git a/src/main/java/com/microsoft/graph/CoreConstants.java b/src/main/java/com/microsoft/graph/CoreConstants.java index 1e6413863..4c3bc3d6c 100644 --- a/src/main/java/com/microsoft/graph/CoreConstants.java +++ b/src/main/java/com/microsoft/graph/CoreConstants.java @@ -11,7 +11,7 @@ private CoreConstants() {} private static class VersionValues { private static final int MAJOR = 3; private static final int MINOR = 0; - private static final int PATCH = 5; + private static final int PATCH = 6; } /** @@ -88,7 +88,27 @@ private Serialization(){} public static final String ODATA_TYPE = "@odata.type"; /** OData nextLink property */ public static final String ODATA_NEXT_LINK = "@nextLink"; + } + /** + * Odata Instance Annotation Constants + */ + public static class OdataInstanceAnnotations { + private OdataInstanceAnnotations(){} + /** NextLink odata instance annotation */ + public static final String NEXT_LINK = "@odata.nextLink"; + /** DeltaLink odata instance annotation */ + public static final String DELTA_LINK = "@odata.deltaLink"; } + /** + * Collection Response Method Name Constants + */ + public static class CollectionResponseMethods { + private CollectionResponseMethods(){} + /** Method name constant for getOdataDeltaLink in collection responses*/ + public static final String GET_ODATA_DELTA_LINK = "getOdataDeltaLink"; + /** Method name constant for getOdataNextLink in collection responses*/ + public static final String GET_ODATA_NEXT_LINK = "getOdataNextLink"; + } } diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestContent.java b/src/main/java/com/microsoft/graph/content/BatchRequestContent.java index dc4de85af..86d63da5c 100644 --- a/src/main/java/com/microsoft/graph/content/BatchRequestContent.java +++ b/src/main/java/com/microsoft/graph/content/BatchRequestContent.java @@ -1,5 +1,6 @@ package com.microsoft.graph.content; +import com.google.common.base.Strings; import com.google.gson.*; import com.google.gson.stream.JsonWriter; import com.microsoft.graph.CoreConstants; @@ -127,9 +128,8 @@ public CompletableFuture addBatchRequestStep(@Nonnull RequestInformation * @return True if the request was removed, false otherwise. */ public boolean removeBatchRequestStepWithId(@Nonnull String requestId) { - Objects.requireNonNull(requestId); - if(requestId.isEmpty()) { - throw new IllegalArgumentException("requestId cannot be empty."); + if(Strings.isNullOrEmpty(requestId)) { + throw new IllegalArgumentException("requestId cannot be null or empty."); } boolean isRemoved = false; if(this.batchRequestSteps.containsKey(requestId)) { @@ -262,7 +262,7 @@ private boolean containsCorrespondingRequestId(List dependsOn) { private String getRelativeUrl(HttpUrl url) { String query = url.encodedQuery(); //Query must be encoded in order for batch requests to work. String path = url.encodedPath().substring(5); - if(query == null || query.isEmpty()) { + if(Strings.isNullOrEmpty(query)) { return path; } return (path + "?" + query); // `v1.0/` and `beta/` are both 5 characters diff --git a/src/main/java/com/microsoft/graph/exceptions/ServiceException.java b/src/main/java/com/microsoft/graph/exceptions/ServiceException.java index 72bca1916..c61e722b6 100644 --- a/src/main/java/com/microsoft/graph/exceptions/ServiceException.java +++ b/src/main/java/com/microsoft/graph/exceptions/ServiceException.java @@ -1,5 +1,6 @@ package com.microsoft.graph.exceptions; +import com.google.common.base.Strings; import com.microsoft.kiota.ApiException; import com.microsoft.kiota.serialization.AdditionalDataHolder; import com.microsoft.kiota.serialization.Parsable; @@ -105,7 +106,7 @@ public void setRawResponseBody(@Nonnull String rawResponseBody) { * @return a boolean declaring whether the error code was found within the error stack. */ public boolean isMatch(@Nonnull String errorCode) { - if(errorCode.trim().isEmpty()) { + if(Strings.isNullOrEmpty(errorCode)) { throw new IllegalArgumentException("Parameter 'errorCode 'cannot be null or empty"); } return (this.rawResponseBody.toLowerCase(Locale.ROOT).contains(errorCode.toLowerCase(Locale.ROOT))) diff --git a/src/main/java/com/microsoft/graph/models/BatchRequestStep.java b/src/main/java/com/microsoft/graph/models/BatchRequestStep.java index d10049af5..7133e4ccf 100644 --- a/src/main/java/com/microsoft/graph/models/BatchRequestStep.java +++ b/src/main/java/com/microsoft/graph/models/BatchRequestStep.java @@ -1,5 +1,6 @@ package com.microsoft.graph.models; +import com.google.common.base.Strings; import com.microsoft.graph.exceptions.ErrorConstants; import okhttp3.Request; @@ -23,10 +24,9 @@ public class BatchRequestStep { * @param request The request */ public BatchRequestStep(@Nonnull String requestId, @Nonnull Request request) { - Objects.requireNonNull(requestId, ErrorConstants.Messages.NULL_PARAMETER + "requestId"); Objects.requireNonNull(request, ErrorConstants.Messages.NULL_PARAMETER + "request"); - if(requestId.isEmpty()) { - throw new IllegalArgumentException("requestId cannot be empty."); + if(Strings.isNullOrEmpty(requestId)) { + throw new IllegalArgumentException("requestId cannot be null or empty."); } this.requestId = requestId; this.request = request; @@ -80,9 +80,8 @@ public void setDependsOn(@Nonnull List dependsOn) { * @param id The id of the request to add to the dependsOn list. */ public void addDependsOnId(@Nonnull String id) { - Objects.requireNonNull(id); - if(id.isEmpty()) { - throw new IllegalArgumentException("id cannot be empty"); + if(Strings.isNullOrEmpty(id)) { + throw new IllegalArgumentException("id cannot be null or empty"); } if(dependsOn == null) { dependsOn = new ArrayList<>(); diff --git a/src/main/java/com/microsoft/graph/models/UploadSession.java b/src/main/java/com/microsoft/graph/models/UploadSession.java index 902ca70f0..2cc18ed18 100644 --- a/src/main/java/com/microsoft/graph/models/UploadSession.java +++ b/src/main/java/com/microsoft/graph/models/UploadSession.java @@ -1,5 +1,6 @@ package com.microsoft.graph.models; +import com.google.common.base.Strings; import com.microsoft.graph.exceptions.ErrorConstants; import com.microsoft.kiota.serialization.ParseNode; import com.microsoft.kiota.serialization.SerializationWriter; @@ -46,9 +47,8 @@ public String getUploadUrl() { * @param uploadUrl The upload url for the session. */ public void setUploadUrl(@Nonnull final String uploadUrl) { - Objects.requireNonNull(uploadUrl, ErrorConstants.Messages.NULL_PARAMETER + UPLOAD_URL ); - if(uploadUrl.isEmpty()) - throw new IllegalArgumentException("uploadUrl cannot be empty"); + if(Strings.isNullOrEmpty(uploadUrl)) + throw new IllegalArgumentException("uploadUrl cannot be null or empty"); this.uploadUrl = uploadUrl; } /** diff --git a/src/main/java/com/microsoft/graph/requests/BaseGraphRequestAdapter.java b/src/main/java/com/microsoft/graph/requests/BaseGraphRequestAdapter.java index 9e2964b40..4acdbb50a 100644 --- a/src/main/java/com/microsoft/graph/requests/BaseGraphRequestAdapter.java +++ b/src/main/java/com/microsoft/graph/requests/BaseGraphRequestAdapter.java @@ -1,5 +1,6 @@ package com.microsoft.graph.requests; +import com.google.common.base.Strings; import com.microsoft.kiota.authentication.AuthenticationProvider; import com.microsoft.kiota.http.OkHttpRequestAdapter; import com.microsoft.kiota.serialization.ParseNodeFactory; @@ -62,7 +63,7 @@ private static final EnumMap getCloudList() { @SuppressWarnings("LambdaLast") public BaseGraphRequestAdapter(@Nonnull final AuthenticationProvider authenticationProvider, @Nullable final ParseNodeFactory parseNodeFactory, @Nullable final SerializationWriterFactory serializationWriterFactory, @Nullable final OkHttpClient client, @Nullable final GraphClientOption graphClientOption, @Nullable String baseUrl) { super(authenticationProvider, parseNodeFactory, serializationWriterFactory, client != null ? client : GraphClientFactory.create(graphClientOption).build()); - if (baseUrl != null && !baseUrl.isEmpty()) { + if (!Strings.isNullOrEmpty(baseUrl)) { setBaseUrl(baseUrl); } else { setBaseUrl(determineBaseAddress(null, null)); diff --git a/src/main/java/com/microsoft/graph/requests/GraphClientOption.java b/src/main/java/com/microsoft/graph/requests/GraphClientOption.java index 826a06628..7b490990d 100644 --- a/src/main/java/com/microsoft/graph/requests/GraphClientOption.java +++ b/src/main/java/com/microsoft/graph/requests/GraphClientOption.java @@ -6,6 +6,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import com.google.common.base.Strings; import com.microsoft.graph.CoreConstants; import com.microsoft.graph.exceptions.ErrorConstants; import com.microsoft.kiota.RequestOption; @@ -35,9 +36,8 @@ public GraphClientOption() { * @param clientRequestId the client request id to set, preferably the string representation of a GUID */ public void setClientRequestId(@Nonnull final String clientRequestId) { - Objects.requireNonNull(clientRequestId, ErrorConstants.Messages.NULL_PARAMETER + "clientRequestId"); - if(clientRequestId.isEmpty()) { - throw new IllegalArgumentException("clientRequestId cannot be empty"); + if(Strings.isNullOrEmpty(clientRequestId)) { + throw new IllegalArgumentException("clientRequestId cannot be null or empty"); } this.clientRequestId = clientRequestId; } @@ -57,10 +57,9 @@ public String getClientRequestId() { * @param clientLibraryVersion client library version specified by user. */ public void setClientLibraryVersion(@Nonnull final String clientLibraryVersion) { - Objects.requireNonNull(clientLibraryVersion, ErrorConstants.Messages.NULL_PARAMETER + "clientLibraryVersion"); - if(clientLibraryVersion.isEmpty()) + if(Strings.isNullOrEmpty(clientLibraryVersion)) { - throw new IllegalArgumentException("clientLibraryVersion cannot be empty"); + throw new IllegalArgumentException("clientLibraryVersion cannot be null or empty"); } this.clientLibraryVersion = clientLibraryVersion; } @@ -78,10 +77,9 @@ public String getClientLibraryVersion() { * @param coreLibraryVersion core library version specified by user. */ public void setCoreLibraryVersion(@Nonnull final String coreLibraryVersion) { - Objects.requireNonNull(coreLibraryVersion, ErrorConstants.Messages.NULL_PARAMETER + "coreLibraryVersion"); - if(coreLibraryVersion.isEmpty()) + if(Strings.isNullOrEmpty(coreLibraryVersion)) { - throw new IllegalArgumentException("coreLibraryVersion cannot be empty"); + throw new IllegalArgumentException("coreLibraryVersion cannot be null or empty"); } this.coreLibraryVersion = coreLibraryVersion; } @@ -99,10 +97,9 @@ public String getCoreLibraryVersion() { * @param graphServiceVersion the version of the Api endpoint we are targeting */ public void setGraphServiceTargetVersion(@Nonnull final String graphServiceVersion) { - Objects.requireNonNull(graphServiceVersion, ErrorConstants.Messages.NULL_PARAMETER + "graphServiceVersion"); - if(graphServiceVersion.isEmpty()) + if(Strings.isNullOrEmpty(graphServiceVersion)) { - throw new IllegalArgumentException("graphServiceVersion cannot be empty"); + throw new IllegalArgumentException("graphServiceVersion cannot be null or empty"); } this.graphServiceTargetVersion = graphServiceVersion; } diff --git a/src/main/java/com/microsoft/graph/requests/upload/UploadSessionRequestBuilder.java b/src/main/java/com/microsoft/graph/requests/upload/UploadSessionRequestBuilder.java index 3b2d7883d..a7ac7bafe 100644 --- a/src/main/java/com/microsoft/graph/requests/upload/UploadSessionRequestBuilder.java +++ b/src/main/java/com/microsoft/graph/requests/upload/UploadSessionRequestBuilder.java @@ -1,5 +1,6 @@ package com.microsoft.graph.requests.upload; +import com.google.common.base.Strings; import com.microsoft.graph.models.IUploadSession; import com.microsoft.kiota.*; import com.microsoft.kiota.serialization.Parsable; @@ -34,11 +35,11 @@ public UploadSessionRequestBuilder(@Nonnull String sessionUrl, @Nonnull final ParsableFactory factory) { this.responseHandler = new UploadResponseHandler(); this.requestAdapter = Objects.requireNonNull(requestAdapter); - this.urlTemplate = Objects.requireNonNull(sessionUrl); - if(sessionUrl.isEmpty()) + if(Strings.isNullOrEmpty(sessionUrl)) { - throw new IllegalArgumentException("sessionUrl cannot be empty"); + throw new IllegalArgumentException("sessionUrl cannot be null or empty"); } + this.urlTemplate = sessionUrl; this.factory = Objects.requireNonNull(factory); } /** diff --git a/src/main/java/com/microsoft/graph/requests/upload/UploadSliceRequestBuilder.java b/src/main/java/com/microsoft/graph/requests/upload/UploadSliceRequestBuilder.java index 85a22e761..341a2485a 100644 --- a/src/main/java/com/microsoft/graph/requests/upload/UploadSliceRequestBuilder.java +++ b/src/main/java/com/microsoft/graph/requests/upload/UploadSliceRequestBuilder.java @@ -1,5 +1,6 @@ package com.microsoft.graph.requests.upload; +import com.google.common.base.Strings; import com.microsoft.graph.models.UploadResult; import com.microsoft.kiota.*; import com.microsoft.kiota.serialization.Parsable; @@ -44,11 +45,11 @@ public UploadSliceRequestBuilder(@Nonnull String sessionUrl, long rangeEnd, long totalSessionLength, @Nonnull ParsableFactory factory) { - this.urlTemplate = Objects.requireNonNull(sessionUrl); - if(sessionUrl.isEmpty()) + if(Strings.isNullOrEmpty(sessionUrl)) { - throw new IllegalArgumentException("sessionUrl cannot be empty"); + throw new IllegalArgumentException("sessionUrl cannot be null or empty"); } + this.urlTemplate = sessionUrl; this.requestAdapter = Objects.requireNonNull(requestAdapter); this.factory = factory; this.rangeBegin = rangeBegin; diff --git a/src/main/java/com/microsoft/graph/tasks/PageIterator.java b/src/main/java/com/microsoft/graph/tasks/PageIterator.java new file mode 100644 index 000000000..0e5dcc82b --- /dev/null +++ b/src/main/java/com/microsoft/graph/tasks/PageIterator.java @@ -0,0 +1,477 @@ +package com.microsoft.graph.tasks; + +import com.google.common.base.Strings; +import com.microsoft.graph.CoreConstants; +import com.microsoft.graph.exceptions.ServiceException; +import com.microsoft.graph.requests.IBaseClient; +import com.microsoft.kiota.HttpMethod; +import com.microsoft.kiota.RequestAdapter; +import com.microsoft.kiota.RequestInformation; +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParsableFactory; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * A class for iterating through pages of a collection + * Uses to automatically pages through result sets across multiple calls and process each item in the result set. + * @param The type of the entity returned in the collection. This type must implement {@link Parsable} + * @param The Microsoft Graph collection response type returned in the collection response. This type must implement {@link Parsable} and {@link AdditionalDataHolder} + */ +public class PageIterator { + /** + * Creates a new instance of the PageIterator class + */ + protected PageIterator() { + // default constructor + } + private static final String NO_COLLECTION_PROPERTY_ERROR = "The Parsable does not contain a collection property."; + private RequestAdapter requestAdapter; + private TCollectionPage currentPage; + private ParsableFactory collectionPageFactory; + private Queue pageItemQueue; + private Function processPageItemCallback; + private Function> asyncProcessPageItemCallback; + private UnaryOperator requestConfigurator; + + + private String deltaLink; + + /** + * The deltaLink returned from a delta query. + * @return the deltaLink from the delta query + */ + @Nullable + public String getDeltaLink() { + return deltaLink; + } + private String nextLink; + /** + * The nextLink returned from a collection query. + * @return the nextLink from the collection query + */ + @Nullable + public String getNextLink() { + return nextLink; + } + private PageIteratorState state = PageIteratorState.NOT_STARTED; + /** + * The state of the page iterator + * @return the state of the page iterator + */ + @Nonnull + public PageIteratorState getPageIteratorState() { + return state; + } + /** + * Boolean indicating whether the processPageItemCallback is synchronous or asynchronous + */ + protected boolean isProcessPageItemCallbackAsync; + /** + * Sets the request adapter to use for requests in the page iterator. + * @param requestAdapter the request adapter to use for requests. + */ + protected void setRequestAdapter(@Nonnull RequestAdapter requestAdapter) { + this.requestAdapter = Objects.requireNonNull(requestAdapter); + } + /** + * The factory to use for creating the collection page instance. + * @param collectionPageFactory the factory to use for creating the collection page. + */ + protected void setCollectionPageFactory(@Nonnull ParsableFactory collectionPageFactory) { + this.collectionPageFactory = Objects.requireNonNull(collectionPageFactory); + } + /** + * The request configurator to use for requests in the page iterator. + * @param requestConfigurator the request configurator to use when modifying requests. + */ + protected void setRequestConfigurator(@Nullable UnaryOperator requestConfigurator) { + this.requestConfigurator = requestConfigurator; + } + /** + * The current page of the collection. + * @param currentPage the current page of the collection. + */ + protected void setCurrentPage(@Nonnull TCollectionPage currentPage) { + this.currentPage = Objects.requireNonNull(currentPage); + } + /** + * The processPageItemCallback to use for processing each item in the collection. + * @param processPageItemCallback the processPageItemCallback to use for processing each item in the collection. + */ + protected void setProcessPageItemCallback(@Nonnull Function processPageItemCallback) { + this.processPageItemCallback = Objects.requireNonNull(processPageItemCallback); + isProcessPageItemCallbackAsync = false; + } + /** + * The asyncProcessPageItemCallback to use for processing each item in the collection. + * @param asyncProcessPageItemCallback the asyncProcessPageItemCallback to use for processing each item in the collection. + */ + protected void setAsyncProcessPageItemCallback(@Nonnull Function> asyncProcessPageItemCallback) { + this.asyncProcessPageItemCallback = Objects.requireNonNull(asyncProcessPageItemCallback); + isProcessPageItemCallbackAsync = true; + } + /** + * The queue of items in the current page. + * @param pageItemQueue the queue of items in the current page. + */ + protected void setPageItemQueue(@Nonnull Queue pageItemQueue) { + this.pageItemQueue = Objects.requireNonNull(pageItemQueue); + } + + /** + * A builder class for building a PageIterator. + * This Builder class should be used when the processPageItemCallback is asynchronous. + * @param The type of the entity returned in the collection. This type must implement {@link Parsable} + * @param The Microsoft Graph collection response type returned in the collection response. This type must implement {@link Parsable} and {@link AdditionalDataHolder} + */ + public static class BuilderWithAsyncProcess implements PageIteratorBuilder{ + /** + * Constructor for the Builder class of a PageIterator with an asynchronous processPageItemCallback. + */ + public BuilderWithAsyncProcess() { + // default constructor + } + private RequestAdapter requestAdapter; + private TCollectionPage currentPage; + private ParsableFactory collectionPageFactory; + private UnaryOperator requestConfigurator; + private Function> asyncProcessPageItemCallback; + private RequestAdapter getRequestAdapter() { + return this.requestAdapter; + } + private TCollectionPage getCollectionPage() { + return this.currentPage; + } + private ParsableFactory getCollectionPageFactory() { + return this.collectionPageFactory; + } + private UnaryOperator getRequestConfigurator() { + return this.requestConfigurator; + } + private Function> getAsyncProcessPageItemCallback() { + return this.asyncProcessPageItemCallback; + } + @Override + @Nonnull + public BuilderWithAsyncProcess client(@Nonnull IBaseClient client) { + Objects.requireNonNull(client); + return this.requestAdapter(client.getRequestAdapter()); + } + @SuppressFBWarnings + @Override + @Nonnull + public BuilderWithAsyncProcess requestAdapter(@Nonnull RequestAdapter requestAdapter) { + this.requestAdapter = Objects.requireNonNull(requestAdapter); + return this; + } + @Override + @Nonnull + public BuilderWithAsyncProcess collectionPage(@Nonnull TCollectionPage collectionPage) { + this.currentPage = Objects.requireNonNull(collectionPage); + return this; + } + @Override + @Nonnull + public BuilderWithAsyncProcess collectionPageFactory(@Nonnull ParsableFactory collectionPageFactory) { + this.collectionPageFactory = Objects.requireNonNull(collectionPageFactory); + return this; + } + @Override + @Nonnull + public BuilderWithAsyncProcess requestConfigurator(@Nonnull UnaryOperator requestConfigurator) { + this.requestConfigurator = Objects.requireNonNull(requestConfigurator); + return this; + } + /** + * Sets the callback to be called for each item in the collection. + * @param asyncProcessPageItemCallback the callback to be called for each item in the collection. + * @return the builder object itself + */ + @Nonnull + public BuilderWithAsyncProcess asyncProcessPageItemCallback(@Nonnull Function> asyncProcessPageItemCallback) { + this.asyncProcessPageItemCallback = Objects.requireNonNull(asyncProcessPageItemCallback); + return this; + } + /** + * Builds the PageIterator object. + * Will fail if request adapter is not set. + * Will fail if current collection page is not set. + * Will fail if collection page factory is not set. + * Will fail if process page item callback is not set. + */ + @Nonnull + private PageIterator build(@Nonnull PageIterator instance) throws InvocationTargetException, IllegalAccessException { + Objects.requireNonNull(instance); + if(!this.currentPage.getFieldDeserializers().containsKey("value")) { + throw new IllegalArgumentException(NO_COLLECTION_PROPERTY_ERROR); + } + instance.setRequestAdapter(Objects.requireNonNull(this.getRequestAdapter())); + instance.setCurrentPage(Objects.requireNonNull(this.getCollectionPage())); + instance.setCollectionPageFactory(Objects.requireNonNull(this.getCollectionPageFactory())); + instance.setRequestConfigurator(this.getRequestConfigurator()); + instance.setAsyncProcessPageItemCallback(Objects.requireNonNull(this.getAsyncProcessPageItemCallback())); + + Queue currentCollection = new LinkedList<>(extractEntityListFromParsable(this.getCollectionPage())); + instance.setPageItemQueue(currentCollection); + return instance; + } + @Override + @Nonnull + public PageIterator build() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + return this.build(new PageIterator<>()); + } + } + + /** + * A builder class for building a PageIterator. + * This Builder class should be used when the processPageItemCallback is synchronous. + * @param The type of the entity returned in the collection. This type must implement {@link Parsable} + * @param The Microsoft Graph collection response type returned in the collection response. This type must implement {@link Parsable} and {@link AdditionalDataHolder} + */ + public static class BuilderWithSyncProcess implements PageIteratorBuilder{ + /** + * Constructor for the Builder class of a PageIterator with a synchronous processPageItemCallback. + */ + public BuilderWithSyncProcess() { + // Default constructor + } + private RequestAdapter requestAdapter; + private TCollectionPage currentPage; + private ParsableFactory collectionPageFactory; + private UnaryOperator requestConfigurator; + private Function processPageItemCallback; + private RequestAdapter getRequestAdapter() { + return this.requestAdapter; + } + private TCollectionPage getCollectionPage() { + return this.currentPage; + } + private ParsableFactory getCollectionPageFactory() { + return this.collectionPageFactory; + } + + private UnaryOperator getRequestConfigurator() { + return this.requestConfigurator; + } + private Function getProcessPageItemCallback() { + return this.processPageItemCallback; + } + + @Override + @Nonnull + public BuilderWithSyncProcess client(@Nonnull IBaseClient client) { + Objects.requireNonNull(client); + return this.requestAdapter(client.getRequestAdapter()); + } + @SuppressFBWarnings + @Override + @Nonnull + public BuilderWithSyncProcess requestAdapter(@Nonnull RequestAdapter requestAdapter) { + this.requestAdapter = Objects.requireNonNull(requestAdapter); + return this; + } + @Override + @Nonnull + public BuilderWithSyncProcess collectionPage(@Nonnull TCollectionPage collectionPage) { + this.currentPage = Objects.requireNonNull(collectionPage); + return this; + } + @Override + @Nonnull + public BuilderWithSyncProcess collectionPageFactory(@Nonnull ParsableFactory collectionPageFactory) { + this.collectionPageFactory = Objects.requireNonNull(collectionPageFactory); + return this; + } + @Override + @Nonnull + public BuilderWithSyncProcess requestConfigurator(@Nonnull UnaryOperator requestConfigurator) { + this.requestConfigurator = Objects.requireNonNull(requestConfigurator); + return this; + } + /** + * Sets the callback to be called for each item in the collection. + * @param processPageItemCallback the callback to be called for each item in the collection. + * @return the builder object itself + */ + @Nonnull + public BuilderWithSyncProcess processPageItemCallback(@Nonnull Function processPageItemCallback) { + this.processPageItemCallback = Objects.requireNonNull(processPageItemCallback); + return this; + } + /** + * Builds the PageIterator object. + * Will fail if request adapter is not set. + * Will fail if current collection page is not set. + * Will fail if collection page factory is not set. + * Will fail if process page item callback is not set. + */ + @Nonnull + private PageIterator build(@Nonnull PageIterator instance) throws InvocationTargetException, IllegalAccessException { + Objects.requireNonNull(instance); + if(!this.currentPage.getFieldDeserializers().containsKey("value")) { + throw new IllegalArgumentException(NO_COLLECTION_PROPERTY_ERROR); + } + instance.setRequestAdapter(Objects.requireNonNull(this.getRequestAdapter())); + instance.setCurrentPage(Objects.requireNonNull(this.getCollectionPage())); + instance.setCollectionPageFactory(Objects.requireNonNull(this.getCollectionPageFactory())); + instance.setRequestConfigurator(this.getRequestConfigurator()); + instance.setProcessPageItemCallback(Objects.requireNonNull(this.getProcessPageItemCallback())); + + Queue currentCollection = new LinkedList<>(extractEntityListFromParsable(this.getCollectionPage())); + instance.setPageItemQueue(currentCollection); + return instance; + } + @Override + @Nonnull + public PageIterator build() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + return this.build(new PageIterator<>()); + } + } + private CompletableFuture intrapageIterate() throws ReflectiveOperationException { + this.state = PageIteratorState.INTRAPAGE_ITERATION; + while (!this.pageItemQueue.isEmpty()) { + boolean shouldContinue; + if (isProcessPageItemCallbackAsync) { + shouldContinue = this.asyncProcessPageItemCallback.apply(this.pageItemQueue.remove()).join(); + } else { + shouldContinue = this.processPageItemCallback.apply(this.pageItemQueue.remove()); + } + if (!shouldContinue) { + this.state = PageIteratorState.PAUSED; + return CompletableFuture.completedFuture(false); + } + } + + String extractedNextLink = extractNextLinkFromParsable(this.currentPage, null); + if (!Strings.isNullOrEmpty(extractedNextLink)){ + this.nextLink = extractedNextLink; + this.deltaLink = ""; + return CompletableFuture.completedFuture(true); + } + + String extractedDeltaLink = extractNextLinkFromParsable(this.currentPage, CoreConstants.CollectionResponseMethods.GET_ODATA_DELTA_LINK); + if (!Strings.isNullOrEmpty(extractedDeltaLink)){ + this.deltaLink = extractedDeltaLink; + this.state = PageIteratorState.DELTA; + } else { + this.state = PageIteratorState.COMPLETE; + } + this.nextLink = ""; + return CompletableFuture.completedFuture(false); + } + private CompletableFuture interpageIterate() throws ReflectiveOperationException, ServiceException { + this.state = PageIteratorState.INTERPAGE_ITERATION; + + if(!Strings.isNullOrEmpty(nextLink) || !Strings.isNullOrEmpty(deltaLink)) { + RequestInformation nextPageRequestInformation = new RequestInformation(); + nextPageRequestInformation.httpMethod = HttpMethod.GET; + nextPageRequestInformation.urlTemplate = Strings.isNullOrEmpty(nextLink) ? deltaLink : nextLink; + + nextPageRequestInformation = requestConfigurator == null ? nextPageRequestInformation : requestConfigurator.apply(nextPageRequestInformation); + this.currentPage = Objects.requireNonNull(this.requestAdapter.sendAsync(nextPageRequestInformation, this.collectionPageFactory, null)).join(); + List pageItems = extractEntityListFromParsable(this.currentPage); + if(!pageItems.isEmpty()) { + this.pageItemQueue.addAll(pageItems); + } + } + if(!Strings.isNullOrEmpty(nextLink) && this.nextLink.equals(extractNextLinkFromParsable(this.currentPage, null))) { + throw new ServiceException("Detected a nextLink loop. NextLink value: " + this.nextLink); + } + return CompletableFuture.completedFuture(null); + } + + /** + * Iterates over the collection of entities in the collation page. + * Will continues to iterate over the collection of entities in the next page, if there is a next page. + * @return a CompletableFuture that completes when the iteration is complete. + * @throws ServiceException if the request was unable to complete for any reason. + * @throws ReflectiveOperationException if the entity or collection page could not be instantiated or if they are of invalid types. + */ + @Nonnull + public CompletableFuture iterate() throws ServiceException, ReflectiveOperationException { + if(this.state == PageIteratorState.DELTA) { + interpageIterate().join(); + } + boolean shouldContinueInterpageIteration = intrapageIterate().join(); + while (shouldContinueInterpageIteration) { + interpageIterate().join(); + shouldContinueInterpageIteration = intrapageIterate().join(); + } + return CompletableFuture.completedFuture(null); + } + + /** + * Resumes the iteration over the collection of entities in the collation page. + * @return a CompletableFuture that completes when the iteration is complete. + * @throws ServiceException if the request was unable to complete for any reason. + * @throws ReflectiveOperationException if the entity or collection page could not be instantiated or if they are of invalid types. + */ + @Nonnull + public CompletableFuture resume() throws ServiceException, ReflectiveOperationException { + return CompletableFuture.completedFuture(iterate().join()); + } + + /** + * Extracts the list of entities from the Parsable collection page. + * @param parsableCollection the Parsable collection page. + * @return the list of entities. + * @param the type of the entity. + * @param the type of the collection page. + * @throws IllegalAccessException if the Parsable does not contain a collection property. + * @throws InvocationTargetException if the Parsable does not contain a collection property. + */ + @Nonnull + @SuppressFBWarnings + protected static List extractEntityListFromParsable(@Nonnull TCollectionPage parsableCollection) throws IllegalAccessException, InvocationTargetException { + try{ + return (List) parsableCollection.getClass().getDeclaredMethod("getValue").invoke(parsableCollection); + } catch (NoSuchMethodException e) { + throw new IllegalAccessException("NO_COLLECTION_PROPERTY_ERROR"); + } + } + private static String extractNextLinkFromParsable(@Nonnull TCollectionPage parsableCollection, @Nullable String getNextLinkMethodName) throws ReflectiveOperationException { + String methodName = getNextLinkMethodName == null ? CoreConstants.CollectionResponseMethods.GET_ODATA_NEXT_LINK : getNextLinkMethodName; + Method[] methods = parsableCollection.getClass().getDeclaredMethods(); + String nextLink; + if(Arrays.stream(methods).anyMatch(m -> m.getName().equals(methodName))) { + try { + nextLink = (String) parsableCollection.getClass().getDeclaredMethod(methodName).invoke(parsableCollection); + if(!Strings.isNullOrEmpty(nextLink)) { + return nextLink; + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new ReflectiveOperationException("Could not extract nextLink from parsableCollection."); + } + } + nextLink = (String) parsableCollection.getAdditionalData().get(CoreConstants.OdataInstanceAnnotations.NEXT_LINK); + return nextLink == null ? "" : nextLink; + } + + /** + * Enum to represent the possible states of the PageIterator. + */ + public enum PageIteratorState { + /** The PageIterator has not started iterating. */ + NOT_STARTED, + /** The PageIterator is currently paused. A callback returned false. Iterator can be resumed. */ + PAUSED, + /** The PageIterator is currently iterating over paged requests. */ + INTERPAGE_ITERATION, + /** The PageIterator is currently iterating over the contents of a page. */ + INTRAPAGE_ITERATION, + /** A deltaToken was returned, the iterator is can be resumed. */ + DELTA, + /** The PageIterator has completed iterating. */ + COMPLETE + } +} diff --git a/src/main/java/com/microsoft/graph/tasks/PageIteratorBuilder.java b/src/main/java/com/microsoft/graph/tasks/PageIteratorBuilder.java new file mode 100644 index 000000000..f0835a26e --- /dev/null +++ b/src/main/java/com/microsoft/graph/tasks/PageIteratorBuilder.java @@ -0,0 +1,50 @@ +package com.microsoft.graph.tasks; + +import com.microsoft.graph.requests.IBaseClient; +import com.microsoft.kiota.RequestAdapter; +import com.microsoft.kiota.RequestInformation; +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParsableFactory; + +import javax.annotation.Nonnull; +import java.lang.reflect.InvocationTargetException; +import java.util.function.UnaryOperator; + +interface PageIteratorBuilder { + /** + * Sets the client for the PageIteratorBuilder. + * @param client the client to set. + */ + public PageIteratorBuilder client(@Nonnull IBaseClient client); + /** + * Sets the request adapter for the PageIteratorBuilder. + * @param requestAdapter the request adapter to set. + */ + public PageIteratorBuilder requestAdapter(@Nonnull RequestAdapter requestAdapter); + /** + * Sets the page to be iterated over. + * @param collectionPage the page to be iterated over. + */ + public PageIteratorBuilder collectionPage(@Nonnull TCollectionPage collectionPage) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException; + /** + * Sets factory to use for creating a collection page. + * @param collectionPageFactory the factory to use for creating a collection page. + */ + public PageIteratorBuilder collectionPageFactory(@Nonnull ParsableFactory collectionPageFactory); + /** + * Sets the function to configure each subsequent request. + * @param requestConfigurator function to configure each subsequent request. + */ + public PageIteratorBuilder requestConfigurator(@Nonnull UnaryOperator requestConfigurator); + /** + * Build the PageIterator. + * Should fail if request adapter is not set. + * Should fail if current collection page is not set. + * Should fail if collection page factory is not set. + * Should fail if process page item callback is not set. + * @return the built PageIterator. + */ + PageIterator build() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException; + +} diff --git a/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java b/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java index f5542c240..16180c9ea 100644 --- a/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java +++ b/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java @@ -15,14 +15,11 @@ import org.junit.jupiter.params.aggregator.ArgumentsAccessor; import org.junit.jupiter.params.provider.CsvSource; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Objects; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -53,9 +50,9 @@ void BatchRequestContent_InitializeWithBatchRequestSteps() { @Test void BatchRequestContent_InitializeWithInvalidDependsOnIds() { BatchRequestStep requestStep = new BatchRequestStep("1", mock(Request.class)); - BatchRequestStep requestStep2 = new BatchRequestStep("2", mock(Request.class), List.of("3")); + BatchRequestStep requestStep2 = new BatchRequestStep("2", mock(Request.class), Arrays.asList("3")); try { - new BatchRequestContent(client, List.of(requestStep, requestStep2)); + new BatchRequestContent(client, Arrays.asList(requestStep, requestStep2)); } catch (IllegalArgumentException ex) { assertEquals(ErrorConstants.Messages.INVALID_DEPENDS_ON_REQUEST_ID, ex.getMessage()); } @@ -84,7 +81,7 @@ void BatchRequestContent_AddBatchRequestStepToBatchRequestContentWithMaxSteps() @Test void BatchRequestContent_AddBatchRequestStepWithExistingRequestStep() { BatchRequestStep batchRequestStep = new BatchRequestStep("1", mock(Request.class)); - BatchRequestContent batchRequestContent = new BatchRequestContent(client, List.of(batchRequestStep)); + BatchRequestContent batchRequestContent = new BatchRequestContent(client, Arrays.asList(batchRequestStep)); assertFalse(batchRequestContent.addBatchRequestStep(batchRequestStep)); assertNotNull(batchRequestContent.getBatchRequestSteps()); @@ -93,7 +90,7 @@ void BatchRequestContent_AddBatchRequestStepWithExistingRequestStep() { @Test void BatchRequestContent_AddBatchRequestStepWithNullRequestStep() { BatchRequestStep batchRequestStep = new BatchRequestStep("1", mock(Request.class)); - BatchRequestContent batchRequestContent = new BatchRequestContent(client, List.of(batchRequestStep)); + BatchRequestContent batchRequestContent = new BatchRequestContent(client, Arrays.asList(batchRequestStep)); assertFalse(batchRequestContent.addBatchRequestStep((BatchRequestStep) null)); assertNotNull(batchRequestContent.getBatchRequestSteps()); @@ -102,8 +99,8 @@ void BatchRequestContent_AddBatchRequestStepWithNullRequestStep() { @Test void BatchRequestContent_RemoveBatchRequestStepWithIdForExistingId() { BatchRequestStep batchRequestStep = new BatchRequestStep("1", mock(Request.class)); - BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", mock(Request.class), List.of("1", "1", "1")); - BatchRequestContent batchRequestContent = new BatchRequestContent(client, List.of(batchRequestStep, batchRequestStep2)); + BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", mock(Request.class), Arrays.asList("1", "1", "1")); + BatchRequestContent batchRequestContent = new BatchRequestContent(client, Arrays.asList(batchRequestStep, batchRequestStep2)); assertTrue(batchRequestContent.removeBatchRequestStepWithId("1")); assertEquals(1, batchRequestContent.getBatchRequestSteps().size()); @@ -112,23 +109,23 @@ void BatchRequestContent_RemoveBatchRequestStepWithIdForExistingId() { @Test void BatchRequestContent_RemoveBatchRequestStepWithIdForNonExistingId() { BatchRequestStep batchRequestStep = new BatchRequestStep("1", mock(Request.class)); - BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", mock(Request.class), List.of("1")); - BatchRequestContent batchRequestContent = new BatchRequestContent(client, List.of(batchRequestStep, batchRequestStep2)); + BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", mock(Request.class), Arrays.asList("1")); + BatchRequestContent batchRequestContent = new BatchRequestContent(client, Arrays.asList(batchRequestStep, batchRequestStep2)); assertFalse(batchRequestContent.removeBatchRequestStepWithId("3")); assertEquals(2, batchRequestContent.getBatchRequestSteps().size()); assertEquals(Objects.requireNonNull(batchRequestStep2.getDependsOn()).get(0), Objects.requireNonNull(batchRequestContent.getBatchRequestSteps().get("2").getDependsOn()).get(0)); } @Test - void BatchRequestContent_GetBatchRequestContentFromStep() throws IOException, URISyntaxException { + void BatchRequestContent_GetBatchRequestContentFromStep() throws Exception { Request request = new Request.Builder().url(requestUrl).build(); BatchRequestStep batchRequestStep = new BatchRequestStep("1", mock(Request.class)); - BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", request, List.of("1")); - BatchRequestContent batchRequestContent = new BatchRequestContent(client, List.of(batchRequestStep, batchRequestStep2)); + BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", request, Arrays.asList("1")); + BatchRequestContent batchRequestContent = new BatchRequestContent(client, Arrays.asList(batchRequestStep, batchRequestStep2)); batchRequestContent.removeBatchRequestStepWithId("1"); InputStream requestContent = batchRequestContent.getBatchRequestContentAsync().join(); - String requestContentString = new String(requestContent.readAllBytes(), StandardCharsets.UTF_8); + String requestContentString = readInputStream(requestContent); requestContentString = requestContentString.replace("\n", "").replaceAll("\\s", ""); String expectedContent = "{\"requests\":[{\"id\":\"2\",\"url\":\"/me\",\"method\":\"GET\"}]}"; @@ -137,7 +134,7 @@ void BatchRequestContent_GetBatchRequestContentFromStep() throws IOException, UR assertEquals(expectedContent, requestContentString); } @Test - void BatchRequestContent_GetBatchRequestContentFromStepAsyncDoesNotModifyDateTimes() throws IOException { + void BatchRequestContent_GetBatchRequestContentFromStepAsyncDoesNotModifyDateTimes() throws Exception { String bodyString = "{\n" + " \"subject\": \"Lets go for lunch\",\n" + " \"body\": {\n \"contentType\": \"HTML\",\n" + @@ -165,11 +162,11 @@ void BatchRequestContent_GetBatchRequestContentFromStepAsyncDoesNotModifyDateTim "}"; Request eventRequest = new Request.Builder().url(requestUrl).method("POST", RequestBody.create(bodyString, MediaType.parse("application/json"))).build(); BatchRequestStep batchRequestStep = new BatchRequestStep("1", new Request.Builder().url(requestUrl).build()); - BatchRequestStep batchRequestSte2 = new BatchRequestStep("2", eventRequest, List.of("1")); - BatchRequestContent batchRequestContent = new BatchRequestContent(client, List.of(batchRequestStep, batchRequestSte2)); + BatchRequestStep batchRequestSte2 = new BatchRequestStep("2", eventRequest, Arrays.asList("1")); + BatchRequestContent batchRequestContent = new BatchRequestContent(client, Arrays.asList(batchRequestStep, batchRequestSte2)); InputStream stream = batchRequestContent.getBatchRequestContentAsync().join(); - String requestContentString = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + String requestContentString = readInputStream(stream); String expectedJson = "{\n" + " \"requests\": [\n" + " {\n" + @@ -272,7 +269,7 @@ void BatchRequestContent_AddBatchRequestStepWithBaseRequest() throws IOException assertEquals(batchRequestContent.getBatchRequestSteps().get(requestId).getRequest().method(), requestInfo.httpMethod.toString()); } @Test - void BatchRequestContent_AddBatchRequestStepWithBaseRequestWithHeaderOptions() throws IOException { + void BatchRequestContent_AddBatchRequestStepWithBaseRequestWithHeaderOptions() throws Exception { BatchRequestContent batchRequestContent = new BatchRequestContent(client); Request request = new Request.Builder() .url(requestUrl) @@ -286,7 +283,7 @@ void BatchRequestContent_AddBatchRequestStepWithBaseRequestWithHeaderOptions() t assertNotNull(Objects.requireNonNull(batchRequestContent.getBatchRequestSteps().get(requestId).getRequest().body()).contentType()); InputStream stream = batchRequestContent.getBatchRequestContentAsync().join(); - String requestContentString = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + String requestContentString = readInputStream(stream); String expectedJsonSection = " \"url\": \"/me\"," + " \"method\": \"POST\"," + " \"body\": {}," + @@ -329,7 +326,7 @@ void BatchRequestContent_AddBatchRequestStepWithBaseRequestToBatchRequestContent "https://graph.microsoft.com/v1.0/users?$filter=identities/any(id:id/issuer%20eq%20'$74707853-18b3-411f-ad57-2ef65f6fdeb0'%20and%20id/issuerAssignedId%20eq%20'**bobbetancourt@fakeemail.com**') , /users?$filter=identities/any(id:id/issuer%20eq%20%27$74707853-18b3-411f-ad57-2ef65f6fdeb0%27%20and%20id/issuerAssignedId%20eq%20%27**bobbetancourt@fakeemail.com**%27)" , "https://graph.microsoft.com/beta/users?$filter=identities/any(id:id/issuer%20eq%20'$74707853-18b3-411f-ad57-2ef65f6fdeb0'%20and%20id/issuerAssignedId%20eq%20'**bobbetancourt@fakeemail.com**')&$top=1 , /users?$filter=identities/any(id:id/issuer%20eq%20%27$74707853-18b3-411f-ad57-2ef65f6fdeb0%27%20and%20id/issuerAssignedId%20eq%20%27**bobbetancourt@fakeemail.com**%27)&$top=1" , }) - void BatchRequestContent_AddBatchRequestStepWithBaseRequestProperlySetsVersion(ArgumentsAccessor argumentsAccessor) throws IOException { + void BatchRequestContent_AddBatchRequestStepWithBaseRequestProperlySetsVersion(ArgumentsAccessor argumentsAccessor) throws Exception { Request request = new Request.Builder().url(argumentsAccessor.getString(0)).build(); BatchRequestStep batchRequestStep = new BatchRequestStep("1", request); BatchRequestContent batchRequestContent = new BatchRequestContent(client); @@ -337,7 +334,7 @@ void BatchRequestContent_AddBatchRequestStepWithBaseRequestProperlySetsVersion(A batchRequestContent.addBatchRequestStep(batchRequestStep); InputStream stream = batchRequestContent.getBatchRequestContentAsync().join(); - String requestContentString = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + String requestContentString = readInputStream(stream); String expectedJson = "{" + " \"requests\": [" + " {" + @@ -352,4 +349,14 @@ void BatchRequestContent_AddBatchRequestStepWithBaseRequestProperlySetsVersion(A requestContentString = requestContentString.replaceAll("\\s", "").replace("\n", ""); assertEquals(expectedJson, requestContentString); } + private static String readInputStream(InputStream stream) throws Exception { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = stream.read(buffer)) != -1) { + result.write(buffer, 0, length); + } + return new String(result.toByteArray(), StandardCharsets.UTF_8); + } + } diff --git a/src/test/java/com/microsoft/graph/requests/BatchRequestBuilderTest.java b/src/test/java/com/microsoft/graph/requests/BatchRequestBuilderTest.java index 89e2ba86f..8567da4ec 100644 --- a/src/test/java/com/microsoft/graph/requests/BatchRequestBuilderTest.java +++ b/src/test/java/com/microsoft/graph/requests/BatchRequestBuilderTest.java @@ -11,7 +11,7 @@ import okhttp3.RequestBody; import org.junit.jupiter.api.Test; -import java.util.List; +import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -26,9 +26,9 @@ void BatchRequestBuilder_DefaultBuilderTest() { RequestBody requestBody = RequestBody.create("{}", MediaType.get(CoreConstants.MimeTypeNames.APPLICATION_JSON)); Request request2 = new Request.Builder().url("https://graph.microsoft.com/v1.0/me/onenote/notebooks").post(requestBody).build(); BatchRequestStep batchRequestStep = new BatchRequestStep("1", request); - BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", request2, List.of("1")); + BatchRequestStep batchRequestStep2 = new BatchRequestStep("2", request2, Arrays.asList("1")); - BatchRequestContent batchRequestContent = new BatchRequestContent(client,List.of(batchRequestStep, batchRequestStep2)); + BatchRequestContent batchRequestContent = new BatchRequestContent(client,Arrays.asList(batchRequestStep, batchRequestStep2)); RequestInformation requestInformation = batchRequestBuilder.toPostRequestInformationAsync(batchRequestContent).join(); assertEquals("{+baseurl}/$batch", requestInformation.urlTemplate); diff --git a/src/test/java/com/microsoft/graph/tasks/LargeFileUploadTest.java b/src/test/java/com/microsoft/graph/tasks/LargeFileUploadTest.java index 07a796dab..dc6434c4e 100644 --- a/src/test/java/com/microsoft/graph/tasks/LargeFileUploadTest.java +++ b/src/test/java/com/microsoft/graph/tasks/LargeFileUploadTest.java @@ -22,13 +22,13 @@ class LargeFileUploadTest { final OkHttpRequestAdapter adapter = new OkHttpRequestAdapter(mock(AuthenticationProvider.class)); @Test - void ThrowsIllegalArgumentExceptionOnEmptyStream() throws NoSuchFieldException, IllegalAccessException, IOException { + void ThrowsIllegalArgumentExceptionOnEmptyStream() throws IllegalAccessException, IOException { UploadSession session = new UploadSession(); session.setNextExpectedRanges(Arrays.asList("0-")); session.setUploadUrl("http://localhost"); session.setExpirationDateTime(OffsetDateTime.parse("2019-11-07T06:39:31.499Z")); - InputStream stream = InputStream.nullInputStream(); + InputStream stream = new ByteArrayInputStream(new byte[0]); int size = stream.available(); long maxSliceSize = 200*1024; diff --git a/src/test/java/com/microsoft/graph/tasks/PageIteratorTest.java b/src/test/java/com/microsoft/graph/tasks/PageIteratorTest.java new file mode 100644 index 000000000..ed16adb5c --- /dev/null +++ b/src/test/java/com/microsoft/graph/tasks/PageIteratorTest.java @@ -0,0 +1,456 @@ +package com.microsoft.graph.tasks; + +import com.microsoft.graph.BaseClient; +import com.microsoft.graph.CoreConstants; +import com.microsoft.graph.exceptions.ServiceException; +import com.microsoft.graph.testModels.*; +import com.microsoft.kiota.RequestInformation; +import com.microsoft.kiota.authentication.AuthenticationProvider; +import com.microsoft.kiota.http.OkHttpRequestAdapter; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParsableFactory; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.junit.jupiter.api.Test; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +class PageIteratorTest { + + private PageIterator pageIterator; + final OkHttpRequestAdapter adapter = new OkHttpRequestAdapter(mock(AuthenticationProvider.class)); + BaseClient baseClient = new BaseClient(adapter); + + @Test + void given_NonCollection_Parsable_Will_Throw_ArgumentException() { + try { + new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(new TestEventItem()) + .collectionPageFactory(TestEventItem::createFromDiscriminatorValue) + .processPageItemCallback(item -> true) + .requestConfigurator(requestInformation -> requestInformation) + .build(); + } catch (Exception e) { + assertEquals("The Parsable does not contain a collection property.", e.getMessage()); + assertEquals(IllegalArgumentException.class, e.getClass()); + } + } + @SuppressFBWarnings(value = "NP_NONNULL_PARAM_VIOLATION", justification = "Testing null value") + @Test + void given_Null_Collection_Page_Will_Throw_NullPointerException() { + try { + pageIterator = new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(null) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(item -> true) + .requestConfigurator(requestInformation -> requestInformation) + .build(); + } catch (Exception e) { + assertEquals(NullPointerException.class, e.getClass()); + } + } + @SuppressFBWarnings(value = "NP_NONNULL_PARAM_VIOLATION", justification = "Testing null value") + @Test + void given_Null_Async_Delegate_Will_Throw_NullPointerException() { + try{ + pageIterator = new PageIterator.BuilderWithAsyncProcess() + .client(baseClient) + .collectionPage(new TestEventsResponse()) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .asyncProcessPageItemCallback(null) + .requestConfigurator(requestInformation -> requestInformation) + .build(); + } catch (Exception e) { + assertEquals(NullPointerException.class, e.getClass()); + } + } + @SuppressFBWarnings(value = "NP_NONNULL_PARAM_VIOLATION", justification = "Testing null value") + @Test + void given_Null_Delegate_Will_Throw_NullPointerException() { + try{ + pageIterator = new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(new TestEventsResponse()) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(null) + .requestConfigurator(requestInformation -> requestInformation) + .build(); + } catch (Exception e) { + assertEquals(NullPointerException.class, e.getClass()); + } + } + @Test + void given_Concrete_Generated_Collection_Page_Will_Iterate_PageItems() throws ReflectiveOperationException, ServiceException { + int inputEventCount = 17; + + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList()); + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + List testEventItems = new LinkedList(); + pageIterator = new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(item -> { + testEventItems.add(item); + return true; }) + .build(); + + assertFalse(pageIterator.isProcessPageItemCallbackAsync); + pageIterator.iterate().join(); + + assertFalse(testEventItems.isEmpty()); + assertEquals(inputEventCount, testEventItems.size()); + } + + @Test + void given_Concrete_Generated_CollectionPage_It_Stops_Iterating_PageItems() throws ReflectiveOperationException, ServiceException { + int inputEventCount = 10; + + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList()); + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + List testEventItems = new LinkedList(); + pageIterator = new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(item -> { + if(item.getSubject().equals("Test Event: 7")) { + return false; + } + testEventItems.add(item); + return true; + }).build(); + pageIterator.iterate().join(); + + assertEquals(7, testEventItems.size()); + assertEquals(PageIterator.PageIteratorState.PAUSED, pageIterator.getPageIteratorState()); + } + @Test + void given_CollectionPage_Without_NextLink_Property_It_Iterates_Across_Pages() throws ReflectiveOperationException, ServiceException { + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + HashMap additionalData = new HashMap<>(); + additionalData.put(CoreConstants.OdataInstanceAnnotations.NEXT_LINK, "http://localhost/events?$skip=11"); + originalPage.setAdditionalData(additionalData); + + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + TestEventsResponse secondPage = new TestEventsResponse(); + secondPage.setValue(new LinkedList()); + int secondPageEventCount = 5; + for(int i = 0; i < secondPageEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Second Page Test Event: " + i); + secondPage.getValue().add(testEventItem); + } + + final Boolean[] reachedNextPage = {false}; + + Function processPageItemCallback = item -> { + if(item.getSubject().contains("Second Page Test Event")) { + reachedNextPage[0] = true; + return false; + } + return true; + }; + + MockAdapter mockAdapter = new MockAdapter(mock(AuthenticationProvider.class), secondPage); + pageIterator = new PageIterator.BuilderWithSyncProcess() + .requestAdapter(mockAdapter) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(processPageItemCallback) + .build(); + + pageIterator.iterate().join(); + + assertTrue(reachedNextPage[0]); + assertEquals(PageIterator.PageIteratorState.PAUSED, pageIterator.getPageIteratorState()); + assertEquals("http://localhost/events?$skip=11", pageIterator.getNextLink()); + + } + @Test + void given_CollectionPage_Delta_Link_Property_It_Iterates_Across_Pages() throws ReflectiveOperationException, ServiceException { + TestEventsDeltaResponse originalPage = new TestEventsDeltaResponse(); + originalPage.setValue(new LinkedList<>()); + originalPage.setOdataDeltaLink("http://localhost/events?$skip=11"); + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + Function processPageItemCallback = item -> true; + + PageIterator pageIterator = new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsDeltaResponse::createFromDiscriminatorValue) + .processPageItemCallback(processPageItemCallback) + .build(); + + pageIterator.iterate().join(); + + assertEquals(PageIterator.PageIteratorState.DELTA, pageIterator.getPageIteratorState()); + assertEquals("http://localhost/events?$skip=11", pageIterator.getDeltaLink()); + } + + @Test + void given_CollectionPage_It_Iterates_Across_Pages_With_Async_Delegate() throws ReflectiveOperationException, ServiceException { + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + originalPage.setOdataNextLink("http://localhost/events?$skip=11"); + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + TestEventsResponse secondPage = new TestEventsResponse(); + secondPage.setValue(new LinkedList<>()); + int secondPageEventCount = 5; + for(int i = 0; i < secondPageEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Second Page Test Event: " + i); + secondPage.getValue().add(testEventItem); + } + + boolean[] reachedNextPage = {false}; + + Function> processPageItemCallback = item -> { + if(item.getSubject().contains("Second Page Test Event")) { + + reachedNextPage[0] = true; + return CompletableFuture.completedFuture(false); + } + return CompletableFuture.completedFuture(true); + }; + + MockAdapter mockAdapter = new MockAdapter(mock(AuthenticationProvider.class), secondPage); + + pageIterator = new PageIterator.BuilderWithAsyncProcess() + .requestAdapter(mockAdapter) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .asyncProcessPageItemCallback(processPageItemCallback) + .build(); + assertTrue(pageIterator.isProcessPageItemCallbackAsync); + + pageIterator.iterate().join(); + + assertTrue(reachedNextPage[0]); + assertEquals(PageIterator.PageIteratorState.PAUSED, pageIterator.getPageIteratorState()); + } + @Test + void given_CollectionPage_It_Iterates_Across_Pages() throws ReflectiveOperationException, ServiceException{ + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + originalPage.setOdataNextLink("http://localhost/events?$skip=11"); + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + TestEventsResponse secondPage = new TestEventsResponse(); + secondPage.setValue(new LinkedList<>()); + int secondPageEventCount = 5; + for(int i = 0; i < secondPageEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Second Page Test Event: " + i); + secondPage.getValue().add(testEventItem); + } + + boolean[] reachedNextPage = {false}; + + Function processPageItemCallback = item -> { + if(item.getSubject().contains("Second Page Test Event")) { + + reachedNextPage[0] = true; + return false; + } + return true; + }; + + MockAdapter mockAdapter = new MockAdapter(mock(AuthenticationProvider.class), secondPage); + + pageIterator = new PageIterator.BuilderWithSyncProcess() + .requestAdapter(mockAdapter) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(processPageItemCallback) + .build(); + + pageIterator.iterate().join(); + + assertFalse(pageIterator.isProcessPageItemCallbackAsync); + assertTrue(reachedNextPage[0]); + assertEquals(PageIterator.PageIteratorState.PAUSED, pageIterator.getPageIteratorState()); + } + @Test + void given_CollectionPage_It_Detects_Next_Link_Loop() throws ReflectiveOperationException { + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + originalPage.setOdataNextLink("http://localhost/events?$skip=11"); + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + TestEventsResponse secondPage = new TestEventsResponse(); + secondPage.setValue(new LinkedList<>()); + secondPage.setOdataNextLink("http://localhost/events?$skip=11"); + int secondPageEventCount = 5; + for(int i = 0; i < secondPageEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Second Page Test Event: " + i); + secondPage.getValue().add(testEventItem); + } + + Function processPageItemCallback = item -> true; + + MockAdapter mockAdapter = new MockAdapter(mock(AuthenticationProvider.class), secondPage); + + pageIterator = new PageIterator.BuilderWithSyncProcess() + .requestAdapter(mockAdapter) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(processPageItemCallback) + .build(); + + try{ + pageIterator.iterate().join(); + } catch (Exception e) { + assertEquals(ServiceException.class, e.getClass()); + assertTrue(e.getMessage().contains("Detected a nextLink loop. NextLink value:")); + } + } + @Test + void given_CollectionPage_It_Handles_Empty_NextPage() throws ReflectiveOperationException { + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + originalPage.setOdataNextLink("http://localhost/events?$skip=11"); + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + TestEventsResponse secondPage = new TestEventsResponse(); + secondPage.setValue(new LinkedList<>()); + + Function processPageItemCallback = item -> true; + + MockAdapter mockAdapter = new MockAdapter(mock(AuthenticationProvider.class), secondPage); + + pageIterator = new PageIterator.BuilderWithSyncProcess() + .requestAdapter(mockAdapter) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(processPageItemCallback) + .build(); + + try{ + pageIterator.iterate().join(); + } catch (ServiceException e) { + fail("Should not throw exception"); + } + } + @Test + void given_PageIterator_It_Has_NotStarted_PagingState() throws ReflectiveOperationException { + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + pageIterator = new PageIterator.BuilderWithSyncProcess() + .client(baseClient) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(item-> true) + .build(); + assertEquals(PageIterator.PageIteratorState.NOT_STARTED, pageIterator.getPageIteratorState()); + } + @Test + void given_RequestConfigurator_It_Is_Invoked() throws ReflectiveOperationException, ServiceException { + TestEventsResponse originalPage = new TestEventsResponse(); + originalPage.setValue(new LinkedList<>()); + originalPage.setOdataNextLink("http://localhost/events?$skip=11"); + int inputEventCount = 17; + for(int i = 0; i < inputEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Test Event: " + i); + originalPage.getValue().add(testEventItem); + } + + TestEventsResponse secondPage = new TestEventsResponse(); + secondPage.setValue(new LinkedList<>()); + int secondPageEventCount = 5; + for(int i = 0; i < secondPageEventCount; i++) { + TestEventItem testEventItem = new TestEventItem(); + testEventItem.setSubject("Second Page Test Event: " + i); + secondPage.getValue().add(testEventItem); + } + + boolean[] requestConfiguratorInvoked = {false}; + + UnaryOperator requestConfigurator = request -> { + requestConfiguratorInvoked[0] = true; + return request; + }; + + MockAdapter mockAdapter = new MockAdapter(mock(AuthenticationProvider.class), secondPage); + + pageIterator = new PageIterator.BuilderWithSyncProcess() + .requestAdapter(mockAdapter) + .collectionPage(originalPage) + .collectionPageFactory(TestEventsResponse::createFromDiscriminatorValue) + .processPageItemCallback(item -> true) + .requestConfigurator(requestConfigurator) + .build(); + + pageIterator.iterate().join(); + + assertTrue(requestConfiguratorInvoked[0]); + } +} + class MockAdapter extends OkHttpRequestAdapter { + Object mockResponse; + public MockAdapter(@Nonnull AuthenticationProvider authenticationProvider, Object response) { + super(authenticationProvider); + mockResponse = response; + } + + public CompletableFuture sendAsync(@Nonnull RequestInformation request, @Nonnull ParsableFactory parsableFactory, @Nullable final HashMap> errorMappings) { + return CompletableFuture.completedFuture((T) this.mockResponse); + } + } diff --git a/src/test/java/com/microsoft/graph/testModels/TestEvent.java b/src/test/java/com/microsoft/graph/testModels/TestEvent.java index babb02ce6..7f7d77a44 100644 --- a/src/test/java/com/microsoft/graph/testModels/TestEvent.java +++ b/src/test/java/com/microsoft/graph/testModels/TestEvent.java @@ -9,6 +9,7 @@ import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; import java.util.function.Consumer; @SuppressFBWarnings @@ -104,9 +105,7 @@ public HashMap> getFieldDeserializers() { } public void serialize(SerializationWriter writer) { - if (writer == null) { - throw new IllegalArgumentException("The writer cannot be null."); - } + Objects.requireNonNull(writer); writer.writeStringValue("@odata.type", getODataType()); writer.writeStringValue("id", getId()); writer.writeStringValue("subject", getSubject()); diff --git a/src/test/java/com/microsoft/graph/testModels/TestEventItem.java b/src/test/java/com/microsoft/graph/testModels/TestEventItem.java new file mode 100644 index 000000000..b6cf6cd46 --- /dev/null +++ b/src/test/java/com/microsoft/graph/testModels/TestEventItem.java @@ -0,0 +1,283 @@ +package com.microsoft.graph.testModels; + +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; + +import javax.annotation.Nonnull; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.function.Consumer; + +public class TestEventItem implements Parsable, AdditionalDataHolder { + private Boolean allowNewTimeProposals; + private String bodyPreview; + private Boolean hasAttachments; + private Boolean hideAttendees; + private String iCalUId; + public List instances; + private Boolean isAllDay; + private Boolean isCancelled; + private Boolean isDraft; + private Boolean isOnlineMeeting; + private Boolean isOrganizer; + private Boolean isReminderOn; + private String onlineMeetingUrl; + private String originalEndTimeZone; + private OffsetDateTime originalStart; + private String originalStartTimeZone; + private Integer reminderMinutesBeforeStart; + private Boolean responseRequested; + private String seriesMasterId; + private String subject; + private String transactionId; + private String webLink; + public Map additionalData; + + public Boolean getAllowNewTimeProposals() { + return allowNewTimeProposals; + } + + public void setAllowNewTimeProposals(Boolean allowNewTimeProposals) { + this.allowNewTimeProposals = allowNewTimeProposals; + } + + public String getBodyPreview() { + return bodyPreview; + } + + public void setBodyPreview(String bodyPreview) { + this.bodyPreview = bodyPreview; + } + + public Boolean getHasAttachments() { + return hasAttachments; + } + + public void setHasAttachments(Boolean hasAttachments) { + this.hasAttachments = hasAttachments; + } + + public Boolean getHideAttendees() { + return hideAttendees; + } + + public void setHideAttendees(Boolean hideAttendees) { + this.hideAttendees = hideAttendees; + } + + public String getiCalUId() { + return iCalUId; + } + + public void setiCalUId(String iCalUId) { + this.iCalUId = iCalUId; + } + + public List getInstances() { + return this.instances; + } + + public void setInstances(List instances) { + this.instances = new ArrayList<>(instances); + } + + public Boolean getIsAllDay() { + return isAllDay; + } + + public void setIsAllDay(Boolean isAllDay) { + this.isAllDay = isAllDay; + } + + public Boolean getIsCancelled() { + return isCancelled; + } + + public void setIsCancelled(Boolean isCancelled) { + this.isCancelled = isCancelled; + } + + public Boolean getIsDraft() { + return isDraft; + } + + public void setIsDraft(Boolean isDraft) { + this.isDraft = isDraft; + } + + public Boolean getIsOnlineMeeting() { + return isOnlineMeeting; + } + + public void setIsOnlineMeeting(Boolean isOnlineMeeting) { + this.isOnlineMeeting = isOnlineMeeting; + } + + public Boolean getIsOrganizer() { + return isOrganizer; + } + + public void setIsOrganizer(Boolean isOrganizer) { + this.isOrganizer = isOrganizer; + } + + public Boolean getIsReminderOn() { + return isReminderOn; + } + + public void setIsReminderOn(Boolean isReminderOn) { + this.isReminderOn = isReminderOn; + } + + public String getOnlineMeetingUrl() { + return onlineMeetingUrl; + } + + public void setOnlineMeetingUrl(String onlineMeetingUrl) { + this.onlineMeetingUrl = onlineMeetingUrl; + } + + public String getOriginalEndTimeZone() { + return originalEndTimeZone; + } + + public void setOriginalEndTimeZone(String originalEndTimeZone) { + this.originalEndTimeZone = originalEndTimeZone; + } + + public OffsetDateTime getOriginalStart() { + return originalStart; + } + + public void setOriginalStart(OffsetDateTime originalStart) { + this.originalStart = originalStart; + } + + public String getOriginalStartTimeZone() { + return originalStartTimeZone; + } + + public void setOriginalStartTimeZone(String originalStartTimeZone) { + this.originalStartTimeZone = originalStartTimeZone; + } + + public Integer getReminderMinutesBeforeStart() { + return reminderMinutesBeforeStart; + } + + public void setReminderMinutesBeforeStart(Integer reminderMinutesBeforeStart) { + this.reminderMinutesBeforeStart = reminderMinutesBeforeStart; + } + + public Boolean getResponseRequested() { + return responseRequested; + } + + public void setResponseRequested(Boolean responseRequested) { + this.responseRequested = responseRequested; + } + + public String getSeriesMasterId() { + return seriesMasterId; + } + + public void setSeriesMasterId(String seriesMasterId) { + this.seriesMasterId = seriesMasterId; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getWebLink() { + return webLink; + } + + public void setWebLink(String webLink) { + this.webLink = webLink; + } + + @Nonnull + @Override + public Map getAdditionalData() { + return this.additionalData; + } + public void setAdditionalData (@Nonnull Map additionalData) { + this.additionalData = new HashMap<>(additionalData); + } + + @Nonnull + public Map> getFieldDeserializers() { + HashMap> fieldDeserializers = new HashMap<>(); + fieldDeserializers.put("allowNewTimeProposals", (n) -> setAllowNewTimeProposals(n.getBooleanValue())); + fieldDeserializers.put("bodyPreview", (n) -> setBodyPreview(n.getStringValue())); + fieldDeserializers.put("hasAttachments", (n) -> setHasAttachments(n.getBooleanValue())); + fieldDeserializers.put("hideAttendees", (n) -> setHideAttendees(n.getBooleanValue())); + fieldDeserializers.put("iCalUId", (n) -> setiCalUId(n.getStringValue())); + fieldDeserializers.put("instances", (n) -> setInstances(n.getCollectionOfObjectValues(TestEventItem::createFromDiscriminatorValue))); + fieldDeserializers.put("isAllDay", (n) -> setIsAllDay(n.getBooleanValue())); + fieldDeserializers.put("isCancelled", (n) -> setIsCancelled(n.getBooleanValue())); + fieldDeserializers.put("isDraft", (n) -> setIsDraft(n.getBooleanValue())); + fieldDeserializers.put("isOnlineMeeting", (n) -> setIsOnlineMeeting(n.getBooleanValue())); + fieldDeserializers.put("isOrganizer", (n) -> setIsOrganizer(n.getBooleanValue())); + fieldDeserializers.put("isReminderOn", (n) -> setIsReminderOn(n.getBooleanValue())); + fieldDeserializers.put("onlineMeetingUrl", (n) -> setOnlineMeetingUrl(n.getStringValue())); + fieldDeserializers.put("originalEndTimeZone", (n) -> setOriginalEndTimeZone(n.getStringValue())); + fieldDeserializers.put("originalStart", (n) -> setOriginalStart(n.getOffsetDateTimeValue())); + fieldDeserializers.put("originalStartTimeZone", (n) -> setOriginalStartTimeZone(n.getStringValue())); + fieldDeserializers.put("reminderMinutesBeforeStart", (n) -> setReminderMinutesBeforeStart(n.getIntegerValue())); + fieldDeserializers.put("responseRequested", (n) -> setResponseRequested(n.getBooleanValue())); + fieldDeserializers.put("seriesMasterId", (n) -> setSeriesMasterId(n.getStringValue())); + fieldDeserializers.put("subject", (n) -> setSubject(n.getStringValue())); + fieldDeserializers.put("transactionId", (n) -> setTransactionId(n.getStringValue())); + fieldDeserializers.put("webLink", (n) -> setWebLink(n.getStringValue())); + return fieldDeserializers; + } + + public void serialize(@Nonnull SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeBooleanValue("allowNewTimeProposals", getAllowNewTimeProposals()); + writer.writeStringValue("bodyPreview", getBodyPreview()); + writer.writeBooleanValue("hasAttachments", getHasAttachments()); + writer.writeBooleanValue("hideAttendees", getHideAttendees()); + writer.writeStringValue("iCalUId", getiCalUId()); + writer.writeCollectionOfObjectValues("instances", getInstances()); + writer.writeBooleanValue("isAllDay", getIsAllDay()); + writer.writeBooleanValue("isCancelled", getIsCancelled()); + writer.writeBooleanValue("isDraft", getIsDraft()); + writer.writeBooleanValue("isOnlineMeeting", getIsOnlineMeeting()); + writer.writeBooleanValue("isOrganizer", getIsOrganizer()); + writer.writeBooleanValue("isReminderOn", getIsReminderOn()); + writer.writeStringValue("onlineMeetingUrl", getOnlineMeetingUrl()); + writer.writeStringValue("originalEndTimeZone", getOriginalEndTimeZone()); + writer.writeOffsetDateTimeValue("originalStart", getOriginalStart()); + writer.writeStringValue("originalStartTimeZone",getOriginalStartTimeZone()); + writer.writeIntegerValue("reminderMinutesBeforeStart", getReminderMinutesBeforeStart()); + writer.writeBooleanValue("responseRequested", getResponseRequested()); + writer.writeStringValue("seriesMasterId", getSeriesMasterId()); + writer.writeStringValue("subject", getSubject()); + writer.writeStringValue("transactionId", getTransactionId()); + writer.writeStringValue("webLink", getWebLink()); + } + + public static TestEventItem createFromDiscriminatorValue(ParseNode parseNode) { + if (parseNode == null) { + throw new IllegalArgumentException("parseNode"); + } + return new TestEventItem(); + } +} diff --git a/src/test/java/com/microsoft/graph/testModels/TestEventsDeltaResponse.java b/src/test/java/com/microsoft/graph/testModels/TestEventsDeltaResponse.java new file mode 100644 index 000000000..3ae5f4f8a --- /dev/null +++ b/src/test/java/com/microsoft/graph/testModels/TestEventsDeltaResponse.java @@ -0,0 +1,78 @@ +package com.microsoft.graph.testModels; + +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; + +import java.util.*; +import java.util.function.Consumer; + +public class TestEventsDeltaResponse implements Parsable, AdditionalDataHolder { + public Map additionalData; + private String odataDeltaLink; + private String odataNextLink; + public List value; + + public TestEventsDeltaResponse() { + additionalData = new HashMap<>(); + } + + public Map getAdditionalData() { + return additionalData; + } + + public void setAdditionalData(Map additionalData) { + this.additionalData = additionalData; + } + + public String getOdataDeltaLink() { + return odataDeltaLink; + } + + public void setOdataDeltaLink(String odataDeltaLink) { + this.odataDeltaLink = odataDeltaLink; + } + + public String getOdataNextLink() { + return odataNextLink; + } + + public void setOdataNextLink(String odataNextLink) { + this.odataNextLink = odataNextLink; + } + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } + + public Map> getFieldDeserializers() { + HashMap> fieldDeserializers = new HashMap<>(); + fieldDeserializers.put("@odata.deltaLink", (n) -> setOdataDeltaLink(n.getStringValue())); + fieldDeserializers.put("@odata.nextLink", (n) -> setOdataNextLink(n.getStringValue())); + fieldDeserializers.put("value", (n) -> setValue(n.getCollectionOfObjectValues(TestEventItem::createFromDiscriminatorValue))); + return fieldDeserializers; + } + + public void serialize(SerializationWriter writer) { + if (writer == null) { + throw new IllegalArgumentException("writer"); + } + + writer.writeStringValue("@odata.deltaLink", odataDeltaLink); + writer.writeStringValue("@odata.nextLink", odataNextLink); + writer.writeCollectionOfObjectValues("value", value); + writer.writeAdditionalData(additionalData); + } + + public static TestEventsDeltaResponse createFromDiscriminatorValue(ParseNode parseNode) { + if (parseNode == null) { + throw new IllegalArgumentException("parseNode"); + } + return new TestEventsDeltaResponse(); + } +} diff --git a/src/test/java/com/microsoft/graph/testModels/TestEventsResponse.java b/src/test/java/com/microsoft/graph/testModels/TestEventsResponse.java new file mode 100644 index 000000000..261bde739 --- /dev/null +++ b/src/test/java/com/microsoft/graph/testModels/TestEventsResponse.java @@ -0,0 +1,68 @@ +package com.microsoft.graph.testModels; + + +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +public class TestEventsResponse implements Parsable, AdditionalDataHolder { + public Map additionalData; + private String odataNextLink; + public List value; + + public TestEventsResponse() { + additionalData = new HashMap<>(); + } + + @Nonnull + public Map getAdditionalData() { + return additionalData; + } + + public void setAdditionalData(Map additionalData) { + this.additionalData = additionalData; + } + + public String getOdataNextLink() { + return odataNextLink; + } + + public void setOdataNextLink(String odataNextLink) { + this.odataNextLink = odataNextLink; + } + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } + @Nonnull + public Map> getFieldDeserializers() { + HashMap> fieldDeserializers = new HashMap<>(); + fieldDeserializers.put("@odata.nextLink", (n) -> setOdataNextLink(n.getStringValue())); + fieldDeserializers.put("value", (n) -> setValue(n.getCollectionOfObjectValues(TestEventItem::createFromDiscriminatorValue))); + return fieldDeserializers; + } + + public void serialize(@Nonnull SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeStringValue("@odata.nextLink", getOdataNextLink()); + writer.writeCollectionOfObjectValues("value", getValue()); + writer.writeAdditionalData(getAdditionalData()); + } + + public static TestEventsResponse createFromDiscriminatorValue(ParseNode parseNode) { + Objects.requireNonNull(parseNode); + return new TestEventsResponse(); + } +}