├── .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 | 
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 |
--------------------------------------------------------------------------------