diff --git a/src/System Application/App/MicrosoftGraph/src/GraphClientImpl.Codeunit.al b/src/System Application/App/MicrosoftGraph/src/GraphClientImpl.Codeunit.al index c5bc81456f..846ced7919 100644 --- a/src/System Application/App/MicrosoftGraph/src/GraphClientImpl.Codeunit.al +++ b/src/System Application/App/MicrosoftGraph/src/GraphClientImpl.Codeunit.al @@ -53,7 +53,7 @@ codeunit 9351 "Graph Client Impl." GraphUriBuilder: Codeunit "Graph Uri Builder"; begin Clear(HttpResponseMessage); - GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters()); + GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters(), GraphOptionalParameters.GetODataQueryParameters()); GraphRequestHelper.SetRestClient(RestClient); HttpResponseMessage := GraphRequestHelper.Get(GraphUriBuilder, GraphOptionalParameters); exit(HttpResponseMessage.GetIsSuccessStatusCode()); @@ -63,7 +63,7 @@ codeunit 9351 "Graph Client Impl." var GraphUriBuilder: Codeunit "Graph Uri Builder"; begin - GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters()); + GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters(), GraphOptionalParameters.GetODataQueryParameters()); GraphRequestHelper.SetRestClient(RestClient); HttpResponseMessage := GraphRequestHelper.Post(GraphUriBuilder, GraphOptionalParameters, RequestHttpContent); exit(HttpResponseMessage.GetIsSuccessStatusCode()); @@ -73,7 +73,7 @@ codeunit 9351 "Graph Client Impl." var GraphUriBuilder: Codeunit "Graph Uri Builder"; begin - GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters()); + GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters(), GraphOptionalParameters.GetODataQueryParameters()); GraphRequestHelper.SetRestClient(RestClient); HttpResponseMessage := GraphRequestHelper.Patch(GraphUriBuilder, GraphOptionalParameters, RequestHttpContent); exit(HttpResponseMessage.GetIsSuccessStatusCode()); @@ -83,7 +83,7 @@ codeunit 9351 "Graph Client Impl." var GraphUriBuilder: Codeunit "Graph Uri Builder"; begin - GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters()); + GraphUriBuilder.Initialize(MicrosoftGraphBaseUrl, GraphAPIVersion, RelativeUriToResource, GraphOptionalParameters.GetQueryParameters(), GraphOptionalParameters.GetODataQueryParameters()); GraphRequestHelper.SetRestClient(RestClient); HttpResponseMessage := GraphRequestHelper.Put(GraphUriBuilder, GraphOptionalParameters, RequestHttpContent); exit(HttpResponseMessage.GetIsSuccessStatusCode()); diff --git a/src/System Application/App/MicrosoftGraph/src/GraphODataQueryParameter.Enum.al b/src/System Application/App/MicrosoftGraph/src/GraphODataQueryParameter.Enum.al new file mode 100644 index 0000000000..b989a4009c --- /dev/null +++ b/src/System Application/App/MicrosoftGraph/src/GraphODataQueryParameter.Enum.al @@ -0,0 +1,106 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.Integration.Graph; + +/// +/// A Microsoft Graph API operation might support one or more of the following OData system query options. +/// These query options are compatible with the OData V4 query language and are supported only in GET operations. +/// See: hhttps://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#odata-system-query-options +/// +enum 9352 "Graph OData Query Parameter" +{ + Access = Public; + Extensible = false; + + /// + /// Query Parameter '$count'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#count-parameter + /// + value(0; count) + { + Caption = '$count', Locked = true; + } + + /// + /// OData Query Parameter '$expand'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#expand-parameter + /// + value(10; expand) + { + Caption = '$expand', Locked = true; + } + + + /// + /// OData Query Parameter '$filter'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#filter-parameter + /// + value(20; filter) + { + Caption = '$filter', Locked = true; + } + + /// + /// OData Query Parameter '$format'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#format-parameter + /// + value(30; format) + { + Caption = '$format', Locked = true; + } + /// + /// OData Query Parameter '$orderBy'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#orderby-parameter + /// + value(40; orderby) + { + Caption = '$orderby', Locked = true; + } + + /// + /// OData Query Parameter '$search'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#search-parameter + /// + value(50; search) + { + Caption = '$search', Locked = true; + } + + /// + /// OData Query Parameter '$select'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#select-parameter + /// + value(60; select) + { + Caption = '$select', Locked = true; + } + + /// + /// OData Query Parameter '$skip'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#skip-parameter + /// + value(70; skip) + { + Caption = '$skip', Locked = true; + } + + /// + /// OData Query Parameter '$skipToken'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#skiptoken-parameter + /// + value(80; skiptoken) + { + Caption = '$skiptoken', Locked = true; + } + + /// + /// OData Query Parameter '$top'. + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#top-parameter + /// + value(90; top) + { + Caption = '$top', Locked = true; + } +} \ No newline at end of file diff --git a/src/System Application/App/MicrosoftGraph/src/GraphOptionalParameters.Codeunit.al b/src/System Application/App/MicrosoftGraph/src/GraphOptionalParameters.Codeunit.al index 1f10cf68a7..1826e31bfa 100644 --- a/src/System Application/App/MicrosoftGraph/src/GraphOptionalParameters.Codeunit.al +++ b/src/System Application/App/MicrosoftGraph/src/GraphOptionalParameters.Codeunit.al @@ -13,6 +13,9 @@ codeunit 9353 "Graph Optional Parameters" InherentEntitlements = X; InherentPermissions = X; + var + GraphOptionalParametersImpl: Codeunit "Graph Optional Parameters Impl"; + #region Headers /// @@ -21,7 +24,7 @@ codeunit 9353 "Graph Optional Parameters" /// Text value specifying the HttpHeader value procedure SetIfMatch("Value": Text) begin - SetRequestHeader('IF-Match', "Value"); + SetRequestHeader(Enum::"Graph Request Header"::"If-Match", "Value"); end; /// @@ -30,7 +33,7 @@ codeunit 9353 "Graph Optional Parameters" /// Text value specifying the HttpHeader value procedure SetIfNoneMatchRequestHeader("Value": Text) begin - SetRequestHeader('If-None-Match', "Value"); + SetRequestHeader(Enum::"Graph Request Header"::"If-None-Match", "Value"); end; /// @@ -39,7 +42,7 @@ codeunit 9353 "Graph Optional Parameters" /// Text value specifying the HttpHeader value procedure SetPreferRequestHeader("Value": Text) begin - SetRequestHeader('Prefer', "Value"); + SetRequestHeader(Enum::"Graph Request Header"::Prefer, "Value"); end; /// @@ -48,18 +51,22 @@ codeunit 9353 "Graph Optional Parameters" /// Text value specifying the HttpHeader value procedure SetConsistencyLevelRequestHeader("Value": Text) begin - SetRequestHeader('ConsistencyLevel', "Value"); + SetRequestHeader(Enum::"Graph Request Header"::ConsistencyLevel, "Value"); end; - local procedure SetRequestHeader(Header: Text; HeaderValue: Text) + /// + /// Sets the value for a HttpHeader for a request. + /// + /// The Request Header + /// Text value specifying the HttpHeader value + procedure SetRequestHeader(GraphRequestHeader: Enum "Graph Request Header"; HeaderValue: Text) begin - RequestHeaders.Remove(Header); - RequestHeaders.Add(Header, HeaderValue); + GraphOptionalParametersImpl.SetRequestHeader(GraphRequestHeader, HeaderValue); end; internal procedure GetRequestHeaders(): Dictionary of [Text, Text] begin - exit(RequestHeaders); + exit(GraphOptionalParametersImpl.GetRequestHeaders()); end; #endregion @@ -72,23 +79,32 @@ codeunit 9353 "Graph Optional Parameters" /// Enum "Graph ConflictBehavior" value specifying the HttpHeader value procedure SetMicrosftGraphConflictBehavior(GraphConflictBehavior: Enum "Graph ConflictBehavior") begin - SetQueryParameter('@microsoft.graph.conflictBehavior', Format(GraphConflictBehavior)); + GraphOptionalParametersImpl.SetMicrosftGraphConflictBehavior(GraphConflictBehavior); end; + internal procedure GetQueryParameters(): Dictionary of [Text, Text] + begin + exit(GraphOptionalParametersImpl.GetQueryParameters()); + end; + #endregion - local procedure SetQueryParameter(Header: Text; HeaderValue: Text) + #region ODataQueryParameters + + + /// + /// Sets the value for an OData Query Parameter + /// see: https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#odata-system-query-options + /// + /// The OData query parameter + /// Text value specifying the query parameter + procedure SetODataQueryParameter(GraphODataQueryParameter: Enum "Graph OData Query Parameter"; ODataQueryParameterValue: Text) begin - QueryParameters.Remove(Header); - QueryParameters.Add(Header, HeaderValue); + GraphOptionalParametersImpl.SetODataQueryParameter(GraphODataQueryParameter, ODataQueryParameterValue); end; - internal procedure GetQueryParameters(): Dictionary of [Text, Text] + internal procedure GetODataQueryParameters(): Dictionary of [Text, Text] begin - exit(QueryParameters); + exit(GraphOptionalParametersImpl.GetODataQueryParameters()); end; #endregion - - var - QueryParameters: Dictionary of [Text, Text]; - RequestHeaders: Dictionary of [Text, Text]; } \ No newline at end of file diff --git a/src/System Application/App/MicrosoftGraph/src/GraphOptionalParametersImpl.Codeunit.al b/src/System Application/App/MicrosoftGraph/src/GraphOptionalParametersImpl.Codeunit.al new file mode 100644 index 0000000000..58b6aa7f17 --- /dev/null +++ b/src/System Application/App/MicrosoftGraph/src/GraphOptionalParametersImpl.Codeunit.al @@ -0,0 +1,77 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.Integration.Graph; + + +codeunit 9358 "Graph Optional Parameters Impl" +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + #region Headers + procedure SetRequestHeader(GraphRequestHeader: Enum "Graph Request Header"; HeaderValue: Text) + begin + SetRequestHeader(Format(GraphRequestHeader), HeaderValue); + end; + + local procedure SetRequestHeader(Header: Text; HeaderValue: Text) + begin + RequestHeaders.Remove(Header); + RequestHeaders.Add(Header, HeaderValue); + end; + + internal procedure GetRequestHeaders(): Dictionary of [Text, Text] + begin + exit(RequestHeaders); + end; + + #endregion + + #region Parameters + + procedure SetMicrosftGraphConflictBehavior(GraphConflictBehavior: Enum "Graph ConflictBehavior") + begin + SetQueryParameter('@microsoft.graph.conflictBehavior', Format(GraphConflictBehavior)); + end; + + + local procedure SetQueryParameter(Header: Text; HeaderValue: Text) + begin + QueryParameters.Remove(Header); + QueryParameters.Add(Header, HeaderValue); + end; + + procedure GetQueryParameters(): Dictionary of [Text, Text] + begin + exit(QueryParameters); + end; + #endregion + + #region ODataQueryParameters + + procedure SetODataQueryParameter(GraphODataQueryParameter: Enum "Graph OData Query Parameter"; ODataQueryParameterValue: Text) + begin + SetODataQueryParameter(Format(GraphODataQueryParameter), ODataQueryParameterValue); + end; + + local procedure SetODataQueryParameter(ODataQueryParameterKey: Text; ODataQueryParameterValue: Text) + begin + ODataQueryParameters.Remove(ODataQueryParameterKey); + ODataQueryParameters.Add(ODataQueryParameterKey, ODataQueryParameterValue); + end; + + procedure GetODataQueryParameters(): Dictionary of [Text, Text] + begin + exit(ODataQueryParameters); + end; + + #endregion + + var + QueryParameters: Dictionary of [Text, Text]; + ODataQueryParameters: Dictionary of [Text, Text]; + RequestHeaders: Dictionary of [Text, Text]; +} \ No newline at end of file diff --git a/src/System Application/App/MicrosoftGraph/src/GraphRequestHeader.Enum.al b/src/System Application/App/MicrosoftGraph/src/GraphRequestHeader.Enum.al new file mode 100644 index 0000000000..a1a420bd9a --- /dev/null +++ b/src/System Application/App/MicrosoftGraph/src/GraphRequestHeader.Enum.al @@ -0,0 +1,46 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.Integration.Graph; + +/// +/// The supported request header for the Microsoft Graph API +/// +enum 9353 "Graph Request Header" +{ + Access = Public; + Extensible = false; + + /// + /// If-Match Request Header + /// + value(0; "If-Match") + { + Caption = 'If-Match', Locked = true; + } + + /// + /// If-None-Match Request Header + /// + value(10; "If-None-Match") + { + Caption = 'If-None-Match', Locked = true; + } + + /// + /// Prefer Request Header + /// + value(20; Prefer) + { + Caption = 'Prefer', Locked = true; + } + + /// + /// ConsistencyLevel Request Header + /// + value(30; ConsistencyLevel) + { + Caption = 'ConsistencyLevel', Locked = true; + } +} \ No newline at end of file diff --git a/src/System Application/App/MicrosoftGraph/src/helper/GraphUriBuilder.Codeunit.al b/src/System Application/App/MicrosoftGraph/src/helper/GraphUriBuilder.Codeunit.al index 67e21e01ad..91706f7601 100644 --- a/src/System Application/App/MicrosoftGraph/src/helper/GraphUriBuilder.Codeunit.al +++ b/src/System Application/App/MicrosoftGraph/src/helper/GraphUriBuilder.Codeunit.al @@ -21,11 +21,12 @@ codeunit 9352 "Graph Uri Builder" procedure Initialize(MicrosoftGraphBaseUrl: Text; NewGraphAPIVersion: Enum "Graph API Version"; RelativeUriToResource: Text) var QueryParameters: Dictionary of [Text, Text]; + ODataQueryParameters: Dictionary of [Text, Text]; begin - Initialize(MicrosoftGraphBaseUrl, NewGraphAPIVersion, RelativeUriToResource, QueryParameters); + Initialize(MicrosoftGraphBaseUrl, NewGraphAPIVersion, RelativeUriToResource, QueryParameters, ODataQueryParameters); end; - procedure Initialize(MicrosoftGraphBaseUrl: Text; NewGraphAPIVersion: Enum "Graph API Version"; RelativeUriToResource: Text; QueryParameters: Dictionary of [Text, Text]) + procedure Initialize(MicrosoftGraphBaseUrl: Text; NewGraphAPIVersion: Enum "Graph API Version"; RelativeUriToResource: Text; QueryParameters: Dictionary of [Text, Text]; ODataQueryParameters: Dictionary of [Text, Text]) var UriBuilder: Codeunit "Uri Builder"; BaseUri: Text; @@ -42,6 +43,9 @@ codeunit 9352 "Graph Uri Builder" UriBuilder.Init(Uri.GetAbsoluteUri()); foreach QueryParameterKey in QueryParameters.Keys() do UriBuilder.AddQueryParameter(QueryParameterKey, QueryParameters.Get(QueryParameterKey)); + foreach QueryParameterKey in ODataQueryParameters.Keys() do + UriBuilder.AddODataQueryParameter(QueryParameterKey, ODataQueryParameters.Get(QueryParameterKey)); + UriBuilder.GetUri(Uri); end; procedure GetUri(): Text diff --git a/src/System Application/Test/MicrosoftGraph/app.json b/src/System Application/Test/MicrosoftGraph/app.json index 54f059f9f6..f35f47f091 100644 --- a/src/System Application/Test/MicrosoftGraph/app.json +++ b/src/System Application/Test/MicrosoftGraph/app.json @@ -11,30 +11,36 @@ "url": "https://go.microsoft.com/fwlink/?LinkId=724011", "logo": "", "dependencies": [ - { - "id": "812b339d-a9db-4a6e-84e4-fe35cbef0c44", - "name": "Rest Client", - "publisher": "Microsoft", - "version": "25.0.0.0" - }, - { - "id": "6d72c93d-164a-494c-8d65-24d7f41d7b61", - "name": "Microsoft Graph", - "publisher": "Microsoft", - "version": "25.0.0.0" - }, - { - "id": "e31ad830-3d46-472e-afeb-1d3d35247943", - "name": "BLOB Storage", - "publisher": "Microsoft", - "version": "25.0.0.0" - }, - { - "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14", - "name": "Library Assert", - "publisher": "Microsoft", - "version": "25.0.0.0" - } + { + "id": "812b339d-a9db-4a6e-84e4-fe35cbef0c44", + "name": "Rest Client", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "6d72c93d-164a-494c-8d65-24d7f41d7b61", + "name": "Microsoft Graph", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "e31ad830-3d46-472e-afeb-1d3d35247943", + "name": "BLOB Storage", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "1b2efb4b-8c44-4d74-a56f-60646645bb21", + "name": "URI", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14", + "name": "Library Assert", + "publisher": "Microsoft", + "version": "25.0.0.0" + } ], "screenshots": [ diff --git a/src/System Application/Test/MicrosoftGraph/src/GraphClientTest.Codeunit.al b/src/System Application/Test/MicrosoftGraph/src/GraphClientTest.Codeunit.al index d766a5183d..72db540ee9 100644 --- a/src/System Application/Test/MicrosoftGraph/src/GraphClientTest.Codeunit.al +++ b/src/System Application/Test/MicrosoftGraph/src/GraphClientTest.Codeunit.al @@ -24,9 +24,9 @@ codeunit 135140 "Graph Client Test" [Test] procedure AuthTriggeredTest() var - HttpResponseMessage: Codeunit "Http Response Message"; GraphAuthSpy: Codeunit "Graph Auth. Spy"; GraphClient: Codeunit "Graph Client"; + HttpResponseMessage: Codeunit "Http Response Message"; MockHttpClientHandler: Codeunit "Mock Http Client Handler"; TempBlob: Codeunit "Temp Blob"; ResponseInStream: InStream; @@ -44,34 +44,62 @@ codeunit 135140 "Graph Client Test" [Test] procedure RequestUriTest() var + GraphAuthSpy: Codeunit "Graph Auth. Spy"; + GraphClient: Codeunit "Graph Client"; HttpRequestMessage: Codeunit "Http Request Message"; HttpResponseMessage: Codeunit "Http Response Message"; + MockHttpClientHandler: Codeunit "Mock Http Client Handler"; + TempBlob: Codeunit "Temp Blob"; + ResponseInStream: InStream; + begin + GraphClient.Initialize(Enum::"Graph API Version"::"v1.0", GraphAuthSpy, MockHttpClientHandler); + ResponseInStream := TempBlob.CreateInStream(); + + // [WHEN] When Get Method is called + GraphClient.Get('groups', HttpResponseMessage); + + // [THEN] Verify request uri is build correct + MockHttpClientHandler.GetHttpRequestMessage(HttpRequestMessage); + LibraryAssert.AreEqual('https://graph.microsoft.com/v1.0/groups', HttpRequestMessage.GetRequestUri(), 'Incorrect Request URI.'); + end; + + [Test] + procedure RequestUriWithODataQueryParameterTest() + var GraphAuthSpy: Codeunit "Graph Auth. Spy"; GraphClient: Codeunit "Graph Client"; + GraphOptionalParameters: Codeunit "Graph Optional Parameters"; + HttpRequestMessage: Codeunit "Http Request Message"; + HttpResponseMessage: Codeunit "Http Response Message"; MockHttpClientHandler: Codeunit "Mock Http Client Handler"; TempBlob: Codeunit "Temp Blob"; + Uri: Codeunit Uri; ResponseInStream: InStream; begin GraphClient.Initialize(Enum::"Graph API Version"::"v1.0", GraphAuthSpy, MockHttpClientHandler); ResponseInStream := TempBlob.CreateInStream(); + // [GIVEN] Optional Parameters with OData Query Parameter set + GraphOptionalParameters.SetODataQueryParameter(Enum::"Graph OData Query Parameter"::format, 'json'); + // [WHEN] When Get Method is called - GraphClient.Get('groups', HttpResponseMessage); + GraphClient.Get('groups', GraphOptionalParameters, HttpResponseMessage); // [THEN] Verify request uri is build correct MockHttpClientHandler.GetHttpRequestMessage(HttpRequestMessage); - LibraryAssert.AreEqual(HttpRequestMessage.GetRequestUri(), 'https://graph.microsoft.com/v1.0/groups', 'Incorrect Request URI.'); + Uri.Init(HttpRequestMessage.GetRequestUri()); + LibraryAssert.AreEqual('?$format=json', Uri.GetQuery(), 'Incorrect query string.'); end; [Test] procedure ResponseBodyTest() var + GraphAuthSpy: Codeunit "Graph Auth. Spy"; + GraphClient: Codeunit "Graph Client"; HttpContent: Codeunit "Http Content"; MockHttpContent: Codeunit "Http Content"; HttpResponseMessage: Codeunit "Http Response Message"; MockHttpResponseMessage: Codeunit "Http Response Message"; - GraphAuthSpy: Codeunit "Graph Auth. Spy"; - GraphClient: Codeunit "Graph Client"; MockHttpClientHandler: Codeunit "Mock Http Client Handler"; TempBlob: Codeunit "Temp Blob"; ResponseInStream: InStream;