├── .github ├── CODEOWNERS └── policies │ └── msgraph-sdk-design-branch-protection.yml ├── ArchitecturalConstraints.md ├── CODE_OF_CONDUCT.md ├── GraphClientFactory.md ├── GraphRequest.md ├── GraphResponse.md ├── LICENSE ├── Observability.md ├── README.md ├── Repositories.md ├── ResponseHandler.md ├── SECURITY.md ├── Versions.md ├── _config.yml ├── assets └── css │ └── style.scss ├── content ├── BatchRequestContent.md ├── BatchRequestContentCollection.md ├── BatchResponseContent.md ├── BatchResponseContentCollection.md ├── ContentArchitecturalConstraints.md ├── ErrorContent.md ├── MultipartContent.md ├── StreamingRequestContent.md └── StreamingResponseContent.md ├── images └── componentArch.png ├── middleware ├── AuthorizationHandler.md ├── BodyInspectionHandler.md ├── ChaosHandler.md ├── CompressionHandler.md ├── ConnectionPoolManager.md ├── DecompressionHandler.md ├── DeltaResponseHandler.md ├── HeadersInspectionHandler.md ├── LoggingHandler.md ├── LongRunningOperationHandler.md ├── ParametersNameDecodingHandler.md ├── RedirectHandler.md ├── RequestContext.md ├── RetryHandler.md ├── ServiceDiscoveryHandler.md ├── SunsetHandler.md ├── TelemetryHandler.md └── middleware.md ├── providers ├── AuthenticationProvider.md ├── LoggingProvider.md └── providers.md ├── raptor └── pipeline.md └── tasks ├── FileUploadTask.md └── PageIteratorTask.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @microsoftgraph/msgraph-devx-eng 2 | -------------------------------------------------------------------------------- /.github/policies/msgraph-sdk-design-branch-protection.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # File initially created using https://github.com/MIchaelMainer/policyservicetoolkit/blob/main/branch_protection_export.ps1. 5 | 6 | name: msgraph-sdk-design-branch-protection 7 | description: Branch protection policy for the msgraph-sdk-design repository 8 | resource: repository 9 | configuration: 10 | branchProtectionRules: 11 | 12 | - branchNamePattern: main 13 | # This branch pattern applies to the following branches as of 06/12/2023 10:31:12: 14 | # main 15 | 16 | # Specifies whether this branch can be deleted. boolean 17 | allowsDeletions: false 18 | # Specifies whether forced pushes are allowed on this branch. boolean 19 | allowsForcePushes: false 20 | # Specifies whether new commits pushed to the matching branches dismiss pull request review approvals. boolean 21 | dismissStaleReviews: true 22 | # Specifies whether admins can overwrite branch protection. boolean 23 | isAdminEnforced: false 24 | # Indicates whether "Require a pull request before merging" is enabled. boolean 25 | requiresPullRequestBeforeMerging: true 26 | # Specifies the number of pull request reviews before merging. int (0-6). Should be null/empty if PRs are not required 27 | requiredApprovingReviewsCount: 1 28 | # Require review from Code Owners. Requires requiredApprovingReviewsCount. boolean 29 | requireCodeOwnersReview: true 30 | # Are commits required to be signed. boolean. TODO: all contributors must have commit signing on local machines. 31 | requiresCommitSignatures: false 32 | # Are conversations required to be resolved before merging? boolean 33 | requiresConversationResolution: true 34 | # Are merge commits prohibited from being pushed to this branch. boolean 35 | requiresLinearHistory: false 36 | # Require branches to be up to date before merging. Requires requiredStatusChecks. boolean 37 | requiresStrictStatusChecks: true 38 | # Indicates whether there are restrictions on who can push. boolean. Should be set with whoCanPush. 39 | restrictsPushes: false 40 | # Restrict who can dismiss pull request reviews. boolean 41 | restrictsReviewDismissals: false 42 | 43 | -------------------------------------------------------------------------------- /ArchitecturalConstraints.md: -------------------------------------------------------------------------------- 1 | # Architectural Constraints 2 | 3 | All SDKs should conform to the following constraints to ensure sufficient flexibility for developers using the SDKs in various scenarios. 4 | 5 | - Translate Graph specific request objects into native request objects. 6 | - Create native client object configured to make Graph requests 7 | - Handle native response objects with Graph optimized behavior 8 | - Graph payloads must by serializable and deserializable 9 | - Model coordinated graph requests as Task objects 10 | 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) 11 | -------------------------------------------------------------------------------- /GraphClientFactory.md: -------------------------------------------------------------------------------- 1 | # Graph Client Factory 2 | 3 | ## Objectives 4 | 5 | Provide a class that can be used to create an instances of platform native HTTP client library that are pre-configured for making requests to Graph APIs. 6 | 7 | There are two primary usage scenarios for the client factory. Developers who wish to make HTTP requests using the native HTTP library can use the factory to create an instance of the native library that is preconfigured to access the Graph API. The second scenario is where the service library client uses the factory for creating its native HTTP client library internally. 8 | 9 | ## Requirements 10 | 11 | - Enable developer to create a native HTTP client instance that is configured with a pipeline of middleware for Graph requests 12 | - Allow developer to provide a custom pipeline that future create requests will respect 13 | - Configure default HTTP handling behavior of native library 14 | - Define default request timeout (100 secs) 15 | - If a platform supports connection timeouts, then allow configuration of that. (default 30 secs) 16 | - Override the default base address for requests 17 | - Configure default request headers 18 | - Set the SdkVersion header with the appropriate moniker based on the following structure `graph-{lang}-{version}`. 19 | - Enable the developer to select a supported sovereign cloud using an enumerated list. Selecting the sovereign cloud should ensure that the AuthenticationProvider uses the appropriate Authentication Endpoint. 20 | - Enable a developer to configure a HTTP proxy that will be used for outgoing requests. 21 | - Enable a developer to provide custom hosts. 22 | - `CustomHosts` option should be set on client creation. 23 | - `CustomHosts` option should be made available in the `Context` so that it is available to the middleware during request processing. 24 | - These hostnames are different from the Graph service endpoints on the national clouds. 25 | - Certain workloads error out when an unexpected header is present in the request. The middlewares should check: 26 | - If the request URL is a Graph service endpoint or a custom host provided by the developer, then append request headers or modify the request content. 27 | - Else the middleware should delete request headers added by that middleware. 28 | - Example of an workload error - [LargeFileUploadTask upload to OneDrive caused CORS error due `SDKVersion` telemetry header](https://github.com/microsoftgraph/msgraph-sdk-javascript/issues/265) 29 | - Provide capabilities to modify or update the `CustomHosts` option after the client creation. 30 | 31 | ## Performance Considerations 32 | 33 | - If available on the platform, enable gzip compression of requests and responses. 34 | 35 | ## Future Implementation 36 | 37 | - Enable developers to set `CustomHosts`. The progress can be tracked as follows: 38 | ``` 39 | | SDK | Implementation Status| 40 | |-------------|----------------------| 41 | | C# | - | 42 | | JAVA | - | 43 | | PHP | - | 44 | | JavaScript | In Progress | 45 | ``` 46 | ## Security Considerations 47 | 48 | - If available on the platform, configure for TLS 1.2 49 | 50 | Authorization tokens MUST not be added to the default request headers. 51 | 52 | ## Open Issues 53 | 54 | - Should we define a response stream read timeout? 55 | - Can the factory detect from the environment which is the appropriate sovereign cloud? 56 | - Should SdkVersion be set as a default header here in the GraphClientFactory, or should it be put assigned by the TelemetryHandler? Or both? 57 | -------------------------------------------------------------------------------- /GraphRequest.md: -------------------------------------------------------------------------------- 1 | # GraphRequest Object 2 | 3 | TBD -------------------------------------------------------------------------------- /GraphResponse.md: -------------------------------------------------------------------------------- 1 | # GraphResponse Object 2 | 3 | ## Objectives 4 | 5 | Provide a decorator for the native response message to enable attaching Graph specific behaviour and state to a native HTTP response object. 6 | 7 | ## Requirements 8 | 9 | - Maintain reference to corresponding GraphRequest object 10 | - Provide access to HTTP response headers, status codes, and the raw response body. 11 | - Enable transformation from GraphResponse object to native response object. This may be lossy. 12 | - Enable transformation from native response object to GraphResponse object 13 | - Provide access to response content as domain object. 14 | - Provide a method to lazy deserialize the response body into our generated models or another object type based on whether a custom deserializer is provided. 15 | - Possilbly provide support providing a custom deserializer to override the default deserializer. 16 | 17 | ## Performance Considerations 18 | 19 | Custom deserializer support can enable customers to craft a deserializer optimized for a specific response. 20 | 21 | ## Background 22 | 23 | The generated .Net client library either returns: 24 | 1. the response content as an object or collection object as the result of a successful call to Microsoft Graph, or 25 | 2. a ServiceException in the case the call fails. 26 | 27 | We consider this a design flaw as the client library swallows useful information such as the response headers and the http status code. Mitigation such as stuffing the information in AdditionalData is insufficient for all scenarios. 28 | For example, there is no AdditionalData on Stream objects, and Delete operations return void. 29 | 30 | Related to: 31 | 32 | - https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/457 33 | - https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/361 34 | 35 | 36 | ## Current Implementations 37 | 38 | ### Javascript 39 | 40 | The Javascript SDK exposes the native [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object when selecting the RAW ResponseType option. 41 | 42 | ### .Net 43 | 44 | > Currently not supported 45 | 46 | ### Java 47 | 48 | > Currently not supported. Should be able to use the same pattern as .Net. 49 | 50 | ### PHP 51 | 52 | The PHP SDK returns a Graph model (or collection) or a GraphResponse object, depending on whether a returnType is specified. This means that the returnType information is not supplied to the GraphResponse. The response body is set as a JSON array on GraphResponse. The only work I'd suggest here is that we add an option to pass the returnType to the GraphResponse and add a lazy deserialize method so it is easy to get the Graph models. 53 | 54 | ## The GraphResponse Object 55 | 56 | The GraphResponse object can wrap the native HttpResponseMessage object so that customers can use the familiar HttpResponseMessage members. 57 | GraphResponse will be part of Microsoft.Graph.Core and it will expose the headers,Content and status code while also keeping reference to the Request object as below. 58 | 59 | 60 | ### GraphResponse 61 | ```csharp 62 | 63 | /// 64 | /// The GraphResponse Object 65 | /// 66 | public class GraphResponse : IDisposable 67 | { 68 | /// 69 | /// The GraphResponse Constructor 70 | /// 71 | /// The Request made for the response 72 | /// The response 73 | public GraphResponse(IBaseRequest iBaseRequest,HttpResponseMessage httpResponseMessage) 74 | { 75 | this.httpResponseMessage = httpResponseMessage; 76 | this.BaseRequest = iBaseRequest; 77 | } 78 | 79 | private readonly HttpResponseMessage httpResponseMessage; 80 | 81 | /// 82 | /// The Response Status code 83 | /// 84 | public HttpStatusCode StatusCode => httpResponseMessage.StatusCode; 85 | 86 | /// 87 | /// The Response Content 88 | /// 89 | public HttpContent Content => httpResponseMessage.Content; 90 | 91 | /// 92 | /// The Response Headers 93 | /// 94 | public HttpResponseHeaders HttpHeaders => httpResponseMessage.Headers; 95 | 96 | /// 97 | /// The reference to the Request 98 | /// 99 | public IBaseRequest BaseRequest; 100 | 101 | /// 102 | /// Get the native Response Message 103 | /// 104 | /// 105 | public HttpResponseMessage ToHttpResponseMessage() 106 | { 107 | return httpResponseMessage; 108 | } 109 | 110 | /// 111 | /// Cleanup 112 | /// 113 | public void Dispose() 114 | { 115 | httpResponseMessage?.Dispose(); 116 | } 117 | 118 | /// 119 | /// String representain of GraphResponse 120 | /// 121 | public override string ToString() 122 | { 123 | return $"Status: {StatusCode}"; 124 | } 125 | } 126 | 127 | ``` 128 | 129 | ### GraphResponse< T > 130 | 131 | This derives from non generic GraphResponse and provides a method of deserialization using a function called `GetResponseObjectAsync()` 132 | 133 | ```csharp 134 | 135 | /// 136 | /// The GraphResponse Object 137 | /// 138 | public class GraphResponse : GraphResponse 139 | { 140 | /// 141 | /// The GraphResponse Constructor 142 | /// 143 | /// The Request made for the response 144 | /// The response 145 | public GraphResponse(IBaseRequest iBaseRequest, HttpResponseMessage httpResponseMessage) 146 | : base(iBaseRequest, httpResponseMessage) 147 | { 148 | } 149 | 150 | /// 151 | /// Gets the deserialized object 152 | /// 153 | public async Task GetResponseObjectAsync() 154 | { 155 | return await this.BaseRequest.ResponseHandler.HandleResponse(this); 156 | } 157 | } 158 | 159 | ``` 160 | 161 | ## Exposing the GraphResponse object from the Service Library 162 | 163 | We can create parallel *Async functions to enable the GraphResponse object functionality. We will generate requests with the following public API signatures: 164 | 165 | * `GetResponseAsync(): : GraphResponse` 166 | * `CreateResponseAsync(NewObject: Entity) : GraphResponse` 167 | * `PostResponseAsync(NewObject: Entity) : GraphResponse` 168 | * `UpdateResponseAsync(UpdatedObject: Entity) : GraphResponse` 169 | * `DeleteResponseAsync() : GraphResponse` //no generic here 170 | 171 | These would in turn call a function in the core library(BaseRequest) that looks something like 172 | 173 | ```csharp 174 | 175 | public async Task SendAsyncWithGraphResponse() 176 | { 177 | var response = await this.SendRequestAsync(serializableObject, cancellationToken, completionOption).ConfigureAwait(false)) 178 | return new GraphResponse(this,response); 179 | } 180 | 181 | ``` 182 | ### Example Usage 183 | 184 | Example calls will look like this. 185 | 186 | ```csharp 187 | 188 | GraphResponse response = await graphServiceClient.Users.Request().GetWithGraphResponseAsync(cancellationToken); 189 | 190 | GraphResponse response = await graphServiceClient.Me.Request().UpdateWithGraphResponseAsync(patchUser, cancellationToken); 191 | 192 | ``` 193 | 194 | ### Other Notes 195 | 196 | * Delete requests can (by default) return GraphResponse objects rather than void 197 | * Pro: Use generator to get capabilty annotation generation support. 198 | * Cons: T4 template code to maintain. 199 | * We can possibly add extension methods to GraphResponse to also return domain objects 200 | 201 | ## Late and Custom Deserialization 202 | 203 | This can be done exposing the `IResponseHandler` interface as below. (This will involve some tweaking of the current ResponseHandlers as well). 204 | 205 | ```csharp 206 | 207 | /// 208 | /// The interface required for all response handlers. 209 | /// 210 | public interface IResponseHandler 211 | { 212 | /// 213 | /// Process raw HTTP response into the requested domain type. 214 | /// 215 | /// The type to return 216 | /// The HttpResponseMessage to handle. 217 | /// 218 | Task HandleResponse(HttpResponseMessage response); 219 | 220 | /// 221 | /// Process the GraphResponse into the requested domain type. 222 | /// 223 | /// The type to return 224 | /// The GraphResponse to handle. 225 | /// 226 | Task HandleResponse(GraphResponse graphResponse); 227 | } 228 | 229 | ``` 230 | 231 | This means that customers could use a custom serializer while implementing the interface and simply throw in a `GraphResponse` object into it. 232 | If we align our current ResponseHandler to this, they could possibly just use our ResponseHandler as well. 233 | 234 | Therefore a developer could possibly follow the following steps. 235 | 236 | ```csharp 237 | 238 | ISerializer serailizer = new CustomSerializer(); //Custom Serializer 239 | IResponseHandler responseHandler = new ResponseHandler(serailizer); // Our ResponseHadler with custom Serializer 240 | 241 | GraphResponse graphResponse = await graphServiceClient.Me.Request() 242 | .WithResponseHandler(responseHandler)// customized yay! 243 | .UpdateWithGraphResponseAsync(patchUser, cancellationToken);//response with no serialization 244 | 245 | // 246 | // Do other stuff here like check status/headers 247 | // 248 | 249 | User user = graphResponse.GetResponseObjectAsync(); //calls the responsehandler with custom serailizer 250 | 251 | ``` 252 | 253 | ## Opportunities 254 | 255 | * `SendAsync` calls in the core library can be updated/revised to return `GraphResponse` rather than `HttpReponseMessage` 256 | 257 | * Opportunity to implement stuff from our proposed "breaking moment" a simplified http provider. This would also allow us to tryout enabling custom serlializer as well. 258 | 259 | * We could release this as an experimental preview. After preview, we de-emphasize *Async classic methods and update the snippet generator to use this form. Deemphasis will be done via blog post and /// comment. 260 | We instrument the generated methods so we discover usage. 261 | We deprecate when we hit a threshold. Deprecation will be done via blog post and /// comment. 262 | We can delete at another threshold. The cool thing is that they will still get the latest functionality in the service libraries since they are decoupled. 263 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Microsoft Graph 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Observability.md: -------------------------------------------------------------------------------- 1 | # Observability 2 | 3 | [Observability](https://lightstep.com/blog/opentelemetry-101-what-is-observability/) generally refers to the ability to understand an application’s performance based on output data. This telemetry data is divided into three types: metrics, tracing and logging. 4 | 5 | Microsoft Graph SDKs should provide the ability for application developers to gather data about the calls made via the SDKs. No telemetry data is captured and transmitted to Microsoft beyond what is included in the HTTP request headers. 6 | 7 | ## OpenTelemetry 8 | 9 | [OpenTelemetry](https://opentelemetry.io) is an industry standard for capturing observability data from applications. The OpenTelemetry project has a set of libraries that contain the interfaces that an application interacts with to capture data. Observability can be divided in four main different goals: 10 | 11 | - Telemetry: allows the owners of a product to understand better its usage. 12 | - Tracing: allows the administrators of an application to understand the details of an action end to end, across multiple systems. 13 | - Logging: allows the administrators of an application to understand what is happening for the application globally, this aspect is orthogonal to the tracing aspect. 14 | - Metrics: allows the administrators to measures specific events or facets of the application. (e.g. number of requests per minute, average latency...) 15 | 16 | The design guidelines already provide separate details about the telemetry aspect and it'll be out of scope for the current document. The logging specification for Open Telemetry is currently in a draft state, and sparsely implemented, for that reason we'll revisit logging requirements at a later time. For metrics, we need to get a better understanding of what elements applications administrators care about in their day to day activities, more user research is required. 17 | 18 | ## Why not just write to logs? 19 | 20 | As application developers we are used to injecting a logger into our code and using log statements to add useful messages to a log file. Our log messages are often used to signal a particular code path has been taken. A common question is whether to log the start of a section of code, or the end of a section of code. Sometimes it makes sense to log both to understand if a section has been entered and whether it was completed successfully. There are times when it is useful to understand the time it took for a section of code. Where code is reused it is often useful to understand the calling context of the code. All of this can be done using log statements. However, OpenTelemetry offers better solution. 21 | 22 | OpenTelemetry provides different model for capturing information about the execution of a program. It uses a span to surround a section of code. A span always has a start and an end, and spans can be nested. When instrumenting library code, spans are created to identify interesting sections of code. Spans also allow attaching information that provides context about the currently executing section of code. 23 | 24 | As a library author, adding a log statement comes with an expectation that a string will be written to a log file (or some other log storage) if the application developer has enabled the appropriate level level of logging. The OpenTelemetry model does not make the same commitments to the library author. The library author suggests that a span might be of interest to the application developer, but how that information is processed and recorded is the responsibility of the `collector` component that is configured by the application developer. The collector decides if the start and the end of a span will be recorded. It decides if the data elements are stored. It decides if the span should be timed. Some collectors are sufficiently advanced that they may buffer the `trace` of a set of nested spans and based on the outcome decide if they are going to store the information. For example, if a HTTP call fails, then record the span details. Or, if a call takes more than some configured duration, then the span details are recorded. A collector can be configured to either serialize events based on the timeline of when they happen regardless of the thread that is executing, or they can be collected to show just the events related to a single thread of execution. This is one of the major distinctions between logging and tracing. Tracing shows activity related to a single inbound request to the library, whereas logging show all activity performed by the library. 25 | 26 | Spans also have more advanced concepts like a status to indicate if the span completed successfully. It is also possible to link spans together. Spans can also have events attached to them to indicate point in time events similar to how log events are captured. 27 | 28 | While OpenTelemetry uses terms like `spans`, `baggage`, `traces`, `metrics`, not all of the libraries that support OpenTelemetry collectors use the same terms. For example, in .NET span is represented by an Activity in the DiagnosticSource library. The OpenTelemetry ecosystem is a bit messy in its design because it is result of a set of pragmatic compromises between a large number of existing APM (Application Performance Monitoring) vendors that already had existing solutions. The benefit is broad operability and future that is far more aligned than the past. 29 | 30 | The important take-away from this for us as SDK developers, is that with this slightly different model for capturing information about our running SDKs, we open up a wide range of possibilities for developers writing apps with our SDKs to get very clear visibility into what is happening inside our library. 31 | 32 | ## What to capture - tracing 33 | 34 | Open Telemetry spans are nested and can have attributes attached to each span. The following spans should be created for each request with the indicated attributes. 35 | 36 | ```JSONC 37 | { 38 | "ApplicationRequest?": { // this span is owned by the application itself in the calling code that calls the chained API surface or equivalent 39 | "SetContentFromParsable?": { // request information or equivalent when a request body is used from a model, also applies to setContentFromScalar and from stream 40 | "com.microsoft.kiota.request.type": "Fully qualified name of the content model" 41 | }, 42 | "SendAsync - ": { // http request adapter or equivalent, also applies to the other overloads like sendPrimitiveAsync etc... 43 | "http.response.body.size?": "int value for the response content length", 44 | "http.response.status_code": "http response status code", 45 | "network.protocol.name": "1.1 or 2 or 3", 46 | "http.response.header.content-type?": "value from the response content type header", 47 | "http.request.method": "http method for the request", 48 | "url.scheme": "http or https", 49 | "url.full!": "URI of the request", 50 | "server.address": "request host", 51 | "http.request.body.size?": "int value for the request content length", 52 | "http.request.header.content-type?": "value from the request content type header", 53 | "url.uri_template": "URI template for the request", 54 | "com.microsoft.kiota.response.type": "The fully qualified name of the deserialized model", 55 | "getHttpResponseMessage": { 56 | "GetAuthorizationToken": { // comes from the authentication provider 57 | "com.microsoft.kiota.authentication.is_url_valid": "true if the URL was valid, false otherwise (https, host)", 58 | "com.microsoft.kiota.authentication.additional_claims_provided": "true when CAE/POP claims were provided by the request adapter, false otherwise", 59 | "com.microsoft.kiota.authentication.scopes": "comma separated value of the scopes for the request" 60 | }, 61 | "getRequestFromRequestInformation": { 62 | // collection of all the http attributes from the parent span might be done here, this span is here to measure errors/timing of the conversion to a native object 63 | }, 64 | "RetryHandler_intercept?": { // single span when the request is successful, multiple when retries occurred 65 | "com.microsoft.kiota.handler.retry.enable": "boolean, true if the handler is present and enabled", 66 | "http.request.resend_count": "int number of retries that occurred before the request was successful", 67 | "http.request.resend_delay?": "int duration in seconds before the request was retried", 68 | "http.response.status_code": "status code of the response", 69 | "RedirectHandler_intercept?": { // single span when the request is successful, multiple when retries occurred 70 | "com.microsoft.kiota.handler.redirect.enable": "boolean, true if the handler is present and enabled", 71 | "com.microsoft.kiota.handler.redirect.count": "int, the number of redirects that occurred", 72 | "http.response.status_code": "status code of the response", 73 | "CompressionHandler_intercept?": { 74 | "com.microsoft.kiota.handler.compression.enable": "boolean, true if the handler is present and enabled", 75 | "http.request.body.compressed": "boolean, true if the response was compressed by the handler", 76 | "http.request.body.size": "long, byte size of the request body", 77 | "DecompressionHandler_intercept?": { 78 | "com.microsoft.kiota.handler.decompression.enable": "boolean, true if the handler is present and enabled", 79 | "ParametersNameDecodingHandler_intercept?": { 80 | "com.microsoft.kiota.handler.parameters_name_decoding.enable": "boolean, true if the handler is present and enabled", 81 | "SunsetHandler_intercept?": { 82 | "com.microsoft.kiota.handler.sunset.enable": "boolean, true if the handler is present and enabled", 83 | "Event - com.microsoft.kiota.sunset_header_received?": { 84 | // raised only if a sunset header is present in the response 85 | "sunset_date": "Date at internet date format from the sunset header value", 86 | "sunset_link?": "link header with a sunset value for rel (only the URI)" 87 | }, 88 | "HeadersInspectionHandler_intercept?": { 89 | "com.microsoft.kiota.handler.headersInspection.enable": "boolean, true if the handler is present and enabled", 90 | "BodyInspectionHandler_intercept?": { 91 | "com.microsoft.kiota.handler.bodyInspection.enable": "boolean, true if the handler is present and enabled", 92 | "AuthorizationHandler_intercept?": { 93 | "com.microsoft.kiota.handler.authorization.enable": "boolean, true if the handler is present and enabled", 94 | "com.microsoft.kiota.handler.authorization.token_present?": "boolean, true if an authorization header was already present", 95 | "com.microsoft.kiota.handler.authorization.token_obtained?": "boolean, true if obtaining the token was successful", 96 | "Event - com.microsoft.kiota.handler.authorization.challenge_received?" : { 97 | // raised only if a www-authenticate header has been received and we're obtaining a new token 98 | "com.microsoft.kiota.handler.authorization.token_obtained": "boolean, true if obtaining the token was successful", 99 | "http.request.resend_count": "int number of retries that occurred before the request was successful" 100 | }, 101 | "Request_transport?": { 102 | // this span is present only to measure network latency and deduct it from the middleware pipeline 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | }, 113 | "retryCAEResponseIfRequired": { 114 | "http.request.resend_count?": "1 of we had to retry for CAE challenge, not set otherwise", 115 | "Event - com.microsoft.kiota.authenticate_challenge_received?": { 116 | // raised only if the application is receiving a CAE challenge 117 | } 118 | }, 119 | }, 120 | "throwFailedResponses": { 121 | "com.microsoft.kiota.error.mapping_found": "boolean, whether the client has error mappings for that status code", 122 | "com.microsoft.kiota.error.body_found": "boolean, whether the client found an error body in the response", 123 | "status": "error", // use the set status method for those two, they are a special kind of attribute 124 | "status_message": "received_error_response" 125 | }, 126 | "Event - com.microsoft.kiota.response_handler_invoked?": { 127 | // raised only if the application has provided a custom response handler 128 | }, 129 | "getRootParseNode": { 130 | // to measure the time it gets to pull the stream 131 | }, 132 | "GetObjectValue": { 133 | // or the equivalent deserialization method name, this is mainly to measure deserialization errors/time 134 | } 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | Legend: 141 | 142 | - Objects braces describe a span. 143 | - Properties describe attributes/tags. 144 | - Spans prefixed with a `Event` represent an event. 145 | - Spans starting with an uppercase letter represent a public method and should always be present. 146 | - Spans starting with a lowercase letter represent a private method and might be structured differently depending on implementation details. 147 | - ? sign describes something optional (depending on the request or configuration). 148 | - ! should only be captured if the application has indicated the EUII can be captured. 149 | 150 | > Note: Error messages should never include EUII unless it is explicitly indicated by the application. 151 | > Note: spans that have the same parent span are described sequentially. 152 | > Note: the nesting of the middleware handlers depends on how they were configured by the client application. 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Graph SDKs - Requirements and Design 2 | 3 | This repository holds documents related to current and on-going work on Microsoft Graph SDKs. The following diagram shows a high level view of the SDK component architecture. The goal is to enable developers to opt-into functionality that they wish to use. Over time all the SDKs will be adapted to follow this pattern. 4 | 5 | ![Component Architecture](images/componentArch.png) 6 | 7 | ## SDK Features Support 8 | 9 | | Component |Feature| .net | Java | JS | Powershell | PHP | Ruby | Python | Go | TS | 10 | |--|--|--|--|--|--|--|--|--|--|--| 11 | |[Middleware](middleware/middleware.md) 12 | | | Pipeline |✓|✓|[✓][js_middleware]|✓| | |[✓][python_middleware]| [✓][go_middleware]| 13 | | | [Authorization Handler](middleware/AuthorizationHandler.md) | | |[✓][js_authhandler]| [✓][powershell_authhandler] | | | | | | 14 | | | [Retry Handler](middleware/RetryHandler.md) |[✓][dotnet_retryhandler]|[✓][java_retryhandler]|[✓][js_retryhandler]|[✓][dotnet_retryhandler]| | | [✓][python_retryhandler]| [✓][go_retryhandler] 15 | | | [Redirect Handler](middleware/RedirectHandler.md) |[✓][dotnet_redirecthandler]|[✓][java_redirecthandler]|[✓][js_redirecthandler]|[✓][dotnet_redirecthandler]| | |✓ | [✓][go_redirecthandler]| 16 | | | [Request compression Handler](middleware/CompressionHandler.md) | | | | | | | 17 | | | [Response decompression Handler](middleware/DecompressionHandler.md) |N|N|N|N| | 18 | | | [Logging Handler](middleware/LoggingHandler.md) (PowerShell only since other languages implement Observability/OTEL ) | N/A | N/A | |[✓][powershell_logginghandler]| N/A | N/A | N/A | N/A | N/A | 19 | | | [Telemetry Handler](middleware/TelemetryHandler.md) |[✓][dotnet_telemetryhandler]|[✓][java_telemetryhandler]|✓|[✓][powershell_telemetryhandler]| | |[✓][python_telemetryhandler] | [✓][go_telemetryhandler]| 20 | | | [Connection Management](middleware/ConnectionPoolManager.md) | | | | | | 21 | | | [Long Running Operations](middleware/LongRunningOperationHandler.md) | | | | | | 22 | | | [Chaos Handler](middleware/ChaosHandler.md) | |[✓][java_chaoshandler] |O| | | 23 | | | [Sunset Handler](middleware/SunsetHandler.md) | 24 | | | [Headers Inspection Handler](middleware/HeadersInspectionHandler.md) | [✓][dotnet_headersinspectionhandler] | [✓][java_headersinspectionhandler] | | | [✓][php_headersinspectionhandler] | | [✓][python_headersinspectionhandler] | [✓][go_headersinspectionhandler] | [✓][typescript_headersinspectionhandler] | 25 | | | [Parameters Name Decoding Handler](middleware/ParametersNameDecodingHandler.md) | [✓][dotnet_paramhandler] | [✓][java_paramhandler] | | N/A | [✓][php_paramhandler] | [✓][ruby_paramhandler] | [✓][python_paramhandler] | [✓][go_paramhandler] 26 | | | [Service Discovery Handler](middleware/ServiceDiscoveryHandler.md) | | | | | | 27 | | [Content](content/ContentArchitecturalConstraints.md) 28 | || [Batch Request Content](content/BatchRequestContent.md) |[✓][dotnet_batchrequestcontent]|[✓][java_batchrequestcontent]|[✓][js_batchrequestcontent]| | | 29 | || [Batch Response Content](content/BatchResponseContent.md) |[✓][dotnet_batchresponsecontent]|[✓][java_batchresponsecontent]|[✓][js_batchresponsecontent] | | | 30 | || [Multipart Content](content/MultipartContent.md) |✓|[✓][java_multipartcontent]| | | | 31 | || [Error Content](content/ErrorContent.md) | | | |N| | 32 | | Graph Components 33 | || [Client Factory](GraphClientFactory.md) |[✓][dotnet_clientfactory]|[✓][java_httpclients]|[✓][js_graphclientfactory]|[✓][dotnet_clientfactory]| | |[✓][python_graphclientfactory] | [✓][go_graphclientfactory] 34 | || [Response Handling](ResponseHandler.md) |[✓][dotnet_responsehandler]| |[✓][js_responsehandler]|✓|| 35 | | Tasks 36 | || [File Upload](tasks/FileUploadTask.md) | |[✓][java_largefileupload]|[✓][js_fileuploadtask] |[✓][powershell_fileupload] | | 37 | || [Page Iterator](tasks/PageIteratorTask.md) |[✓][dotnet_pageiteratortask]||[✓][js_pageiteratortask] |[✓][powershell_pageiterator]| | 38 | | [Providers](providers/providers.md) 39 | || [Authentication](providers/AuthenticationProvider.md) |[✓][dotnet_authprovider]|[✓][java_authprovider]|[✓][js_authprovider]|[✓][powershell_authprovider]| | | | [✓][go_authprovider]| 40 | || [Logging](providers/LoggingProvider.md) | | | |N| | 41 | 42 | ✓ - Completed 43 | O - In progress 44 | N - Native library support 45 | 46 | > NOTE: additionally all handler should provide observability and tracing support through Open Telemetry. [More information](./Observability.md) 47 | 48 | ## Supported Languages 49 | 50 | ### .NET 51 | 52 | |Role| Repo | Packages | 53 | |--|--|--| 54 | |Service+Models|[msgraph-sdk-dotnet](https://github.com/microsoftgraph/msgraph-sdk-dotnet)|[Nuget](https://www.nuget.org/packages/Microsoft.Graph/)| 55 | |Core|[msgraph-sdk-dotnet-core](https://github.com/microsoftgraph/msgraph-sdk-dotnet-core)|[Nuget](https://www.nuget.org/packages/Microsoft.Graph.Core/)| 56 | |Auth|[Azure Identity](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/identity/Azure.Identity)|[Nuget](https://www.nuget.org/packages/Azure.Identity/)| 57 | |Beta|[msgraph-beta-sdk-dotnet](https://github.com/microsoftgraph/msgraph-beta-sdk-dotnet)|[Nuget](https://www.nuget.org/packages/Microsoft.Graph.Beta/)| 58 | 59 | ### Go 60 | 61 | |Role| Repo | Packages | 62 | |--|--|--| 63 | |Service+Models|[msgraph-sdk-go](https://github.com/microsoftgraph/msgraph-sdk-go)|[pkg.go.dev](https://pkg.go.dev/github.com/microsoftgraph/msgraph-sdk-go/)| 64 | |Core|[msgraph-sdk-go-core](https://github.com/microsoftgraph/msgraph-sdk-go-core)|[pkg.go.dev](https://pkg.go.dev/github.com/microsoftgraph/msgraph-sdk-go-core/)| 65 | |Auth|[azure-identity](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity)|[pkg.go.dev](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity)| 66 | |beta|[msgraph-beta-sdk-go](https://github.com/microsoftgraph/msgraph-beta-sdk-go)|[pkg.go.dev](https://pkg.go.dev/github.com/microsoftgraph/msgraph-beta-sdk-go/)| 67 | 68 | ### Javascript 69 | 70 | |Role| Repo | Packages | 71 | |--|--|--| 72 | |Service+Core|[msgraph-sdk-javascript](https://github.com/microsoftgraph/msgraph-sdk-javascript)|| 73 | |Models|[msgraph-typescript-typings](https://github.com/microsoftgraph/msgraph-typescript-typings)|| 74 | 75 | ### Java 76 | 77 | |Role| Repo | Packages | 78 | |--|--|--| 79 | |Service+Models|[msgraph-sdk-java](https://github.com/microsoftgraph/msgraph-sdk-java)|[Maven Central](https://mvnrepository.com/artifact/com.microsoft.graph/microsoft-graph)| 80 | |Core|[msgraph-sdk-java-core](https://github.com/microsoftgraph/msgraph-sdk-java-core)|[Maven Central](https://mvnrepository.com/artifact/com.microsoft.graph/microsoft-graph-core)| 81 | |Auth|[azure-identity](https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/identity/azure-identity)|[Maven Central](https://mvnrepository.com/artifact/com.azure/azure-identity)| 82 | |beta|[msgraph-beta-sdk-java](https://github.com/microsoftgraph/msgraph-beta-sdk-java)|[Maven Central](https://mvnrepository.com/artifact/com.microsoft.graph/microsoft-graph-beta)| 83 | 84 | Java Core is based on the [OkHttp](https://github.com/square/okhttp). 85 | 86 | ### PHP 87 | 88 | |Role| Repo | Packages | 89 | |--|--|--| 90 | |Service+Models|[msgraph-sdk-php](https://github.com/microsoftgraph/msgraph-sdk-php)|| 91 | |Core|[msgraph-sdk-php-core](https://github.com/microsoftgraph/msgraph-sdk-php-core)|| 92 | |beta|[msgraph-sdk-php-beta](https://github.com/microsoftgraph/msgraph-beta-sdk-php)|| 93 | 94 | ### Ruby 95 | 96 | |Role| Repo | Packages | 97 | |--|--|--| 98 | |All|[msgraph-sdk-ruby](https://github.com/microsoftgraph/msgraph-sdk-ruby)| 99 | 100 | ### Python 101 | 102 | |Role| Repo | Packages | 103 | |--|--|--| 104 | |Service+Models|[msgraph-sdk-python](https://github.com/microsoftgraph/msgraph-sdk-python)|[PyPI](https://pypi.org/project/msgraph-sdk/)| 105 | |Core|[msgraph-sdk-python-core](https://github.com/microsoftgraph/msgraph-sdk-python-core)|[PyPI](https://pypi.org/project/msgraph-core/)| 106 | |beta|[msgraph-beta-sdk-python](https://github.com/microsoftgraph/msgraph-beta-sdk-python)|[PyPI](https://pypi.org/project/msgraph-beta-sdk/)| 107 | 108 | ## Issues 109 | 110 | View or log issues on the [Issues](https://github.com/microsoftgraph/msgraph-sdk-design/issues) tab in the repo. 111 | 112 | ## Copyright and license 113 | 114 | Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT [license](LICENSE). 115 | 116 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 117 | 118 | [java_authprovider]: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/identity/azure-identity 119 | [java_chaoshandler]: https://github.com/microsoft/kiota-java/blob/main/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/ChaosHandler.java 120 | [java_redirecthandler]: https://github.com/microsoft/kiota-java/blob/main/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RedirectHandler.java 121 | [java_retryhandler]: https://github.com/microsoft/kiota-java/blob/main/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java 122 | [java_telemetryhandler]: https://github.com/microsoft/kiota-java/blob/main/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/TelemetryHandler.java 123 | [java_batchrequestcontent]: https://github.com/microsoftgraph/msgraph-sdk-java-core/blob/main/src/main/java/com/microsoft/graph/core/content/BatchRequestContent.java 124 | [java_batchresponsecontent]: https://github.com/microsoftgraph/msgraph-sdk-java-core/blob/main/src/main/java/com/microsoft/graph/core/content/BatchResponseContent.java 125 | [java_multipartcontent]: https://github.com/microsoft/kiota-java/blob/main/components/abstractions/src/main/java/com/microsoft/kiota/MultipartBody.java 126 | [java_largefileupload]: https://github.com/microsoftgraph/msgraph-sdk-java-core/blob/main/src/main/java/com/microsoft/graph/core/tasks/LargeFileUploadTask.java 127 | [java_httpclients]: https://github.com/microsoftgraph/msgraph-sdk-java-core/blob/dev/src/main/java/com/microsoft/graph/httpcore/HttpClients.java 128 | [java_paramhandler]: https://github.com/microsoft/kiota-java/blob/main/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/ParametersNameDecodingHandler.java 129 | [java_headersinspectionhandler]: https://github.com/microsoft/kiota-java/blob/main/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/HeadersInspectionHandler.java 130 | 131 | [dotnet_authprovider]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/identity/Azure.Identity 132 | [dotnet_retryhandler]: https://github.com/microsoft/kiota-dotnet/blob/main/src/http/httpClient/Middleware/RetryHandler.cs 133 | [dotnet_redirecthandler]: https://github.com/microsoft/kiota-dotnet/blob/main/src/http/httpClient/Middleware/RedirectHandler.cs 134 | [dotnet_clientfactory]: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/main/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs 135 | [dotnet_batchrequestcontent]: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/main/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContent.cs 136 | [dotnet_batchresponsecontent]: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/main/src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs 137 | [dotnet_responsehandler]: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/main/src/Microsoft.Graph.Core/Requests/ResponseHandler.cs 138 | [dotnet_pageiteratortask]: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/main/src/Microsoft.Graph.Core/Tasks/PageIterator.cs 139 | [dotnet_telemetryhandler]: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/main/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs 140 | [dotnet_paramhandler]: https://github.com/microsoft/kiota-dotnet/blob/main/src/http/httpClient/Middleware/ParametersNameDecodingHandler.cs 141 | [dotnet_headersinspectionhandler]: https://github.com/microsoft/kiota-dotnet/blob/main/src/http/httpClient/Middleware/HeadersInspectionHandler.cs 142 | 143 | [js_middleware]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/middleware/IMiddleware.ts 144 | [js_authhandler]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/middleware/AuthenticationHandler.ts 145 | [js_responsehandler]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/GraphResponseHandler.ts 146 | [js_batchrequestcontent]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/content/BatchRequestContent.ts 147 | [js_batchresponsecontent]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/content/BatchResponseContent.ts 148 | [js_fileuploadtask]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/tasks/LargeFileUploadTask.ts 149 | [js_pageiteratortask]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/tasks/PageIterator.ts 150 | [js_redirecthandler]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/middleware/RedirectHandler.ts 151 | [js_retryhandler]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/middleware/RetryHandler.ts 152 | [js_graphclientfactory]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/HTTPClientFactory.ts 153 | [js_authprovider]: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/MSALAuthenticationProvider.ts 154 | 155 | [python_middleware]: https://github.com/microsoftgraph/msgraph-sdk-python-core/blob/dev/msgraph/core/middleware/middleware.py 156 | [python_retryhandler]: https://github.com/microsoftgraph/msgraph-sdk-python-core/blob/dev/msgraph/core/middleware/retry.py 157 | [python_graphclientfactory]: https://github.com/microsoftgraph/msgraph-sdk-python-core/blob/dev/msgraph/core/_client_factory.py 158 | [python_telemetryhandler]: https://github.com/microsoftgraph/msgraph-sdk-python-core/blob/dev/msgraph/core/middleware/telemetry.py 159 | [python_paramhandler]: https://github.com/microsoft/kiota-http-python/blob/main/kiota_http/middleware/parameters_name_decoding_handler.py 160 | [python_headersinspectionhandler]: https://github.com/microsoft/kiota-http-python/blob/main/kiota_http/middleware/headers_inspection_handler.py 161 | 162 | [go_middleware]: https://github.com/microsoft/kiota-http-go/blob/main/pipeline.go 163 | [go_authprovider]: https://github.com/microsoft/kiota-authentication-azure-go 164 | [go_retryhandler]: https://github.com/microsoft/kiota-http-go/blob/main/retry_handler.go 165 | [go_redirecthandler]: https://github.com/microsoft/kiota-http-go/blob/main/redirect_handler.go 166 | [go_graphclientfactory]: https://github.com/microsoftgraph/msgraph-sdk-go-core/blob/main/graph_client_factory.go 167 | [go_telemetryhandler]: https://github.com/microsoftgraph/msgraph-sdk-go-core/blob/main/graph_telemetry_handler.go 168 | [go_paramhandler]: https://github.com/microsoft/kiota-http-go/blob/main/parameters_name_decoding_handler.go 169 | [go_headersinspectionhandler]: https://github.com/microsoft/kiota-http-go/blob/main/headers_inspection_handler.go 170 | 171 | [powershell_telemetryhandler]: https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/src/Authentication/Authentication/Helpers/HttpHelpers.cs 172 | [powershell_logginghandler]: https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/tools/Custom/Module.cs 173 | [powershell_fileupload]: https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/tools/Custom/FileUploadCmdlet.cs 174 | [powershell_pageiterator]: https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/tools/Custom/ListCmdlet.cs 175 | [powershell_authprovider]: https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs 176 | [powershell_authhandler]: https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs 177 | 178 | [php_paramhandler]: https://github.com/microsoft/kiota-http-guzzle-php/blob/dev/src/Middleware/ParametersNameDecodingHandler.php 179 | [php_headersinspectionhandler]: https://github.com/microsoft/kiota-http-guzzle-php/blob/dev/src/Middleware/HeadersInspectionHandler.php 180 | 181 | [ruby_paramhandler]: https://github.com/microsoft/kiota-http-ruby/blob/main/lib/microsoft_kiota_faraday/middleware/parameters_name_decoding_handler.rb 182 | 183 | [typescript_headersinspectionhandler]: https://github.com/microsoft/kiota-typescript/blob/main/packages/http/fetch/src/middlewares/headersInspectionHandler.ts 184 | -------------------------------------------------------------------------------- /Repositories.md: -------------------------------------------------------------------------------- 1 | # Repositories 2 | 3 | With the Kiota onboarding, our code will be using the following four solutions: 4 | 5 | - Kiota generator 6 | - Kiota core libraries 7 | - Graph core library which will be depending on the Kiota core. 8 | - Graph service library containing Kiota generated request builders. 9 | 10 | The proposed plan to set the GitHub repositories for the above solutions is as follows : 11 | 12 | - Kiota (the generator) - common for all languages 13 | - kiota- : contains the abstractions and core implementation packages 14 | - msgraph-sdk--core 15 | - msgraph-sdk- 16 | - msgraph-beta-sdk- 17 | - (optional) msgraph-china-sdk-: an advantage of Kiota based generation is we can deliver SDKs tailored to customers' environments, this includes the ability to ship one service library per national cloud per version as national clouds generally have access to fewer endpoints. 18 | - (optional) msgraph-beta-china-sdk- 19 | 20 | Concerns to be discussed more: 21 | 22 | 1. Duplicate names - Consider adding new repositories for all the Kiota generated libraries and discontinuing the repos that use the current code generation process. Also consider renaming the older repository. 23 | In this case, we will need to think of duplicate repository names, that is, the old repo and the new repo cannot be named as `msgraph-sdk-`. 24 | 2. JavaScript - The current core library is named as `msgraph-sdk-javascript`. 25 | 26 | 27 | -------------------------------------------------------------------------------- /ResponseHandler.md: -------------------------------------------------------------------------------- 1 | # Response Handler 2 | 3 | Implement default behaviour for Graph calls. These can be used either by generated Request classes or called directly by developers using native libraries. 4 | 5 | ## Requirements 6 | 7 | - Provide the ability to take a response payload from a successful request and deserialize it into a strong type. 8 | - Successful requests that MAY return a response payload MUST also support an empty response body. 9 | - Provide the ability to take a response payload from a failed request and deserialize it into a ErrorContent object. 10 | - Responses with 4XX or 5XX should attempt to translate response body into ErrorContent, otherwise a default ErrorContent should be created. 11 | - Calls to the native Http library should not throw ServiceExceptions. 12 | - Service Library calls SHOULD throw an exception with a ErrorContent object. Ideally a different exception should be thrown based on a) the call could not be made b) the server returned an error. See [ErrorContent Requirements](./ErrorContent.md#requirements) for recommended exception names. 13 | - For responses with an ErrorContent payload, attempt to update the ErrorContent with X-ThrowSite header if the the header exists and the ErrorContent does not have a payload. 14 | 15 | ## Example Usage 16 | 17 | ```CSharp 18 | 19 | var responseHandler = new ResponseHandler(new Serializer()); 20 | 21 | var response = await httpClient.SendAsync(request); 22 | 23 | if (response.IsSuccessCode()) { 24 | User user = responseHandler.HandleSuccessfulResponse(response); 25 | return user; 26 | } else { 27 | ErrorContent error = responseHandler.HandleErrorResponse(response); 28 | throw new ServiceException(error); 29 | } 30 | ``` 31 | 32 | ## Discussion 33 | 34 | All response handlers MUST implement the HTTP specification correctly. Specifically this portion of the specification 35 | 36 | > HTTP status codes are extensible. HTTP clients are not required to 37 | understand the meaning of all registered status codes, though such 38 | understanding is obviously desirable. However, **a client MUST 39 | understand the class of any status code, as indicated by the first 40 | digit, and treat an unrecognized status code as being equivalent to 41 | the x00 status code of that class**, with the exception that a 42 | recipient MUST NOT cache a response with an unrecognized status code. 43 | 44 | https://datatracker.ietf.org/doc/html/rfc7231#section-6 45 | 46 | A HTTP request MUST not fail with an exception specifically because it received an HTTP status code that is was not expecting. 47 | 48 | ### Content vs No Content 49 | 50 | Response handlers cannot assume that a request that normally returns a response body, that can be deserialized into an entity, will always return a response body. The response may be empty. As an empty body is not a valid JSON payload it will need to be handled explicitly. 51 | 52 | For successful responses, only requests that use HEAD, or return 204/205 can a client assume the response body will be empty. All other responses may, or may not have a non-zero length body. The presence of a body in a response can be detected by the existence of a content-length with a non-zero value or a Transfer-Encoding header including the term chunked and a payload with a non-zero value in the first line. Most language native client libraries abstract away chunk-encoding by default and therefore content-length is usually sufficient to detect a body. The presence of Content-Type should not be used as an indicator of a body. 53 | 54 | Examples of where the presence of a body is ambiguous: 55 | 56 | | Method | Status Code | Notes | 57 | |--|--|--| 58 | |POST | 200 | According to RFC 7231, the body contains the results or status of the action. In theory, this could be the created resource representation. However, it is debatable whether it would be better to return a 201 with the actual response body. | 59 | |POST | 201 | According to RFC 7231, if this returns a body, it should contain a link and information about newly created object, not the object itself. Some APIs do return the created object. | 60 | |POST | 204 | This MUST not return a body. | 61 | |PATCH | 200 | According to RFC 7231, this should return the updated body. In practice, that isn't always the case.| 62 | |PATCH | 201 | Same as POST. | 63 | |PATCH | 204 | This MUST not return a body. | 64 | |PUT | 200 | According to RFC 7231, responses to PUTs are not cacheable, indicating that it is not expected to return the response body. However, returning the updated response does not seem to be prohibited. | 65 | |PUT | 201 | Same rules as POST. | 66 | |PUT | 204 | This MUST not return a body. | 67 | |DELETE | 200 | The response body, if present should describe the status of the operation. In theory this could be a representation of the resource that was deleted. | 68 | |DELETE | 204 | This MUST not return a body. | 69 | |GET | 200 | This MUST return a body that represents the identified resource. If a representation doesn't exist, then the response SHOULD be a 404. | 70 | |GET | 202 | A 202 may be used if the GET request will take a long time. In this case the body might be empty. | 71 | 72 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /Versions.md: -------------------------------------------------------------------------------- 1 | # Versions 2 | 3 | The Microsoft Graph SDKs MUST follow [Semantic Versioning 2.0.0](https://semver.org/). This is important for capturing information about the use of each SDK via the SdkVersion header emitted by our libraries. 4 | 5 | Changes made to the metadata and our generation tools that break a signature MUST result in a major version change. 6 | 7 | Changes made to the underlying API behavior of the service API that are not intentional should be considered a service bug and not result in a major version bump. Ideally we block the change from being propagated to the generated service libraries and the service API gets fixed. 8 | 9 | ## Version format per language 10 | 11 | Unless otherwise specified, the SDK library and packages should use the following format for specifying preview versions: `x.y.z-preview.n`. An example of this is `1.3.0-preview.2`. The first preview release should be labeled like `1.3.0-preview`. Subsequent iterations of a preview can be suffixed with the preview iteration like this: `1.3.0-preview.1`. 12 | 13 | Service libraries for the beta endpoints will not increment the pre-release version when new releases are made. We will typically increment the minor version across new beta service libraries `4.3.0-preview` --> `4.4.0-preview`. 14 | 15 | Prelease identifiers like `preview`, `beta`, and `rc` should be lower case. 16 | 17 | ### PHP version format 18 | 19 | Composer has a [more restrictive format](https://getcomposer.org/doc/articles/versions.md#stabilities) so we should use `x.y.z-rcn` for release candidates and `x.y.z-beta` for service libraries based on the beta endpoint. The first release candidate (preview) release should look like `2.6.0-rc`. Subsequent iterations of a preview can be suffixed with the preview iteration like this: `2.6.0-rc1`. 20 | 21 | ### Golang version format 22 | 23 | [Golang standards](https://golang.org/doc/modules/version-numbers) specify that the version should use `vx.y.z` for release versions and `vx.y.z-beta.n` for pre-release versions. For example, a production release version may look like `v1.3.4`, and release candidate may look like `v1.3.4-beta`, with updates to the release candidate looking like `v1.3.4-beta.1`. -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | 6 | .main-content { 7 | padding-right:1rem; 8 | padding-left:1rem; 9 | } -------------------------------------------------------------------------------- /content/BatchRequestContent.md: -------------------------------------------------------------------------------- 1 | # BatchRequestContent 2 | 3 | ## Objectives 4 | 5 | Provide a class that makes it simple to create the payload for a Graph Batch request. 6 | 7 | ## Requirements 8 | 9 | See [constraints](ContentArchitecturalConstraints.md) related to all content objects. 10 | 11 | - It MUST be possible to add native platform HTTP request objects to a BatchRequestContent object. The request URL MUST be a relative URL. Adding Graph specific Request objects MAY be supported. 12 | - Added requests MUST be assigned unique identifiers. Those identifiers MAY be user provided or autogenerated. 13 | - It MUST be possible to create dependencies between request objects. 14 | - The serialized representation, written to a stream, or returned as a byte array MUST be a JSON object that conforms to the format described in the [Graph Documentation](1) and [OData specification](2). Request bodies must be base64 encoded. 15 | - It MUST be possible to use a dictionary of existing unique identifiers and their corresponding to response status code to create a new/fresh `BatchRequestContent` instance to unlock re-emitting/retrying failed requests from an existing `BatchRequestContent` instance. 16 | 17 | ## Performance Considerations 18 | 19 | A batch cannot hold more that 20 requests and requests cannot contain a batch request payload. 20 | 21 | ## Security Considerations 22 | 23 | Requests that require additional consent, not already obtained, should not be included in a batch. 24 | 25 | ## Usage Examples 26 | 27 | ```cs 28 | // create the batch request 29 | var batchRequestContent = new BatchRequestContent(graphServiceClient); 30 | var requestInformation = graphServiceClient.Users.ToGetRequestInformation(); 31 | 32 | // add step using requestInformation 33 | var requestStepId = await batchRequestContent.AddBatchRequestStepAsync(requestInformation); 34 | 35 | // add step using native platform HTTP request 36 | var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); 37 | var secondRequestStepId = batchRequestContent.AddBatchRequestStep(httpRequestMessage); 38 | 39 | // send and get back response 40 | var batchResponseContent = await graphServiceClient.Batch.PostAsync(batchRequestContent); 41 | 42 | // enumerate list of response status codes 43 | var statusCodes = await batchResponseContent.GetResponsesStatusCodesAsync(); 44 | // filter the requests to retry 45 | var rateLimitedResponses = statusCodes.Where(x => x.Value == HttpStatusCode.TooManyRequests).ToDictionary(x => x.Key,y => y.Value); 46 | if (rateLimitedResponses.Any()) 47 | { 48 | var retryBatch = batchRequestContent.NewBatchWithFailedRequests(rateLimitedResponses); 49 | // send and get back response 50 | var retryBatchResponseContent = await graphServiceClient.Batch.PostAsync(retryBatch); 51 | } 52 | ``` 53 | 54 | ## References 55 | 56 | 1. [Graph Documentation]( https://learn.microsoft.com/graph/json-batching) 57 | 1. [OData](https://www.oasis-open.org/committees/download.php/60365/odata-json-format-v4.01-wd02-2017-03-24.docx) 58 | -------------------------------------------------------------------------------- /content/BatchRequestContentCollection.md: -------------------------------------------------------------------------------- 1 | # BatchRequestContentCollection 2 | 3 | ## Objectives 4 | 5 | Provide a class that does not have limitation of that 20 requests by creating new `BatchRequestContent` objects to hold new requests to simplify the batching for advanced scenarios. 6 | The `BatchRequestContentCollection` is essentially a collection wrapper for `BatchRequestContent` and is not expected to be serializable as the individual `BatchRequestContent` instance are sent out independently and will receive independent responses. 7 | 8 | ## Requirements 9 | 10 | Similar to [BatchRequestContent](BatchRequestContent.md), it must support the below functionality while holding a collection of BatchRequestContent instances. 11 | 12 | - It MUST be possible to add native platform HTTP request objects to a BatchRequestContent object. The request URL MUST be a relative URL. Adding Graph specific Request objects MAY be supported. 13 | - Added requests MUST be assigned unique identifiers. Those identifiers MAY be user provided or autogenerated. The uniqueness SHOULD be enforced/validated across the same `BatchRequestContent` instance the request objects are held in AND across the `BatchRequestContentCollection` instance. 14 | - It MUST be possible to create dependencies between request objects. However, these dependencies SHOULD be enforced/validated across the same `BatchRequestContent` instance the request objects are held in and NOT across `BatchRequestContent` instances in the `BatchRequestContentCollection`. 15 | - The addition of a new request object with a dependency, SHOULD make the best effort at placing the object in the same `BatchRequestContent` instance as its dependency and throw an error in the event this is not possible. 16 | - It MUST be possible to configure the maximum number of request objects per `BatchRequestContent` in the `BatchRequestContentCollection` to a value lower than the default value of 20 request objects. This is to enable customized scenarios for developers and provide a means of client side optimization/throttling for workloads that may have lower maximums in batched requests. 17 | - It MUST possible to use a dictionary of existing unique identifiers and their corresponding to response status code to create a new/fresh `BatchRequestContentCollection` instance to unlock re-emitting/retrying failed requests from an existing `BatchRequestContentCollection` instance. 18 | 19 | > Given the similar API experience between `BatchRequestContentCollection` and `BatchRequestContent`, public documentation(code comments and examples) SHOULD encourage the use and point to `BatchRequestContentCollection` to avoid confusion for the end user. Adding new requests should automatically generate new `BatchRequestContent` instances to the `BatchRequestContentCollection`. `BatchRequestContent` should be a public facing type to enable users with customized batching scenarios. 20 | 21 | ## Performance Considerations 22 | 23 | To minimize the risk of clients getting throttled by the API, the request client SHOULD send `BatchRequestContent` instances in sequence if the `BatchRequestContentCollection` contains more than one `BatchRequestContent` instance by default. The request client MAY send `BatchRequestContent` instances in parallel by use of a configuration parameter to allow the user to opt-in to this behavior in the event that the user is aware of the potential throttling limits. 24 | 25 | ## Usage Examples 26 | 27 | ```cs 28 | // create the batch request content collection 29 | var batchRequestContentCollection = new BatchRequestContentCollection(graphServiceClient); 30 | var requestInformation = graphServiceClient.Users.ToGetRequestInformation(); 31 | 32 | // add step using requestInformation 33 | var requestStepId = await batchRequestContentCollection.AddBatchRequestStepAsync(requestInformation); 34 | 35 | // add step using native platform HTTP request 36 | var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); 37 | var secondRequestStepId = batchRequestContentCollection.AddBatchRequestStep(httpRequestMessage); 38 | 39 | for (var i = 0; i < 25; i++) // its possible to add more than 20 requests 40 | { 41 | BatchRequestStep batchRequestStep = new BatchRequestStep(i.ToString(), new HttpRequestMessage(HttpMethod.Get, REQUEST_URL)); 42 | batchRequestContentCollection.AddBatchRequestStep(batchRequestStep); 43 | } 44 | 45 | // send and get back response 46 | var batchResponseContentCollection = await graphServiceClient.Batch.PostAsync(batchRequestContentCollection); 47 | 48 | // enumerate list of response status codes 49 | var statusCodes = await batchResponseContentCollection.GetResponsesStatusCodesAsync(); 50 | // filter the requests to retry 51 | var rateLimitedResponses = statusCodes.Where(x => x.Value == HttpStatusCode.TooManyRequests).ToDictionary(x => x.Key, y => y.Value); 52 | if (rateLimitedResponses.Any()) 53 | { 54 | var retryBatch = batchRequestContentCollection.NewBatchWithFailedRequests(rateLimitedResponses); 55 | // send and get back response 56 | var retryBatchResponseContentCollection = await graphServiceClient.Batch.PostAsync(retryBatch); 57 | } 58 | ``` 59 | 60 | ## References 61 | 62 | 1. [Graph Documentation]( https://learn.microsoft.com/graph/json-batching) 63 | 1. [OData](https://www.oasis-open.org/committees/download.php/60365/odata-json-format-v4.01-wd02-2017-03-24.docx) 64 | -------------------------------------------------------------------------------- /content/BatchResponseContent.md: -------------------------------------------------------------------------------- 1 | # BatchResponseContent 2 | 3 | ## Objectives 4 | 5 | Provide component to simplify the processing of batch responses. 6 | 7 | ## Requirements 8 | 9 | See [constraints](ContentArchitecturalConstraints.md) related to all content objects. 10 | 11 | - Deserialize batch response payload into a collection of GraphResponse objects 12 | - Allow users to enumerate collection of GraphResponse objects 13 | - Allow users to retrieve a specific response object by using the same unique identifiers used in making the request. 14 | - Allow users to retrieve a specific deserialized response content value by using the same unique identifiers used in making the request. 15 | - Allow users to retrieve a specific stream response content value by using the same unique identifiers used in making the request. 16 | - Allow users to retrieve a dictionary of the unique identifiers used in making the request corresponding to response status code to enable the re-emitting/retrying of failed individual requests. 17 | 18 | ## Performance Considerations 19 | 20 | ## Security Considerations 21 | 22 | ## Usage Examples 23 | 24 | ```cs 25 | var batchRequestContent = new BatchRequestContent(graphServiceClient); 26 | 27 | // add step 1 using requestInformation 28 | var requestInformation = graphServiceClient.Users.ToGetRequestInformation(); 29 | var requestId = await batchRequestContent.AddBatchRequestStepAsync(requestInformation); 30 | 31 | // add step 2 using requestInformation 32 | var eventsRequestInformation = graphServiceClient.Me.Events.ToGetRequestInformation(); 33 | var eventsRequestId = await batchRequestContent.AddBatchRequestStepAsync(eventsRequestInformation); 34 | 35 | // add step 2 using requestInformation 36 | var imageRequestInformation = graphServiceClient.Me.Photo.Content.ToGetRequestInformation(); 37 | var imageRequestId = await batchRequestContent.AddBatchRequestStepAsync(eventsRequestInformation); 38 | 39 | // send and get back response 40 | var batchResponseContent = await graphServiceClient.Batch.PostAsync(batchRequestContent); 41 | 42 | // get back specific native response message 43 | HttpResponseMessage responseMessage = await batchResponseContent.GetResponseByIdAsync(requestId); 44 | 45 | // get back specific response using identifier and deserialize it 46 | EventCollectionResponse events = await batchResponseContent.GetResponseByIdAsync(eventsRequestId); 47 | 48 | // get back specific response using identifier and get the stream 49 | Stream imageStream = await batchResponseContent.GetResponseStreamByIdAsync(imageRequestId); 50 | 51 | // enumerate list of response status codes 52 | var statusCodes = await batchResponseContent.GetResponsesStatusCodesAsync(); 53 | // filter the requests to retry 54 | var rateLimitedResponses = statusCodes.Where(x => x.Value == HttpStatusCode.TooManyRequests).ToDictionary(x => x.Key, y => y.Value); 55 | if (rateLimitedResponses.Any()) 56 | { 57 | var retryBatch = batchRequestContent.NewBatchWithFailedRequests(rateLimitedResponses); 58 | // send and get back response 59 | var retryBatchResponseContent = await graphServiceClient.Batch.PostAsync(retryBatch); 60 | } 61 | ``` 62 | 63 | ## References 64 | 65 | 1. [Graph Documentation]( https://learn.microsoft.com/graph/json-batching) 66 | 1. [OData](https://www.oasis-open.org/committees/download.php/60365/odata-json-format-v4.01-wd02-2017-03-24.docx) 67 | -------------------------------------------------------------------------------- /content/BatchResponseContentCollection.md: -------------------------------------------------------------------------------- 1 | # BatchResponseContentCollection 2 | 3 | ## Objectives 4 | 5 | Provide component to simplify the processing of multiple batch responses made via `BatchRequestContentCollection`. 6 | 7 | ## Requirements 8 | 9 | See [constraints](ContentArchitecturalConstraints.md) related to all content objects. 10 | 11 | - Hold multiple batch response objects (i.e. `BatchResponseContent`) made from a collection of batch request objects(i.e. `BatchRequestContentCollection`) 12 | - Allow users to enumerate collection of individual response objects. 13 | - Allow users to retrieve a specific response object by using the SAME unique identifiers used in making the request (the unique identifiers will be unique across the `BatchResponseContentCollection` as they were unique across the `BatchRequestContentCollection` when making the request). 14 | - Allow users to retrieve a specific deserialized response content value by using the same unique identifiers used in making the request. 15 | - Allow users to retrieve a specific stream response content value by using the same unique identifiers used in making the request. 16 | - Allow users to retrieve a dictionary of the unique identifiers used in making the request corresponding to response status code to enable the re-emitting/retrying of failed individual requests. 17 | 18 | ## Performance Considerations 19 | 20 | ## Security Considerations 21 | 22 | ## Usage Examples 23 | 24 | ```cs 25 | var batchRequestContentCollection = new BatchRequestContentCollection(graphServiceClient); 26 | 27 | // add step 1 using requestInformation 28 | var requestInformation = graphServiceClient.Users.ToGetRequestInformation(); 29 | var requestId = await batchRequestContentCollection.AddBatchRequestStepAsync(requestInformation); 30 | 31 | // add step 2 using requestInformation 32 | var eventsRequestInformation = graphServiceClient.Me.Events.ToGetRequestInformation(); 33 | var eventsRequestId = await batchRequestContentCollection.AddBatchRequestStepAsync(eventsRequestInformation); 34 | 35 | // add step 2 using requestInformation 36 | var imageRequestInformation = graphServiceClient.Me.Photo.Content.ToGetRequestInformation(); 37 | var imageRequestId = await batchRequestContentCollection.AddBatchRequestStepAsync(eventsRequestInformation); 38 | 39 | // send and get back response 40 | var batchResponseContentCollection = await graphServiceClient.Batch.PostAsync(batchRequestContentCollection); 41 | 42 | // get back specific native response message 43 | HttpResponseMessage responseMessage = await batchResponseContentCollection.GetResponseByIdAsync(requestId); 44 | 45 | // get back specific response using identifier and deserialize it 46 | EventCollectionResponse events = await batchResponseContentCollection.GetResponseByIdAsync(eventsRequestId); 47 | 48 | // get back specific response using identifier and get the stream 49 | Stream imageStream = await batchResponseContentCollection.GetResponseStreamByIdAsync(imageRequestId); 50 | 51 | // enumerate list of response status codes 52 | var statusCodes = await batchResponseContentCollection.GetResponsesStatusCodesAsync(); 53 | // filter the requests to retry 54 | var rateLimitedResponses = statusCodes.Where(x => x.Value == HttpStatusCode.TooManyRequests).ToDictionary(x => x.Key, y => y.Value); 55 | if (rateLimitedResponses.Any()) 56 | { 57 | var retryBatch = batchResponseContentCollection.NewBatchWithFailedRequests(rateLimitedResponses); 58 | // send and get back response 59 | var retryBatchResponseContent = await graphServiceClient.Batch.PostAsync(retryBatch); 60 | } 61 | 62 | ``` 63 | 64 | ## References 65 | 66 | 1. [Graph Documentation]( https://learn.microsoft.com/graph/json-batching) 67 | 1. [OData](https://www.oasis-open.org/committees/download.php/60365/odata-json-format-v4.01-wd02-2017-03-24.docx) 68 | -------------------------------------------------------------------------------- /content/ContentArchitecturalConstraints.md: -------------------------------------------------------------------------------- 1 | # Graph Content Classes 2 | 3 | ## Objectives 4 | 5 | To create classes that encapsulate standardized patterns that are used to define HTTP request and response payloads across the Microsoft Graph. 6 | 7 | ## Requirements 8 | 9 | - Content classes MUST allow serializing to a provided stream if streams exists on the platform. 10 | - Content classes MUST be able to generate wire content as byte array. 11 | - The behavior of content classes MUST be independent of the request URL. 12 | - Content classes MAY provide ability to update request headers with content related headers. 13 | 14 | ## Performance Considerations 15 | 16 | Being able to serialize to a stream is important for several scenarios: 17 | 18 | - allows writing large content that will be sent using chunk encoding 19 | - elimnating the double memory usage where request payload is transferred into client side buffer before sending 20 | - recreating request bodies when retrying or redirecting 21 | 22 | ## Security Considerations 23 | 24 | There are no security issues specific to content classes. -------------------------------------------------------------------------------- /content/ErrorContent.md: -------------------------------------------------------------------------------- 1 | # Error Content 2 | 3 | ## Objectives 4 | 5 | - Provide access to error information included in the response body of a 4XX or 5XX response. 6 | - Define standard class names for error scenarios. 7 | 8 | ## Requirements 9 | 10 | - Match the specification as defined by the [Microsoft API Guidelines](https://github.com/microsoft/api-guidelines/blob/07a1e42e3e33f94df782abfcbaec24b63968c120/Guidelines.md#5-taxonomy) for errors returned in API responses. 11 | - Service errors SHOULD be defined in a class named GraphServiceException or GraphServiceError depending on the platform. 12 | - Service errors SHOULD contain the response status code and response headers. 13 | - Client errors SHOULD be defined in a class named GraphClientException or GraphClientError depending on the platform. 14 | - Client and service errors should use common properties according to the Microsoft API Guidelines. 15 | 16 | GraphServiceException or GraphServiceError MUST support all potential OData error properties. See [Microsoft Graph error JSON](https://docs.microsoft.com/en-us/graph/errors#json-representation) for more information. The following is an example GraphServiceException response payload. 17 | 18 | ```json 19 | { 20 | "error": { 21 | "code": "BadRequest", 22 | "message": "Resource not found for the segment 'mef'.", 23 | "innerError": { 24 | "date": "2021-07-02T01:40:19", 25 | "request-id": "1a0ffbc0-086f-4e8f-93f9-bf99881c65f6", 26 | "client-request-id": "225aed2b-cf4a-d456-b313-16ab196c2364" 27 | } 28 | } 29 | } 30 | ``` 31 | 32 | ## Reference service error implementations 33 | 34 | - JavaScript - [GraphError](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/GraphError.ts) 35 | - .NET - [ServiceException](https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/feature/2.0/src/Microsoft.Graph.Core/Exceptions/ServiceException.cs) 36 | - Java - [GraphServiceException](https://github.com/microsoftgraph/msgraph-sdk-java-core/blob/dev/src/main/java/com/microsoft/graph/http/GraphServiceException.java) 37 | 38 | ## Reference client error implementations 39 | 40 | - JavaScript - [GraphClientError](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/GraphClientError.ts) 41 | - .NET - [ClientException](https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/blob/dev/src/Microsoft.Graph.Core/Exceptions/ClientException.cs), everything had been a ServiceException. 42 | - Java - [ClientException](https://github.com/microsoftgraph/msgraph-sdk-java-core/blob/dev/src/main/java/com/microsoft/graph/core/ClientException.java) 43 | -------------------------------------------------------------------------------- /content/MultipartContent.md: -------------------------------------------------------------------------------- 1 | # Multipart Content 2 | 3 | ## Objectives 4 | 5 | - Provide support for request and response payloads using multipart formats. These include `multipart/form-data`, `multipart/mixed` and `multipart/alternative`. 6 | 7 | ## Requirements 8 | 9 | See [constraints](ContentArchitecturalConstraints.md) related to all content objects. 10 | 11 | - Allow adding a part containing a set of HTTP headers and array of bytes. 12 | - Provide helper for adding form-data part with name, content-type and byte array. 13 | - Provide helper for adding part with filename, content-type and byte-array. 14 | - Allow user to specific content type of multi-part payload 15 | - Allow user to override default boundary 16 | - Serialize all headers and boundaries using US-ASCII charset. 17 | 18 | ## Performance Considerations 19 | 20 | ## Security Considerations 21 | 22 | -------------------------------------------------------------------------------- /content/StreamingRequestContent.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-design/a9f5bd8eb020ae99bd186c286bae5dee038262e7/content/StreamingRequestContent.md -------------------------------------------------------------------------------- /content/StreamingResponseContent.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-design/a9f5bd8eb020ae99bd186c286bae5dee038262e7/content/StreamingResponseContent.md -------------------------------------------------------------------------------- /images/componentArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-design/a9f5bd8eb020ae99bd186c286bae5dee038262e7/images/componentArch.png -------------------------------------------------------------------------------- /middleware/AuthorizationHandler.md: -------------------------------------------------------------------------------- 1 | # Authorization Handler 2 | 3 | ## Objectives 4 | 5 | A piece of client-side middleware intended to allow the use of an authorization provider service as part of the middleware pipeline. 6 | 7 | ## Requirements 8 | 9 | - Transparently authorize requests 10 | - Accept AuthorizationProvider instance in constructor 11 | - Update Authorization Header 12 | - Certain workloads error out when an unexpected header is present in the request. The middlewares should check: 13 | - If the request URL is a Graph serve endpoint or a custom url provided by the developer, then append or update the authorization header. 14 | - Else the middleware should delete authorization header. 15 | 16 | Take a request object and use authorization provider to add Authorization header. 17 | 18 | ### Additional requirements for Kiota clients 19 | 20 | - MUST NOT attempt to update the authorization header if it is already present (unless we ran into an authentication challenge). This is to ensure customers can use the same http client for arbitrary requests and with a generated client/request adapter without running into double authentication issues. 21 | - MUST accept a [BaseBearerAuthenticationProvider](https://github.com/microsoft/kiota-dotnet/blob/main/src/abstractions/authentication/BaseBearerTokenAuthenticationProvider.cs) and use the associated AccessTokenProvider to obtain the access token. This is to ensure the client initialization remains simple. 22 | - MUST be implemented in the kiota http layer. This ensures maximum reusability without mixing concerns with the authentication implementations. 23 | - MUST rely on the AllowedHostValidator provided by the access token provider to determine whether or not to perform authorization on a given request. This is to ensure configuration remains consistent and simple, as well as to ensure bearer tokens are not sent to untrusted hosts. 24 | - MUST provide an additional HTTP client factory which accepts a BaseBearerAuthenticationProvider and will setup the default middleware handlers in addition to the authorization one. 25 | - Additional observability requirements in [Observability](../Observability.md) 26 | 27 | ### 401 Handling 28 | 29 | Trap 401 responses and pass information in the `www-authenticate` header to help generate a valid authorization token and re-issue the request. 30 | 31 | ## Performance Considerations 32 | 33 | ## Security Considerations 34 | 35 | ## Examples 36 | 37 | ### C# 38 | 39 | ```csharp 40 | var deviceCodeCredentials = new DeviceCodeCredentials(); 41 | // simplest case MS Graph pre-packaged SDK 42 | var client = new GraphServiceClient(deviceCodeCredentials); 43 | var me = await client.Me.GetAsync(); 44 | // self-served client 45 | var authenticationProvider = new AzureIdentityAuthenticationProvider(deviceCodeCredentials); 46 | var requestAdapter = new HttpRequestAdapter(authenticationProvider); 47 | var client = new MyCustomGraphClient(requestAdapter); 48 | var me = await client.Me.GetAsync(); 49 | var httpClient = KiotaClientFactory.GetHttpClient(authenticationProvider); //this method is the http package 50 | //... make an arbitrary request 51 | ``` 52 | -------------------------------------------------------------------------------- /middleware/BodyInspectionHandler.md: -------------------------------------------------------------------------------- 1 | # Body Inspection Handler 2 | 3 | A middleware handler that allows the client application to inspect request and response bodies. 4 | 5 | ## Requirements 6 | 7 | - MUST export the request and response bodies on the original option value. 8 | - MUST make in memory copies of the streams and rewind the original streams to avoid exhausting payloads before they are sent/parsed. 9 | - MUST rewind the stream copies so the application does not need to. 10 | - Provide an option to enable request body inspection. (default disabled) 11 | - Provide an option to enable response body inspection. (default disabled) 12 | - MUST make clear in documentation comments that callers are responsible for disposing the stream. 13 | - MUST make clear in documentation comments copies are being made, which will lead to memory pressure on the application, use adequately. 14 | - Additional observability requirements in [Observability](../Observability.md) 15 | 16 | ## Example 17 | 18 | ### CSharp 19 | 20 | ```CSharp 21 | 22 | var bodyInspectionOption = new BodyInspectionHandlerOption { 23 | InspectResponseBody = true 24 | }; 25 | var result = await client.Me.GetAsync(c => c.Options.Add(bodyInspectionOption)); 26 | using var reader = new StringReader(bodyInspectionOption.ResponseBody, Encoding.UTF8); 27 | Console.WriteLine("The response content is {content}", await reader.ReadToEndAsync()); 28 | ``` 29 | -------------------------------------------------------------------------------- /middleware/ChaosHandler.md: -------------------------------------------------------------------------------- 1 | # Chaos Handler 2 | 3 | A piece of middleware that enables developers to inject server failures that are difficult to trigger. The goal of this Chaos Handler is to allow a developer to test their application code with random errors injected into their application so that they can see whether the application handles server generated failures. Additionally, a developer can explicitly control the generated failures to assist with reproducing the failure and aid in fixing the application behaviour under failure conditions. 4 | 5 | ## Objectives 6 | 7 | Provide a configurable testing handler component that, by default, generates random error responses, and can be configured to generate tester specified errors. 8 | 9 | ## Requirements 10 | 11 | * MUST support request/response pass-through for non-intercepted responses. 12 | * The rate of error responses returned MUST be configurable with a value that represents the percentage of responses intercepted and replaced with a random error response. 13 | * MUST NOT be a default handler in the midleware chain. 14 | * It SHOULD be easily added to the default middleware chain without affecting the expected middleware functionality. 15 | * It SHOULD be added as the handler immediately before the final handler. 16 | * The default Chaos Handler SHOULD be in random mode by default 17 | * Should be included in middleware using Custom Middleware Chain. 18 | * Should support Manual Mode of operation, where application developer can specify what responses they want for a provided request. 19 | * Manual Mode should be supported at Client Level/ Global Level where they declare a Map and pass this to ChaosHandler. 20 | * This manually declared Map should work on exact matching as well as Regex Matching (Eg - "/me/messages/.*"). 21 | * Manual Mode should be supported at Request Level as well, where they can override the response for a particular request. 22 | * Should support Random mode where application developers receive Random responses on the basis of API method from the list of predefined response code for each method. 23 | 24 | ### Random Mode Response Codes List 25 | 26 | | API Method | Status Code List | 27 | |:----------:|:----------------------------:| 28 | | GET | 429, 500, 502, 503, 504 | 29 | | POST | 429, 500, 502, 503, 504, 507 | 30 | | PUT | 429, 500, 502, 503, 504, 507 | 31 | | PATCH | 429, 500, 502, 503, 504 | 32 | | DELETE | 429, 500, 502, 503, 504, 507 | 33 | 34 | ## Future Potential Requirements 35 | 36 | * Support for custom response bodies. 37 | * Conditional responses based on URL and/or header pattern matching. 38 | * Support for having a default Middleware chain, which can be used as Custom Middleware chain by doing some manipulations. 39 | -------------------------------------------------------------------------------- /middleware/CompressionHandler.md: -------------------------------------------------------------------------------- 1 | # Compression Handler 2 | 3 | A middleware component that compresses request bodies and falls back to an uncompressed body if the API doesn't support request body compression. 4 | 5 | ## Requirements 6 | 7 | - Add `Content-encoding: gzip` to request headers for requests with a body. 8 | - Compresses request bodies. 9 | - Should the handler receive a [415 Unsupported Media Type](https://httpwg.org/specs/rfc7231.html#status.415) response, the handler will retry sending the request without compressing it a maximum of one time before passing on the error. 10 | - Compression handler should be installed as a default handler by [GraphClientFactory](../GraphClientFactory.md) 11 | - The compression handler should accept a `CompressionOptions` object with a property `EnableCompression`. That object can be passed when instantiating the handler, if nothing is passed a default value is created with `EnableCompression: true`. That object can also be passed on a per request base from the fluent-style API by the SDK used. If a value is passed for the request, it takes precedence over the value that was set with the handler. Compression is only performed when the computed value of `EnableCompression` is `true`. 12 | - MUST NOT compress request bodies when a Content-Range header is present with a "bytes" range unit to avoid "corrupting" the set of requests in the range. (e.g. Content-Range: bytes 0-9/20) 13 | - MUST NOT compress request bodies when a Content-Encoding header is already present to avoid double compression. 14 | - MUST NOT attempt to send the original uncompressed payloads if the response status code is `415` and the `Content-Encoding` request header was already present outside of the middleware handler. 15 | - Additional observability requirements in [Observability](../Observability.md) 16 | 17 | ## Remarks 18 | 19 | For response payload decompression, refer to the [decompression handler](./DecompressionHandler.md) specification. 20 | 21 | This handler MUST be inserted BEFORE the [Retry handler](./RetryHanlder.md) to avoid compressing the request payload multiple times over. 22 | 23 | This handler is part of the Graph handlers collection and not the Kiota handler collection because of how it handles bad requests being specific to Microsoft Graph APIs. 24 | 25 | Microsoft Graph environments which do not support request body compression yet return a 400 response code instead of 415. 26 | 27 | ## Example 28 | 29 | ### HTTP request with compressed body 30 | 31 | ```http 32 | POST https://graph.microsoft.com/v1.0/me/microsoft.graph.sendMail HTTP/1.1 33 | Host: graph.microsoft.com 34 | SdkVersion: Graph-dotnet-2.0.5 35 | FeatureFlag: 00000047 36 | Cache-Control: no-store, no-cache 37 | Authorization: Bearer {token} 38 | Accept-Encoding: gzip 39 | Transfer-Encoding: chunked 40 | Content-Type: application/json 41 | Content-Encoding: gzip 42 | …compressed body…. 43 | ``` 44 | 45 | ### HTTP response from Microsoft Graph when request compression is not supported 46 | 47 | ```http 48 | HTTP/1.1 400 Bad Request 49 | 50 | Date: Thu, 09 Dec 2021 03:37:07 GMT 51 | 52 | Content-Type: application/json 53 | 54 | Vary: Accept-Encoding 55 | 56 | Strict-Transport-Security: max-age=31536000 57 | 58 | request-id: 38b4ee5c-1168-4281-863b-920860a4eebe 59 | 60 | client-request-id: 38b4ee5c-1168-4281-863b-920860a4eebe 61 | 62 | x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"Canada East","Slice":"E","Ring":"2","ScaleUnit":"001","RoleInstance":"QB1PEPF0000111E"}} 63 | 64 | Content-Length: 313 65 | 66 | 67 | 68 | {"error":{"code":"BadRequest","message":"Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format.","innerError":{"date":"2021-12-09T03:37:08","request-id":"38b4ee5c-1168-4281-863b-920860a4eebe","client-request-id":"38b4ee5c-1168-4281-863b-920860a4eebe"}}} 69 | ``` 70 | -------------------------------------------------------------------------------- /middleware/ConnectionPoolManager.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-design/a9f5bd8eb020ae99bd186c286bae5dee038262e7/middleware/ConnectionPoolManager.md -------------------------------------------------------------------------------- /middleware/DecompressionHandler.md: -------------------------------------------------------------------------------- 1 | # Decompression Handler 2 | 3 | A middleware component that requests, detects and decompresses response bodies. 4 | 5 | ## Requirements 6 | 7 | - Add `Accept-encoding: gzip` to request headers. 8 | - Decompress response bodies that have the header `Content-Encoding: gzip` 9 | - Compression handler should be installed as a default handler by [GraphClientFactory](../GraphClientFactory.md) 10 | - Additional observability requirements in [Observability](../Observability.md) 11 | 12 | ## Remarks 13 | 14 | For request payload compression, refer to the [compression handler](./CompressionHandler.md) specification. 15 | 16 | ## State of automatic decompression 17 | 18 | The following table describes the state of automatic decompression of responses. When automatic decompression is supported, we don't need to implement it manually. 19 | 20 | | Language | Client | Automatic decompression | Source | Notes | 21 | | -------- | ------ | ----------------------- | ------ | ----- | 22 | | CSharp | HttpClient | Yes | [link](https://learn.microsoft.com/en-us/dotnet/api/system.net.decompressionmethods?view=net-8.0) | Only if the handler `AutomaticDecompression` is set to `All` (not the default!) | 23 | | Go | net/http | Yes | [link](https://webscraping.ai/faq/go/how-do-i-process-compressed-http-responses-in-go) | Only gzip is supported. Ensure `DisableCompression` is false (default) on the transport. | 24 | | Java | OkHttp | Yes | [link](https://medium.com/tech-insider/okhttps-gzip-compression-904919638458) | | 25 | | PHP | Guzzle | Yes | [link](https://docs.guzzlephp.org/en/stable/request-options.html#decode-content) | Does NOT add the accept encoding header automatically. | 26 | | Python | HTTPX | Yes | None | Couldn't find any source, but tested with 0.27.0 and httpbin | 27 | | TypeScript/JavaScript | fetch | Yes | [link](https://stackoverflow.com/questions/70092469/node-fetch-automatic-gzip-decompression/78779242#78779242) | Automatically adds the accept encoding header and decompresses the response. Make sure `compress` is set to `true` (default) in the request options. | 28 | 29 | ## Example 30 | 31 | ```csharp 32 | 33 | public class DecompressionHandler : DelegatingHandler 34 | { 35 | protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 36 | { 37 | // Declare compression support to server 38 | var gzipQString = new StringWithQualityHeaderValue("gzip"); 39 | if (!request.Headers.AcceptEncoding.Contains(gzipQString)) 40 | { 41 | request.Headers.AcceptEncoding.Add(gzipQString); 42 | } 43 | 44 | // Record feature usage for telemetry 45 | var requestContext = request.GetRequestContext(); 46 | requestContext.FeatureUsage |= FeatureFlag.CompressionHandler; 47 | 48 | // Send Request 49 | var response = await base.SendAsync(request, cancellationToken); 50 | 51 | // Decompress gzipped responses 52 | if (response.Content != null && response.Content.Headers.ContentEncoding.Contains("gzip")) 53 | { 54 | response.Content = new StreamContent(new GZipStream(await response.Content.ReadAsStreamAsync(), CompressionMode.Decompress)); 55 | } 56 | return response; 57 | } 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /middleware/DeltaResponseHandler.md: -------------------------------------------------------------------------------- 1 | # Delta Response Handler 2 | 3 | Provide a mechanism to discover changes returned via a delta response. Delta responses can have properties set to null. Not all platforms provide a way to discover properties that have been 'unset' to their default state. For example, in .NET, our models default to not serialize null values. 4 | 5 | ## Requirements 6 | 7 | - Provide an ability to discover changed property values, including changes to null or an empty string. 8 | - Changed property MUST be returned as an array of tokens that identify the name of the property returned in a delta response. 9 | - Changed property tokens SHOULD be in dot notation form. The token form SHOULD be in a form that makes it easy to identify the changed property in code. For example, the JSON object below would be represented as `["name", "address.city"]`. 10 | 11 | ```json 12 | { 13 | "value" : 14 | { 15 | "name": null, 16 | "address" : 17 | { 18 | "city": null 19 | } 20 | } 21 | } 22 | ``` 23 | - The changed property array SHOULD be set on a `Changed` property in the response object. 24 | 25 | ## Example usage and implementation 26 | 27 | ### .NET 28 | 29 | We will make the following changes to support this feature: 30 | * BaseRequest.cs/IBaseRequest.cs - Add a private setter for responseHandler. 31 | * Create a **DeltaResponseHandler** class that will encapsulate a serializer that will deserialize the changed properties into an array of tokens in a property named `changed` in the AdditionalData property bag. 32 | * BaseRequestExtensions.cs - Add WithResponseHandler(ResponseHandler rh) request builder where T : IBaseRequest. This will replace the default response handler set on BaseRequest. It will only be applicable to the single request. 33 | 34 | 35 | ```CSharp 36 | // Developer experience 37 | var userDeltaCollectionPage = await graphClient.Users 38 | .Delta() 39 | .Request() 40 | .WithResponseHandler(new DeltaResponseHandler()) 41 | .GetAsync(); 42 | 43 | userDeltaCollectionPage.AdditionalData.TryGetValue["changed"](out object changedPropertyList); 44 | ``` -------------------------------------------------------------------------------- /middleware/HeadersInspectionHandler.md: -------------------------------------------------------------------------------- 1 | # Headers Inspection Handler 2 | 3 | A middleware handler that allows the client application to inspect request and response headers. 4 | 5 | ## Requirements 6 | 7 | - MUST expose the request and response headers on the original option value. 8 | - MUST make copies of the headers and their values. 9 | - MUST perform case insensitive lookups on headers values. 10 | - Provide an option to enable request headers inspection. (default disabled) 11 | - Provide an option to enable response headers inspection. (default disabled) 12 | - Additional observability requirements in [Observability](../Observability.md) 13 | 14 | ## Example 15 | 16 | ### CSharp 17 | 18 | ```CSharp 19 | 20 | var headersInspectionOption = new HeadersInspectionHandlerOption { 21 | InspectResponseHeaders = true, 22 | }; 23 | var result = await client.Me.GetAsync(c => c.Options.Add(headersInspectionOption)); 24 | Console.WriteLine("The response content type is {contentType}", headersInspectionOption.ResponseHeaders["Content-Type"]); 25 | ``` 26 | -------------------------------------------------------------------------------- /middleware/LoggingHandler.md: -------------------------------------------------------------------------------- 1 | # Logging Handler [In Progress] 2 | 3 | ## Objectives 4 | 5 | This component should allow a developer to provide their own logging listener and important information related to the request and response should be written to the listener. Where the native HTTP client library has its own built in logging infrastructure, that should be preferred over a custom solution. 6 | 7 | ## Requirements 8 | 9 | ## Security Considerations 10 | 11 | ## Performance Considerations -------------------------------------------------------------------------------- /middleware/LongRunningOperationHandler.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-design/a9f5bd8eb020ae99bd186c286bae5dee038262e7/middleware/LongRunningOperationHandler.md -------------------------------------------------------------------------------- /middleware/ParametersNameDecodingHandler.md: -------------------------------------------------------------------------------- 1 | # Parameters Name Decoding 2 | 3 | ## Objectives 4 | 5 | Provide support for characters that are reserved for RFC6570 Uri Template so they can be used by clients. Example `$` is a reserved character that is commonly used by OData services for query parameters like `$select`. Since it is reserved, the code generation projects the following URI template `{+baseUrl}/users{?%24select}` which will get expanded to `https://graph.microsoft.com/v1.0/users?%24select=firstName`. The service won't automatically decode it, so this reserved character needs to be decoded by a middleware handler before the request is sent to the service. 6 | 7 | ## Requirements 8 | 9 | - MUST provide an option to enable/disable the handler per request. With a default value for all requests. 10 | - MUST provide a way to set the default option for all requests from the application. With a default value in the handler. 11 | - The option MUST provide a way to select which characters will be decoded. With a default of `$`, `.`, `-`, `~`. (note: those last 3 MIGHT already be decoded by the HTTP client pipeline, in which case only the dollar sign is required as a default) 12 | - MUST ONLY decode the query parameters names, not the values, not the path/host/scheme. 13 | - The handler MUST ONLY URI decode the provided characters, and no other characters. (%XX) 14 | - Additional observability requirements in [Observability](../Observability.md) 15 | -------------------------------------------------------------------------------- /middleware/RedirectHandler.md: -------------------------------------------------------------------------------- 1 | # Redirect Handler 2 | 3 | A piece of client-side middleware designed to handle 3XX responses transparently so that application code doesn't need to. 4 | 5 | ## Objectives 6 | 7 | - Remove the need for application developers to handle redirect responses. 8 | 9 | ## Requirements 10 | 11 | - Additional observability requirements in [Observability](../Observability.md) 12 | 13 | ### Status Codes Handled 14 | 15 | |Status Code | Description | Notes| 16 | |--|--|--| 17 | | 301 | Moved Permanently | | 18 | | 302 | Found | Temporary | 19 | | 303 | See Other | POST changes to GET | 20 | | 307 | Temporary Permanently |Method does not change | 21 | | 308 | Moved Permanently |Method does not change | 22 | 23 | For all status codes listed, redirect handler should use the `Location` header from the redirect response to construct a new request to the new URL that is the same as the original request except for where noted otherwise. 24 | 25 | ### Max redirects 26 | 27 | Requests should be able to include a hint as to how many redirects are allowed before the response should be returned. Allowing zero maximum redirects indicates that no redirects are allowed. Handler should have a default max redirects if request does not provide one. 28 | 29 | ### Redirected Request 30 | 31 | For requests with payloads, handler must ensure the payloads are buffered prior to making the request, so that request can be re-issued if necessary. If max redirects is zero then buffering is not necessary. This allows a client who wishes to stream a request body to stop the buffering from occuring. 32 | 33 | The redirected request URL should be derived from the Location header of the redirect response. If the Location header is a relative reference, the final URL should be relative to the original target URL. 34 | 35 | The redirected request should contain the same headers and body as the original request, except for scenarios noted in the security considerations section. If the original request object contains other state that is not transmitted over the wire, it should also be preserved in the redirected request to ensure consistent middleware behavior. 36 | 37 | ## Performance Considerations 38 | 39 | For permanent redirects, redirection MAY be cached and reused on future requests to avoid round trip. 40 | Care should be taken to try and avoid duplicating request payloads when making the redirected request. 41 | 42 | ## Security Considerations 43 | 44 | Authorization header MUST not be resent if authority (scheme+host) of the Location header is different than the original target URL. 45 | 46 | ## Open Issues 47 | 48 | - What should be the behaviour if a Location header is not provided? -------------------------------------------------------------------------------- /middleware/RequestContext.md: -------------------------------------------------------------------------------- 1 | # Request Context 2 | 3 | ## Objectives 4 | 5 | ## Requirements 6 | 7 | - A request context object is attached to the native Http Request object, or Graph Request Object, to control the behavior of middleware and allow the calling app to react to events that occur during the execution of middleware. 8 | - The request context object should provide a dictionary of optional middleware options objects that can be accessed by middleware components to override the options provided during middleware initialization. 9 | - The request context object MUST contain a property `ClientRequestId` which can be set to correlate all actions related to the current request. 10 | 11 | ### Feature Usage Flags 12 | 13 | The FeatureUsage flag is set on the Request Context so that telemetry could be sent in a header. 14 | This object should be more broadly available so that we can use this instrument other parts of the core library, and not just the middleware pipeline. 15 | For example, if we want to understand whether customers are using a custom HttpProvider object in the GraphServiceClient, we could capture a flag for that scenario. 16 | We propose a factory method for RequestContext object that can ensure the appropriate feature flags are preset for session level features. 17 | 18 | The request context object should contain a property `FeatureUsage` which is a bitmap value that is used to flag feature usage. 19 | This feature is not used to actually enable behavior, but to aggregate a value that is used by the telemetry handler to capture the presence of a handler. 20 | 21 | 22 | | Flag | Feature | 23 | |--|--| 24 | | 0x00000000 | None | 25 | | 0x00000001 | Redirect Handler Enabled | 26 | | 0x00000002 | Retry Handler Enabled | 27 | | 0x00000004 | Auth Handler Enabled | 28 | | 0x00000008 | Default HttpProvider Enabled | 29 | | 0x00000010 | Logging Handler Enabled | 30 | | 0x00000020 | Service Discovery Handler Enabled | 31 | | 0x00000040 | Compression Handler Enabled | 32 | | 0x00000080 | Connnection Pool Manager Enabled | 33 | | 0x00000100 | Long Running Operation Handler Enabled | 34 | | 0x00000200 | Batch Request Content Used | 35 | | 0x00000400 | Page Iterator task Used | 36 | | 0x00000800 | File Upload task Used | 37 | 38 | ### Middleware Control Map 39 | 40 | Each type of middleware that is installed in a pipeline should have the option to receive a Middleware control object that has properties to affect the behavior of the middleware. These control objects are registered on the request context object so as to be able to provide request specific behavior. 41 | 42 | ### Event Callbacks 43 | Middleware controls need the ability to notify the calling application of significant actions that may be taken by the middleware. Potential callbacks include: 44 | 45 | - ShouldRetryAfter(int delaySeconds, Request request, Response response) 46 | - ShouldRedirect(Response response) 47 | - AuthenticationChallenge(Response response) 48 | 49 | ## Security Requirements 50 | 51 | ## Performance Requirements 52 | -------------------------------------------------------------------------------- /middleware/RetryHandler.md: -------------------------------------------------------------------------------- 1 | # Retry Handler 2 | 3 | ## Objectives 4 | 5 | Provide a reusable component that provides application developers with effective handling of failed requests that can be retried. 6 | 7 | ## Requirements 8 | 9 | - Retried Requests should be equivalent to the initial request made. 10 | - A `retry-attempt` header SHOULD be added which contains a numeric value representing the retry number. This value will be used for diagnostics and determining the effectiveness of the retry handler. 11 | - Retries MUST respect the `retry-after` response header if provided. 12 | - Retries should be limited by maximum elapsed time and maximum retry attempts. 13 | - Where no `retry-after` header is provided by the server, perform retry with the retry handler options specified by the library consumer, or fall back to the default handler implemented as exponential backoff handler. 14 | - Allow library consumer to set the parameters for the exponential backoff, and also provide sane defaults, e.g.: 15 | - initial retry interval: 3 seconds 16 | - retry interval multiplier: (retry number)^2 17 | - maximum interval: 180 seconds 18 | - maximum elapsed time: 1800 seconds (10 retries ^ 2 * 180 seconds) 19 | - maximum retry attempts: 10 20 | - Only requests with payloads that are buffered/rewindable are supported. Payloads with forward only streams will be have the responses returned without any retry attempt. 21 | - Customers can specify a custom value for the `RetriesTimeLimit` greater than 0 to introduce time-based evaluated request retries alongside the default count-based request retry. 22 | - If the `RetriesTimeLimit` value has been specified, the cumulative retry time and `retry-after` value for each request retry will be evaluated against this value; if the cumulative retry time plus the `retry-after` value is greater than the `RetriesTimeLimit`, the failed response will be immediately returned, else the request retry continues. 23 | - Additional observability requirements in [Observability](../Observability.md) 24 | 25 | ### Supported Status Codes 26 | 27 | |Status Code | Description | Notes| 28 | |--|--|--| 29 | | 429 | Too many requests | | 30 | | 503 | Server Unavailable | | 31 | | 504 | Gateway Timeout | | 32 | 33 | ## Future Potential Requirements 34 | 35 | - Individual requests should support a policy mechanism that determines if a retry should occur. 36 | 37 | ## Performance Considerations 38 | 39 | The thread processing a request should not be blocked while waiting for a retry delay to expire. 40 | 41 | ## Security Considerations 42 | 43 | ## References 44 | 45 | [Graph Throttling](https://developer.microsoft.com/en-us/graph/docs/concepts/throttling) 46 | 47 | ## Open Issues 48 | 49 | - New requests made while an existing request has been queued for retry, should be considered for adding to the queue. 50 | - Requests with the exact same URL should be queued. 51 | - Requests with the same path should be queued. 52 | - Requests with n matching initial segments should be queued after 5-n throttled requests. e.g. If a there have been 3 throttled requests for requests with the first two segments matching, then the next matching request should be queued. 53 | -------------------------------------------------------------------------------- /middleware/ServiceDiscoveryHandler.md: -------------------------------------------------------------------------------- 1 | # Service Discovery Handler 2 | 3 | ## Objective 4 | 5 | Depending on where client code is located, it may be optimal to select between different instances of a service. The service locator handler can inspect a request and choose to replace the host of the request URL with an alternative one. 6 | 7 | This approach allows a generated service library to assume the default service location and the target location can be changed per request based on some application defined heuristics. 8 | 9 | ## Requirements 10 | 11 | - Service Discovery Handler must accept a function that will provide an alternate URL based on an input URL 12 | - If the discovery function returns returns a non null URL then the request URL will be updated. 13 | - This handler should be configured to execute after the redirect handler to ensure redirect URLs are also updated. 14 | 15 | 16 | ## Example Usage 17 | 18 | 19 | ```CSharp 20 | 21 | var factory = new GraphClientFactory() 22 | .With(new ServiceDiscoveryHandler((Uri requestUrl) => { 23 | if (requestUrl.Path.StartsWith("/people")) { 24 | return ChangeHost(requestUrl,"https://localhost:3000"); 25 | } 26 | return null; // Don't touch URL 27 | })); 28 | factory.CreateClient() 29 | ``` 30 | -------------------------------------------------------------------------------- /middleware/SunsetHandler.md: -------------------------------------------------------------------------------- 1 | # Sunset Handler 2 | 3 | ## Objectives 4 | 5 | Let application operation teams know that a given operation depends on an API which is slated for removal so they can proactively update the application before a breakage occurs. 6 | 7 | ## Requirements 8 | 9 | - MUST provide an option to disable the behaviour for a given request with a settable default for all requests. 10 | - Additional observability requirements in [Observability](../Observability.md) 11 | - MUST create an event under the span (see Observability) whenever an [HTTP sunset header](https://datatracker.ietf.org/doc/html/rfc8594) is encountered in the response. 12 | - The additional event MUST contain the sunset date value provided by the header as an attribute. 13 | - The span and event MUST NOT contain the URI since parent spans already offer that tag/attribute and it could contain sensitive information. 14 | - The additional event MUST contain the sunset link value (see RFC) if present as an attribute. 15 | -------------------------------------------------------------------------------- /middleware/TelemetryHandler.md: -------------------------------------------------------------------------------- 1 | # Telemetry Handler 2 | 3 | ## Objectives 4 | 5 | Provide a mandatory middleware component that attaches metadata to a Graph request in order help the SDK team improve the developer experience. 6 | 7 | ## Requirements 8 | 9 | - Add a `client-request-id` header with GUID value to request if one is not present. If the [RequestContext](../middleware/RequestContext.md) has a `ClientRequestId` property set then that value should be used. 10 | 11 | - Add a single `SdkVersion` request header to each request to identify the language and version of the client SDK library(s). The product identifier should be of the format `graph-{lang}/{majorVersion}.{minorVersion}.{patchVersion}` for client libraries. If the client SDK library has more than one component i.e. core, v1.0/beta service model library, then the product identifier should capture them as follows based on usage : 12 | 13 | | Client SDK Library Component | Template | Example | 14 | | --- | --- | --- | 15 | | **Core** | `graph-{lang}-core/{major}.{minor}.{patch}` | `graph-dotnet-core/1.16.0` | 16 | | **v1.0 Service Model** | `graph-{lang}/{major}.{minor}.{patch}` | `graph-dotnet/1.16.0` | 17 | | **Beta Service Model** | `graph-{lang}-beta/{major}.{minor}.{patch}` | `graph-dotnet-beta/1.16.0` | 18 | | **Future Service Model** | `graph-{lang}-{endpoint}/{major}.{minor}.{patch}` | `graph-dotnet-v2.0/1.16.0` | 19 | 20 | The `SdkVersion` version can contain multiple values, or appear multiple times in a request. Values MUST be comma delimited as normal header lists are if the `SdkVersion` contains multiple values. e.g. 21 | 22 | ``` 23 | SdkVersion: MyApp/1.0, graph-dotnet/1.15.0, graph-dotnet-core/1.16.0 (featureUsage=0f) 24 | ``` 25 | 26 | - The Telemetry Handler MUST NOT append values to an existing SdkVersion header in the case of a retry scenario. The handler can check for the existance of the `retry-attempt` header to determine whether the request is a retry. 27 | 28 | - The Telemetry Handler SHOULD be the last handler. If there is a final handler, it SHOULD be the second to last handler in the chain so that it can aggregate telemetry information about handlers earlier in the chain. 29 | 30 | - The template for the component version that also represents a package should match the form defined in [Versions](../Versions.md). 31 | 32 | - Client SDK library component names SHOULD be lower case. 33 | 34 | - If available, add a the `featureUsage` value contained in the [RequestContext](../middleware/RequestContext.md) to the `SDKVersion` header as key/value pair in the comment after the product identifier. e.g. 35 | 36 | `SdkVersion: graph-javascript/1.0.0 (featureUsage=0f)` 37 | 38 | - A `hostOS` value can also be added as a key/value pair in the comment to help us identify the OS on which our client SDK is running on. e.g. 39 | 40 | `hostOS=Microsoft Windows 10.0.18362` 41 | 42 | - Add `runtimeEnvironment` key/value pair to capture the runtime framework on which the client SDK is running on. Ideally, we should capture the runtime environment in the form of `runtimeEnvironment=Name/Version`. e.g. 43 | 44 | - `runtimeEnvironment=.NETFramework/1.1` for .NET. 45 | - `runtimeEnvironment=Node/1.1` for JavaScript. 46 | - `runtimeEnvironment=JRE/1.1` for Java. 47 | 48 | Multiple key/value pairs included in the comment should be delimited by a semi-colon. 49 | 50 | - Certain workloads error out when an unexpected header is present in the request. The middlewares should check: 51 | - If the request URL is a Graph serve endpoint or a custom url provided by the developer, then append or update the telemetry headers. 52 | - Else the middleware should delete telemetry header. 53 | 54 | #### Ideal Metadata Capture 55 | ``` 56 | SdkVersion: graph-dotnet-beta/0.6.0-preview, graph-dotnet-core/1.16.0 (featureUsage=0f; hostOS=Microsoft Windows 10.0.18362; runtimeEnvironment=.NETFramework/4.7.2) 57 | client-request-id: fdae6861-5916-486d-93d9-9129160b2d79 58 | 59 | ``` 60 | 61 | ## Remarks 62 | 63 | Ideally we would use the `User-Agent` header to identify the libraries that were involved in making the request. Unfortunately, this does not work for Javascript because despite allowing `User-Agent` to be changed in fetch() there is a [bug in Chromium](https://bugs.chromium.org/p/chromium/issues/detail?id=571722) that remains unfixed for more than 3 years. 64 | 65 | The SdkVersion borrows much of the syntax from `user-agent` but instead uses a comma delimited list. 66 | 67 | ABNF Syntax for SdkVersion: 68 | 69 | SdkVersion = 1#( product [ RWS comment ] ) 70 | product = token ["/" product-version] 71 | 72 | where `product`, `product-version`, `comment` and `token` are defined in [RFC 7230](https://tools.ietf.org/html/rfc7230) and [RFC 7231](https://tools.ietf.org/html/rfc7231). 73 | 74 | Currently, many SDKs embed the version number into the `product` using a hypen as a separator. Over time we should migrate these to use the `/` to make parsing the version easier. `/` should not be used for any other purpose in the `SdkVersion` header. 75 | 76 | The order of `SdkVersion` values is significant. Core library SdkVersion header values MUST be specified as the last value in the SdkVersion header. Service library header values MUST precede the Core library value in the SdkVersion header. Microsoft applications header values MUST precede the Service library header value. Another way to look at this is to list products/components in dependency order where the left most token represents the highest level in the dependency chain. 77 | 78 | > Note: The `SdkVersion` header is intended for Microsoft telemetry purposes. It is used to capture information about Microsoft SDKs and products that use the Microsoft Graph APIs. Non-Microsoft applications that use this header, or alter the value of this header, will gain no benefit from using this header. Use of this header by non-Microsoft applications reduces the telemetry value of this header and reduces the data quality associated with header which reduces the insights that we can gain from this header. In turn, this means fewer data-driven improvements to our SDKs. Users of the SDK or the SDK repository should not alter the value of the SdkVersion header. 79 | 80 | ## Performance 81 | 82 | The SdkVersion header is not used for any performance purposes by Microsoft Graph. 83 | 84 | ## Security 85 | 86 | The SdkVersion header MUST not be used for any security related features unless the security feature use is captured by the `featureUsage` value. 87 | 88 | ## Privacy 89 | 90 | The SdkVersion header MUST not contain information that identifies users or organizations. 91 | -------------------------------------------------------------------------------- /middleware/middleware.md: -------------------------------------------------------------------------------- 1 | # Middleware 2 | 3 | ## Objective 4 | 5 | In order to enable a flexible way to support cross cutting features, client libraries should support a pipeline of pluggable middleware. Users should be able to remove existing middleware, replace it and add new piece of middleware at any location in the pipeline. 6 | 7 | ## Requirements 8 | 9 | - A Middleware pipeline should be configured so that all requests are passed through the chain of middleware in the same order and responses are processed in reverse order to requests. 10 | - A default pipeline should be configured by the Graph Client Factory for providing standard Graph behavior. 11 | - A pipeline can be customized at the time of creation of a HTTP client. Pipelines should not be reconfigured at runtime. 12 | - Middleware should accept a native or wrapped request object and also process the corresponding response. 13 | - Middleware components should be as cohesive as possible with an effort to minimize dependencies on other pieces of middleware. 14 | - A [RequestContext](./RequestContext.md) object should be passed along with the request object, to allow the behavior of middleware to be adjusted based on the specifics of the request and state aggregated by other pieces of middleware. 15 | - When middleware assigns HTTP header values to requests or responses they SHOULD only update a single valued header if no value already exists. 16 | 17 | ## Performance Considerations 18 | 19 | Due to the fact that every request and response pass through each and every piece of middleware it is important that processing is efficient. Middleware pipelines can be used on both application servers as well as clients so they need to be able to support a high volume of request and be thread safe. 20 | 21 | Middleware should be cautious about preserving state between the processing of the request and the handling of the response as this can put significant memory pressure on high load servers. 22 | 23 | ## Security Considerations 24 | 25 | 26 | ## Example Usages 27 | 28 | Here's a straw man proposal for how Middleware control objects could be used to influence the behavior of the request. 29 | 30 | ```csharp 31 | 32 | var client = new HttpClientFactory().CreateClient(new DelegatingHandler[] { 33 | new AuthHandler(new AuthOptions()), 34 | new RetryHandler(new RetryOptions()), 35 | new RedirectHandler(new RedirectOptions()) 36 | }); 37 | 38 | var request = new HttpRequestMessage() 39 | { 40 | RequestUri = new Uri("https://graph.microsoft.com/v1.0/me"), 41 | Method = HttpMethod.Get 42 | }; 43 | 44 | request.AddMiddlewareControl(new IMiddlewareControl[] { 45 | new RetryOptions() 46 | { 47 | ShouldRetry = (req) => { return false; } 48 | }, 49 | new RedirectOptions() 50 | { 51 | MaxRedirects = 0 52 | }, 53 | new AuthOptions() 54 | { 55 | SelectedUser = "bob" 56 | }}); 57 | 58 | var response = client.SendAsync(request); 59 | 60 | ``` 61 | 62 | ## Open Issues 63 | 64 | - Should we create a MiddlewareException? Should we have RequestProcessingException and a ResponseProcessingException so a user knows if the call was actually made? -------------------------------------------------------------------------------- /providers/AuthenticationProvider.md: -------------------------------------------------------------------------------- 1 | # Authentication Provider 2 | 3 | ## Objective 4 | 5 | The AuthenticationProvider is an implementation of a public interface that is used as an adapter to a external authentication library. The goal is to simplify the set of options provided by the OAuth2 library by limiting the functionality to just capabilities available on the Graph. 6 | 7 | ## Graph Requirements 8 | 9 | - Allow selecting National Clouds 10 | - Handle conditional access challenges 11 | - Map external exceptions to MSGraph ServiceException and ClientException 12 | 13 | ## National Cloud Endpoints 14 | 15 | |Country | Authorization Server| 16 | |---|---| 17 | | China | https://login.chinacloudapi.cn | 18 | | Germany | https://login.microsoftonline.de | 19 | | US Government | https://login.microsoftonline.us | 20 | | Global service (default) | https://login.microsoftonline.com | 21 | 22 | 23 | ## Supported Flows 24 | 25 | ### Interactive Authentication Provider 26 | 27 | Shows a UI popup provided by the Auth Server 28 | 29 | ### UsernamePassword Authentication Provider 30 | 31 | Passes username/password over the wire to the AuthServer 32 | 33 | ### IntegratedWindows Authentication Provider 34 | 35 | Grabs credentials from the OS 36 | 37 | ### DeviceCode Authentication Provider 38 | 39 | Outputs a code that needs to be manually entered in a web form at a designated URL. 40 | 41 | 42 | ## Scenarios 43 | 44 | ### Multi-User 45 | 46 | ### Multi-provider 47 | 48 | ### Cloud Discovery 49 | App asks user for email 50 | We call OpenIdConnect discovery endpoint and identify Auth Server/TokenServer 51 | Pass that to the "ClientApplication" 52 | 53 | 54 | ### PerSession Provider 55 | 56 | ### PerRequest Provider 57 | 58 | 59 | 60 | ### Confidential Client 61 | - Multi-user 62 | - Multi-cloud 63 | - Stateless token cache 64 | - I'm thinking that we don't actually need stateless support because if you attach before access and after Update events to a token cache then it can load what it needs on the fly. This would mean that a single token cache can satisfy the distributed cache requirement. 65 | 66 | ### Public Client Application 67 | 68 | = Per Session Config 69 | - Multi-user 70 | - Multi-cloud 71 | 72 | ## OAuth2 Library Requirements 73 | 74 | - Accept native request object and add an Authorization header with a valid bearer token. 75 | - Reuse previously obtained tokens for same host 76 | - Refresh expired tokens 77 | - Obtain scopes from inbound request and compare with current scopes. If new scopes are present during a request then initiate request for consent, and use new token with request. 78 | 79 | ## Security Requirements 80 | 81 | ## Performance Requirements 82 | -------------------------------------------------------------------------------- /providers/LoggingProvider.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-design/a9f5bd8eb020ae99bd186c286bae5dee038262e7/providers/LoggingProvider.md -------------------------------------------------------------------------------- /providers/providers.md: -------------------------------------------------------------------------------- 1 | # Providers 2 | 3 | Providers are pluggable components that allow us to map external libraries to internal services. -------------------------------------------------------------------------------- /raptor/pipeline.md: -------------------------------------------------------------------------------- 1 | # Raptor Pipeline as a Metadata Deployment Gate 2 | 3 | ## Objectives 4 | 5 | Provide an Azure pipeline (or a set of Azure pipeline jobs), which takes arbitrary staging metadata and accompanying documentation input and outputs a decision on whether metadata can be signed off for release based on generated SDK and snippet quality. 6 | 7 | ## Requirements 8 | 9 | - Staging metadata should be an input variable 10 | - Metadata should be available as a public URL or as an Azure DevOps artifact 11 | - Docs branch should be an input variable 12 | - If the metadata is accompanied with new documentation, it should be checked into a branch of `microsoft-graph-docs` repo and branch name should be provided to the pipeline 13 | - Snippet Generation should be available as a tool to ApiDoctor 14 | 15 | ## Current State 16 | 17 | The tools that are required to answer the question in the [Objectives](#Objectives) section are spread across the following repos. 18 | 1. [microsoftgraph/MSGraph-SDK-Code-Generator](https://github.com/microsoftgraph/MSGraph-SDK-Code-Generator) 19 | - Tool to generate C# SDK (Typewriter) 20 | - Has a binary release 21 | - Can be compiled as a .NET Framework executable 22 | 2. [microsoftgraph/msgraph-sdk-raptor](https://github.com/microsoftgraph/msgraph-sdk-raptor) 23 | - Tool to verify whether generated code snippets compile 24 | - Creates compilation test cases for each V1 and Beta snippet live at docs.microsoft.com 25 | - Consumes Microsoft.Graph V1 and Beta DLLs as Nuget packages 26 | 3. [microsoftgraph/msgraph-sdk-dotnet](https://github.com/microsoftgraph/msgraph-sdk-dotnet) 27 | - SDK repo to compile generated code 28 | 4. [microsoftgraph/microsoft-graph-explorer-api](https://github.com/microsoftgraph/microsoft-graph-explorer-api) 29 | - Provides an HTTP endpoint to generate C# snippets from HTTP snippets. The HTTP endpoint is the only current option that is exposed for this functionality. 30 | 5. [microsoftgraph/microsoft-graph-docs](https://github.com/microsoftgraph/microsoft-graph-docs) 31 | - Contains HTTP snippets 32 | - New documentation can be added by forking a branch 33 | 6. [OneDrive/apidoctor](https://github.com/OneDrive/apidoctor) 34 | - Tool to extract HTTP snippets from docs and write generated snippet references 35 | - Snippet generation functionality is not available in the main repo. 36 | - It is available at [andrueastman/apidoctor](https://github.com/andrueastman/apidoctor) / [generate-snippets](https://github.com/andrueastman/apidoctor/tree/generate-snippets). 37 | - The functionality is coupled with a PR generation to docs repo upon completion of snippet generation. 38 | - Hits public Graph Explorer API HTTP endpoint to generate the snippets 39 | 40 | ## Proposed Solution 41 | ### Input variables 42 | 1. `version` : `v1.0` or `beta` 43 | 2. either `metadataURL` or metadata artifact from another pipeline run 44 | 3. `docsBranch` : branch on `microsoft-graph-docs` repo, containing new documentation 45 | 4. `typewriterReleaseVersion` : true if the released version of typewriter to be used from Github releases. Otherwise it is expected to be compiled from source. 46 | 47 | ### Pipeline 48 | 1. Prepare metadata input 49 | 1. If metadata is given as a public URL in `metadataURL`, set `metadataPath` variable to that 50 | 1. Typewriter can take URL as metadata input 51 | 1. If metadata is an Azure DevOps artifact 52 | 1. Download the artifact to a local path 53 | 2. Save that local path as `metadataPath` 54 | 2. Build snippet generation tool 55 | 1. Checkout `microsoft-graph-explorer-api` repo 56 | 2. Build snippet generation tool 57 | 3. Save executable's path as `snippetGeneratorPath` 58 | 3. Checkout `microsoft-graph-docs` repo 59 | 1. Use `docsBranch` variable if defined 60 | 2. Use `master` if not defined 61 | 3. Save repo path as `docsRepoPath` 62 | 4. Run apidoctor 63 | 1. checkout `OneDrive/apidoctor` repo 64 | 2. run `apidoctor` with the following inputs 65 | 1. `docsRepoPath` 66 | 2. `snippetGeneratorPath` 67 | 5. SDK generation 68 | 1. checkout `msgraph-sdk-dotnet` repo 69 | 1. Save path as `dotnetRepoPath` 70 | 2. If `typewriterReleaseVersion` is `true` 71 | 1. Download `Typewriter` from Github releases 72 | 3. Else 73 | 1. Checkout `MSGraph-SDK-Code-Generator` repo 74 | 2. Build `Typewriter` 75 | 3. Run Typewriter Tests 76 | 4. Run `Typewriter` for `CSharp` 77 | 1. Consumes `metadataPath` 78 | 5. Clean old generated files in `dotnetRepoPath` 79 | 6. Copy new generated files into `dotnetRepoPath` 80 | 7. Build `Microsoft.Graph` solution 81 | 8. Run `Microsoft.Graph` tests 82 | 9. Save `Microsoft.Graph.dll` path as `dllPath` 83 | 6. Raptor 84 | 1. checkout `msgraph-sdk-raptor` repo 85 | 2. Reference `Microsoft.Graph.dll` using `dllPath` 86 | 3. Using `version` information attempt to compile corresponding snippets from `docsRepoPath` 87 | 4. Give a thumbs up if all tests pass 88 | 89 | ### Prerequisites for defining the pipeline above 90 | 1. Decouple apidoctor from generating a PR after snippet generation 91 | 1. Current logic is already running as part of a weekly pipeline. PR generation logic can be moved out of apidoctor.exe to an explicit pipeline step. 92 | 2. Have snippet generation logic as a class library or as an executable for apidoctor's consumption instead of HTTP calls 93 | 3. Have Raptor 94 | 1. Either reference the project directly in `msgraph-sdk-dotnet` repo 95 | 2. Or take `Microsoft.Graph.dll` location as an input 96 | 97 | ## Performance Considerations 98 | - Is typewriter build time a consideration? 99 | - `apidoctor` analysis and extraction of HTTP snippets is the slowest step in all of the proposed steps above and we don't have an in-house solution to speed that up at the moment. This is the step that precedes the HTTP calls to Graph Explorer API for generating snippets. We can consider saving that state as a reusable input if we continuously try to build from `master` branch. Not sure about what the structure of that runtime state looks like. 100 | 101 | ## Open Issues 102 | 1. Need to sync with AGS team to see where this pipeline is going to be located. 103 | 2. Is this going to be a stand-alone pipeline or part of an existing pipeline? The design can easily fit both pictures and our preference would be a stand-alone pipeline for testing purposes on our end. 104 | 3. Since we own the tooling that would be part of the gate keeping, we'd need to define an SLA for what type of issues we will address, and when we can address them. There are many ways we could unexpectedly block deployment. 105 | We need to provide prescriptive guidance on what the expectations are when a deployment is gated. What type of issues require changes for the workload, and what are the changes? We will need to check that our error messages are actionable. We may need to start out as a simple non-gated check, and as we improve, we could start gating when we have a higher degree of accuracy and actionable information provided to onboarding workloads. 106 | -------------------------------------------------------------------------------- /tasks/FileUploadTask.md: -------------------------------------------------------------------------------- 1 | # File Upload Task 2 | 3 | ## Objectives 4 | 5 | Uploading a large file can be done by creating an upload session which allows the upload of slices of the file in sequential API requests. This therefore enables the possibility mechanisms such as resuming the upload or cancelling the upload. 6 | This task aims to provide a fluent and easy to use mechanism for the consumer to control, monitor and cancel the handling of large uploads. 7 | 8 | ## Requirements 9 | 10 | - Accept a Microsoft.Graph.UploadSession instance to be used for the upload which is created and managed by the user and handed over to the task. The task should use the instance to start/resume the upload. 11 | - The task should validate the expiration time and expected byte range of the upload session on resume by querying the upload URL(thereby updating its instance of the Microsoft.Graph.UploadSession). 12 | - Accept a file/stream to be uploaded. 13 | - The task should provide a mechanism of configuring the upload properties i.e. the upload slice size. 14 | - The task should provide a mechanism of monitoring the upload progress through a callback/delegate. 15 | - The task should signal upload failures via standard exception mechanism. 16 | - The task should signal upload completion via by returning the status or returning a completed **Task**/**Promise**/**Future** for async APIs. 17 | - Using the RequestContext, the feature flag for the **FileUploadTask** can be set for telemetry purposes. 18 | - The task should not retry to upload failed slices as any retry should already be done by the retry-handler which the task should be using. 19 | - Upload task cancellation - 20 | - The task should provide cancellation capabilities through native task cancellation sources when using async APIs. 21 | - For Outlook, OneDrive and PrintDocument APIs, send a delete request to the upload session URL to cancel an upload task. 22 | - The task should provide api to access the upload session object containing its URL, expiry date and the cancellation status. 23 | - Implement `getUploadSession()` which returns a `LargeFileTaskUploadSession` object. 24 | - Progress Handler - 25 | - Provide capabilities to the user to track progress using callback functions. 26 | - Implement a `IUploadEventHandler`(currently `IProgress` in Java and c#) interface which contains the following callback properties - 27 | - `progress(rangeOfFileUploaded, additionalParamters):void` - called after each chunk upload. 28 | - `additionalParameters` should be an optional parameter of any strict type allowing flexibility in the implementation of the callback. 29 | - The task classes naming should match **LargeFileUploadXXX** (provider, result...) and all the classes should live in a **tasks** subnamespace, and be sharing the same namespace as the **PageIterator** task. 30 | - The task should be agnostic to the kind of upload being performed so as to support for various fileUpload scenarios e.g. **DriveItem** and **FileAttachment**. An example of the agnostic nature of task is how the task is marked as completed considering different response formats from each API: 31 | - If the response status is a 201. 32 | - An additional case for OneDrive is, if the response status is a 200 and the response contains an "id" then the task is complete. 33 | - MUST NOT compress the individual slices when it uploads them. This is because compressing slices with content-encoding header leads to invalid ranges/content length headers. (e.g. for a 100 bytes file of 10*10 bytes uncompressed slices, the content length would be ~7 bytes) 34 | - MUST NOT compress the whole file before sending it. This is because it might lead to memory issues in some client contexts (needs compressed result length to [compute the range header](https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-3)), it's also because the content-encoding header applies to individual requests and not to the overall set of requests and the server would try to decompress each slice independently, leading to a corrupted result. 35 | 36 | > Note: Outlook and Print API does not allow to update an attachment after it has been uploaded. 37 | 38 | Refer to the following documentation for more information: 39 | 40 | - [Outlook](https://docs.microsoft.com/en-us/graph/outlook-large-attachments) 41 | - [OneDriveItem](https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0&preserve-view=true) 42 | - [Print API](https://docs.microsoft.com/en-us/graph/upload-data-to-upload-session) 43 | 44 | ### Appendix on the range header for Microsoft Graph 45 | 46 | Microsoft Graph large file upload is inspired off [range response syntax](https://www.rfc-editor.org/rfc/rfc7233#section-4.2), [updated in RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2). 47 | 48 | ## Large File Upload Session 49 | 50 | Json Schema of the upload session object: 51 | 52 | ```json 53 | { 54 | "properties": { 55 | "url": { "type": "string"}, 56 | "expiryDate": { "type": "date"}, 57 | "isCancelled": { "type": "boolean"}, 58 | } 59 | } 60 | ``` 61 | ## Large File Upload Result Prototype 62 | 63 | Json Schema with a generic type for the object: 64 | 65 | - In case only an id is provided by the response, it'll be set as the id of the responseBody. 66 | - In case a location header is returned by the service, the property will be set and the responseBody will be left null. 67 | 68 | ```json 69 | { 70 | "type": "object", 71 | "properties": { 72 | "location": { "type": "string"}, 73 | "responseBody": { "type": "generic"}, 74 | } 75 | } 76 | ``` 77 | 78 | > Warning: the location header casing can be different between HTTP/1.1 **Location** and HTTP/2. Implementation should account for this. 79 | 80 | ## Large Upload Sequence 81 | 82 | 1. The consumer creates a large upload session using the SDK. 83 | 2. The consumer opens a stream to the file that needs to be uploaded (from storage, network, memory...). 84 | 3. The consumer creates a large upload task (this object model) passing the upload session, upload parameters, and the stream. 85 | 4. The consumer calls the **upload** method which: 86 | 1. Reads the next bytes according to parameters 87 | 2. Performs the upload request. 88 | 3. Reads the response to determine whether a next range of bytes is expected, or the upload is completed, or whether the upload has failed. 89 | 4. If next bytes are expected, the task repeats previous 3 steps. 90 | 5. The upload status is returned to the consumer or an exception is thrown if the upload failed. 91 | 92 | ## Large File Progress Handler Prototype 93 | 94 | ```json 95 | { 96 | "properties":{ 97 | "progress":{"type": "callback"}, 98 | "extraCallbackParam": { "type": "generic"} 99 | } 100 | } 101 | ``` 102 | ### Example of a response calling for the upload of the next range 103 | 104 | ```json 105 | { 106 | "@odata.context":"https://outlook.office.com/api/v2.0/$metadata#Users('')/Messages('')/AttachmentSessions/$entity", 107 | "expirationDateTime":"2019-09-25T01:09:30.7671707Z", 108 | "nextExpectedRanges":["2097152"] 109 | } 110 | ``` 111 | 112 | > Warning: In case the large upload task is targeting Outlook APIs, it is possible the casing of the json properties is different (i.e. **ExpirationDateTime** instead of **expirationDateTime**). Implementation should account for this. 113 | 114 | ## Performance Considerations 115 | 116 | - The **FileUploadTask** should not create buffers for more than the current slice of data being uploaded. 117 | 118 | ## Telemetry considerations 119 | 120 | - Using the RequestContext, the flag for the **FileUploadTask** can be set to enable monitoring of the use of the task. 121 | 122 | ## Example usage 123 | 124 | ### CSharp 125 | 126 | Create a handler for the progress of the upload 127 | 128 | ```CSharp 129 | // Setup the progress monitoring 130 | IProgress progress = new Progress(progress => 131 | { 132 | Console.WriteLine($"Uploaded {progress} bytes of {stream.Length} bytes"); 133 | }); 134 | ``` 135 | 136 | Create an upload session using the request builders 137 | 138 | ```CSharp 139 | // create an upload session 140 | var uploadSession = await graphClient.Drive.Items["01KGPRHTV6Y2GOVW7725BZO354PWSELRRZ"].ItemWithPath("_hamilton.png").CreateUploadSession().Request().PostAsync(); 141 | ``` 142 | 143 | Use the upload session and callback handler to run/resume the upload 144 | 145 | ```CSharp 146 | // create the Large File Upload task with relevant options 147 | var maxSliceSize = 320 * 1024; // 320 KB - Change this to your slice size. 5MB is the default. 148 | // var provider = new ChunkedUploadProvider(uploadSession, graphClient, stream, maxChunkSize); //Old way that is wrapped 149 | var largeFileUploadTask = new LargeFileUploadTask(uploadSession, graphClient, stream, maxSliceSize); 150 | 151 | // upload away with relevant callback 152 | LargeFileUploadResult uploadResult = await largeFileUploadTask.UploadAsync( progress ); 153 | 154 | ``` 155 | 156 | ### Java 157 | 158 | ```java 159 | // Define a callback for the upload progress 160 | final IProgressCallback callback = new IProgressCallback () { 161 | @Override 162 | public void progress(final long current, final long max) { 163 | //Check progress 164 | } 165 | }; 166 | ``` 167 | 168 | Create an upload session using the request builders 169 | 170 | ```java 171 | // create an upload session 172 | final IUploadSession uploadSession = testBase 173 | .graphClient 174 | .me() 175 | .drive() 176 | .items(itemId) 177 | .itemWithPath("_hamilton.jpg") 178 | .createUploadSession(new DriveItemUploadableProperties()) 179 | .buildRequest() 180 | .post(); 181 | 182 | ``` 183 | 184 | Use the upload session and callback handler to run the upload 185 | 186 | ```java 187 | // create the upload provider 188 | final LargeFileUploadTask largeFileUploadTask = new LargeFileUploadTask( 189 | uploadSession, 190 | graphClient, 191 | uploadFile, //the file to upload 192 | fileSize, //size of the slices 193 | DriveItem.class); 194 | 195 | // upload with the provided callback mechanism 196 | final LargeFileUploadResult result = largeFileUploadTask.upload(callback); //or uploadAsync to get a future 197 | 198 | ``` 199 | 200 | ### TypeScript 201 | 202 | Create an upload session using the request builders 203 | 204 | ```typescript 205 | 206 | // create the upload session 207 | const uploadSession = LargeFileUploadTask.createUploadSession(client, "REQUEST_URL", payload); 208 | 209 | // specify the options 210 | let options = { 211 | path: "/Documents", 212 | fileName: file.name, 213 | rangeSize: 1024 * 1024, 214 | }; 215 | 216 | // Create an upload session 217 | const uploadTask = new LargeFileUploadTask(client, fileObj, uploadSession, optionsWithProgress); 218 | 219 | //Use the upload task to run the upload 220 | const uploadResult:UploadResult = await uploadTask.upload(); 221 | ``` 222 | -------------------------------------------------------------------------------- /tasks/PageIteratorTask.md: -------------------------------------------------------------------------------- 1 | # Page Iterator Task 2 | 3 | ## Objectives 4 | 5 | For a variety of reasons, collections of entities are often split into pages and each page is returned with a URL to the next page. Sometimes, page granularity provided by the API does not match the requirements of the consumer. This task aims to simplify the life of consumers of paged collections. 6 | 7 | ## Requirements 8 | 9 | 1. Accept any collection of entities that contains a link to a next page. This must include support for a collection of entities returned from a batch response. 10 | 2. Accept a callback function that will be called for each entity in each page of the collection. The callback function should provide a way to indicate to the iterator to stop as well as resume iterating. The callback function should be passed to the Iterate method. The consumer should drive the use of the API by fetching the next page results whenever they require. 11 | 3. Dereference the next page link, when each of the the entities of the current page have been passed to the callback. 12 | 4. Expose the state of the PageIterator (NotStarted, Started, Delta etc.) to the user. This proves to be helpful in the scenario where a deltaLink is returned at which point the paging will be paused, however the user should be informed that it is safe to follow the delta link if they wish to examine the changes to the resource. 13 | 5. Accept or provide the ability to pass information like headers, middleware options in the request for the next page. For example, when using a PageIteratorTask for processing a large number of events, consumer should be able to specify the timezone preferance in the headers. 14 | 6. RequestOptions should be configurable outside of construction for flexibility with each call to **Iterate**. 15 | 7. Provie a **hasNext** method that returns true if there's a next item and false otherwise 16 | 8. Provide a **Next** method that returns the next item from the current page or the next page. 17 | 18 | ## Performance Considerations 19 | 20 | The PageIteratorTask should not maintain references to pages that it has already iterated over. An optimization could be to allow the Iterator to request future pages before it has completed walking the current page. If this is implemented, then there needs to be a way for a consumer to disable this optimization. 21 | 22 | ## Security Considerations 23 | 24 | ## Example usage 25 | 26 | C# 27 | 28 | ```CSharp 29 | // Walk all pages 30 | var pageIterator = new PageIterator(client, somePagedCollection, 31 | (user) => { 32 | Console.Debug(user.DisplayName); 33 | return true; 34 | } 35 | ); 36 | 37 | pageIterator.IterateAsync(); 38 | 39 | ``` 40 | 41 | C# 42 | 43 | ```CSharp 44 | // Search for something 45 | User foundUser = null; 46 | var pageIterator = new PageIterator(client, somePagedCollection, 47 | (user) => { 48 | if (Match(user)) { 49 | foundUser = user; 50 | return false; 51 | }; 52 | return true; 53 | } 54 | ); 55 | 56 | pageIterator.IterateAsync(); 57 | 58 | ``` 59 | 60 | C# 61 | 62 | ```CSharp 63 | // Create complete collection 64 | List allUsers = new List(); 65 | var pageIterator = new PageIterator(client, somePagedCollection, 66 | (user) => { 67 | allUsers.Add(user); 68 | return true; 69 | } 70 | ); 71 | 72 | pageIterator.IterateAsync(); 73 | 74 | ``` 75 | 76 | TypeScript 77 | 78 | ```typescript 79 | let response: PageCollection = await client.api("/me/messages").get(); 80 | let callback = (data) => { 81 | console.log(data); 82 | return true; 83 | }; 84 | let pageIterator = new PageIterator(client, response, callback); 85 | pageIterator.iterate(); 86 | 87 | // Example of passing headers with a PageIterator 88 | let response: PageCollection = await client 89 | .api("/me/events") 90 | .headers({ Prefer: 'outlook.timezone= "utc"' }) 91 | .get(); 92 | let callback = (data) => { 93 | console.log(data); 94 | return true; 95 | }; 96 | var requestOptions = { headers: { Prefer: 'outlook.timezone= "utc"' } }; 97 | let pageIterator = new PageIterator(client, response, callback, requestOptions); 98 | pageIterator.iterate(); 99 | ``` 100 | 101 | Go 102 | 103 | ```go 104 | pageIterator, err := msgraphgocore.NewPageIterator(graphResponse, *reqAdapter, ParsableCons) 105 | pageIterator.SetHeaders(map[string]string{"Content-Type": "application/json"}) 106 | 107 | err := pageIterator.Iterate(func(pageItem interface{}) bool { 108 | item := pageItem.(graph.User) 109 | res = append(res, *item.GetId()) 110 | return true 111 | }) 112 | 113 | pageIterator.Iterate(func(pageItem interface{}) bool { 114 | item := pageItem.(graph.User) 115 | res = append(res, *item.GetId()) 116 | 117 | return *item.GetId() != "2" // pauses when id == 2 118 | }) 119 | 120 | // resumes iteration from user with id 3 121 | pageIterator.Iterate(func(pageItem interface{}) bool { 122 | item := pageItem.(graph.User) 123 | res2 = append(res2, *item.GetId()) 124 | 125 | return true 126 | }) 127 | ``` 128 | 129 | PHP 130 | ```php 131 | $pageIterator = new PageIterator($graphResponse, $requestAdapter, $parsableConstructor); 132 | $pageIterator->setHeaders(["Content-Type" => "application/json"]); 133 | 134 | $items = []; 135 | $pageIterator->iterate(function ($pageItem) use (&$items) { 136 | $items []= $pageItem; 137 | return true; 138 | }) 139 | ``` 140 | PHP 141 | ```php 142 | $pageIterator = new PageIterator($graphResponse, $requestAdapter, $parsableConstructor); 143 | $pageIterator->setHeaders(["Content-Type" => "application/json"]); 144 | $items = []; 145 | 146 | $pageIterator->iterate(function ($pageItem) use (&$items) { 147 | return $pageItem->id !== '2'; 148 | }); 149 | 150 | // resumes iteration from user with id 3 151 | $pageIterator->iterate(function ($pageItem) use (&$items) { 152 | $items []= $pageItem; 153 | return true; 154 | }); 155 | ``` 156 | Python 157 | ``` 158 | page_iterator = PageIterator(response, client.request_adapter) 159 | page_iterator.set_headers({"Content-Type": "application/json"}) 160 | 161 | page_result = page_iterator.convert_to_page(response) 162 | page_result.odata_next_link = "https://graph.microsoft.com/v1.0/users?$top=2&$skip=2" 163 | 164 | 165 | page_iterator.iterate(lambda page_item: print(f" Page item {page_item}")) 166 | 167 | ``` 168 | ## Future Implementations 169 | 170 | - Support 4th requirement mentioned in this file. The progress can be tracked as follows - 171 | | SDK | Implementation Status | 172 | |-------------|----------------------| 173 | | C# | Implemented | 174 | | JAVA | Implemented | 175 | | PHP | Implemented | 176 | | Objective-C | - | 177 | | JavaScript | In Progress | 178 | | Go | Implemented | 179 | | Python | Implemented | --------------------------------------------------------------------------------