diff --git a/content/docs/analyzers/ApplicationCop/AC0033.md b/content/docs/analyzers/ApplicationCop/AC0033.md index 5f37523..56abc46 100644 --- a/content/docs/analyzers/ApplicationCop/AC0033.md +++ b/content/docs/analyzers/ApplicationCop/AC0033.md @@ -1,23 +1,29 @@ +++ -title = 'Rest Client requires an Http Client Handler implementation' +title = 'Send Rest Client requests through an Http Client Handler in this app' linkTitle = 'AC0033' [params] id = 'AC0033' - severity = 'Warning' + severity = 'Hidden' category = 'Design' codeAction = false ignoreObsolete = true +++ -`Codeunit "Rest Client"` does not send requests itself. It passes every request to an implementation of the `"Http Client Handler"` interface. When no handler is given, it uses the System Application's own `"Http Client Handler"` codeunit, which calls `HttpClient.Send`. The outgoing call then belongs to the System Application, not to the app that built the request. +Enable this rule when you want each app to send its HTTP calls from its own extension, regardless of the dependency structure. -In a sandbox, allowing HTTP client requests for the System Application therefore allows them for every extension that uses the Rest Client. The outgoing web service request telemetry for those calls goes to Microsoft's telemetry instead of the app publisher's. Tests cannot substitute a response either, because the handler is the point where a test replaces the real call. Implement `"Http Client Handler"` in the app and pass it to the Rest Client. +`Codeunit "Rest Client"` does not send requests itself. It passes every request to an implementation of the `"Http Client Handler"` interface. When no handler is given, it uses the System Application's own `"Http Client Handler"` codeunit, which calls `HttpClient.Send`. When the app passes a handler from a dependency app, that app's codeunit sends the request. Either way, the HTTP call is made from another extension, not from the app that built the request. + +This matters for the **Allow HttpClient Requests** setting on the **Extension Settings** page. The setting "specifies whether the runtime should allow this extension to make HTTP requests through the HttpClient data type when running in a non-production environment." Microsoft Learn states that outbound HTTP calls are blocked by default and must be approved for each extension, including library apps. A blocked call fails with *The request was blocked by the runtime to prevent accidental use of production services.* When a production environment is copied to a sandbox, outbound HTTP calls are blocked in the copy until the setting is turned on for each extension. + +When the System Application's default handler sends the request, the setting of the app that built the request does not decide whether the call goes out. Turning the setting on for the System Application allows the calls of every app that relies on the default handler. Implement `"Http Client Handler"` in the app and pass it to the Rest Client, so the app's own setting governs its calls. + +The handler in the app has two more benefits. The outgoing web service request telemetry for the call belongs to the app's own extension. Tests can replace the handler to substitute a response instead of calling the real service. ### Example {{< highlight al "hl_lines=1" >}} -codeunit 50100 "Exchange Rate Sync" // Rest Client requires an Http Client Handler implementation [AC0033] +codeunit 50100 "Exchange Rate Sync" // Send Rest Client requests through an Http Client Handler in this app [AC0033] { procedure GetRates(CurrencyCode: Code[10]): JsonToken var @@ -76,32 +82,27 @@ RestClient := RestClient.Create(HttpHandler); Test codeunits (`Subtype = Test` or `TestRunner`) and obsolete objects are not reported. -Only codeunits in the app being compiled count as an implementation. When the handler lives in a dependency app from the same publisher, such as a shared foundation app, the calls are still attributed to that publisher, but AC0033 does not see the handler. Suppress the diagnostic on the object: +### Why the rule is disabled by default -{{< highlight al >}} -// The "Http Client Handler" implementation is provided by the foundation app, -// a dependency from the same publisher; AC0033 only sees codeunits in this app. -#pragma warning disable AC0033 -codeunit 50100 "Exchange Rate Sync" -#pragma warning restore AC0033 +A shared base app that sends the HTTP calls for the apps that depend on it is a valid design. In that design the base app's setting governs the calls of all its dependent apps. AC0033 only counts handler implementations in the app being compiled, so it does not see a handler in a dependency and would report every dependent app. + +### Enable the rule + +Add AC0033 to your project's ruleset file. Use `Info` to show it as a suggestion, or `Warning` to make it stand out: + +```json { - procedure GetRates(CurrencyCode: Code[10]): JsonToken - var - RestClient: Codeunit "Rest Client"; - FoundationHttpHandler: Codeunit "Foundation Http Handler"; - begin - RestClient.Initialize(FoundationHttpHandler); - exit(RestClient.GetAsJson('https://api.example.com/rates/' + CurrencyCode)); - end; + "id": "AC0033", + "action": "Warning", + "justification": "Each app sends its HTTP calls from its own extension" } -{{< /highlight >}} - -For an app that relies on a shared handler throughout, disable the rule in the ruleset instead. +``` [AC0034](../ac0034/) applies the same check to `Codeunit Telemetry` and `Codeunit "Feature Telemetry"`. ### See also +- [Call external services with the HttpClient data type](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-httpclient) on Microsoft Learn - [Codeunit "Rest Client"](https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/codeunit/system.restclient.rest-client) on Microsoft Learn - [Interface "Http Client Handler"](https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/interface/system.restclient.http-client-handler) on Microsoft Learn - [Prefer the Rest Client module over the native HttpClient](https://github.com/ALCops/Analyzers/discussions/405) on GitHub Discussions diff --git a/content/docs/analyzers/ApplicationCop/_index.md b/content/docs/analyzers/ApplicationCop/_index.md index f4f2382..987851a 100644 --- a/content/docs/analyzers/ApplicationCop/_index.md +++ b/content/docs/analyzers/ApplicationCop/_index.md @@ -42,7 +42,7 @@ ApplicationCop inspects how objects are modeled: tables, fields, pages, enums, l | [AC0030](ac0030/) | Use return value for better error handling | Info | ✓ | | | [AC0031](ac0031/) | Table data access requires explicit object permissions | Info | ✓ | ✓ | | [AC0032](ac0032/) | Unused permission declared | Info | ✓ | ✓ | -| [AC0033](ac0033/) | Rest Client requires an Http Client Handler implementation | Warning | ✓ | | +| [AC0033](ac0033/) | Send Rest Client requests through an Http Client Handler in this app | Info | — | | | [AC0034](ac0034/) | Telemetry requires a Telemetry Logger implementation | Warning | ✓ | | **Note:** Rules marked with "—" in the Enabled column are disabled by default and must be explicitly enabled in your project's ruleset file.