From fde6cf8aa92e55bf9ad4e342e5efa215a7709cc2 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 27 Sep 2026 19:56:32 +0200 Subject: [PATCH 1/3] test(AC0035): add wiring, analyzer stub and fixtures for RestClientInitializeWithHttpClientHandler Co-Authored-By: Claude Opus 5.5 --- ...reInitializeInOneProcedureUsedInAnother.al | 68 ++++++++++++++ .../GlobalUsedWithoutInitialization.al | 63 +++++++++++++ .../HasDiagnostic/LocalBareInitialize.al | 63 +++++++++++++ .../LocalCreateWithAuthenticationAssigned.al | 71 +++++++++++++++ .../LocalCreateWithoutHandlerAssigned.al | 63 +++++++++++++ .../LocalGoodThenBareInitialize.al | 73 +++++++++++++++ .../LocalInitializeWithAuthenticationOnly.al | 71 +++++++++++++++ .../LocalInitializeWithDefaultHandler.al | 71 +++++++++++++++ ...lizeWithDefaultHandlerAndAuthentication.al | 79 ++++++++++++++++ .../LocalInitializeWithoutParentheses.al | 63 +++++++++++++ .../HasDiagnostic/LocalNoInitialization.al | 62 +++++++++++++ ...ssedToSameObjectProcedureBareInitialize.al | 68 ++++++++++++++ ...edToSameObjectProcedureNeverInitialized.al | 67 ++++++++++++++ .../NamespacedFullyQualifiedType.al | 65 ++++++++++++++ .../HasDiagnostic/PageActionTriggerLocal.al | 83 +++++++++++++++++ .../HasDiagnostic/TableTriggerLocal.al | 69 ++++++++++++++ .../ThisReceiverProcedureCall.al | 67 ++++++++++++++ .../HasDiagnostic/TwoLocalsTwoDiagnostics.al | 65 ++++++++++++++ .../AssignedFromProcedureReturn.al | 67 ++++++++++++++ .../NoDiagnostic/AssignedToGlobal.al | 66 ++++++++++++++ .../NoDiagnostic/ClearThenGet.al | 63 +++++++++++++ .../CreateResultPassedAsArgument.al | 78 ++++++++++++++++ .../NoDiagnostic/CreateWithHandlerAssigned.al | 71 +++++++++++++++ .../NoDiagnostic/DeclaredButUnused.al | 61 +++++++++++++ .../NoDiagnostic/ExitWithRestClient.al | 63 +++++++++++++ .../NoDiagnostic/FactoryReturnedHandler.al | 81 +++++++++++++++++ .../GlobalInitializedInSetupProcedure.al | 77 ++++++++++++++++ .../GlobalPassedToOtherCodeunit.al | 73 +++++++++++++++ .../HandlerReceivedAsParameter.al | 63 +++++++++++++ .../InterfaceTypedHandlerLocal.al | 73 +++++++++++++++ .../NoDiagnostic/ObsoletePendingObject.al | 66 ++++++++++++++ .../OwnCodeunitNamedHttpClientHandler.al | 71 +++++++++++++++ .../OwnHandlerAndAuthentication.al | 79 ++++++++++++++++ .../NoDiagnostic/OwnHandlerCodeunit.al | 71 +++++++++++++++ .../NoDiagnostic/PassedToEventPublisher.al | 68 ++++++++++++++ .../NoDiagnostic/PassedToInterfaceMethod.al | 76 ++++++++++++++++ .../PassedToOtherObjectProcedure.al | 71 +++++++++++++++ ...meObjectProcedureInitializedWithHandler.al | 77 ++++++++++++++++ .../RecursiveSameObjectProcedures.al | 83 +++++++++++++++++ .../RestClientIdMatchNameMismatch.al | 21 +++++ .../RestClientNameMatchIdMismatch.al | 21 +++++ .../NoDiagnostic/TestCodeunit.al | 65 ++++++++++++++ .../NoDiagnostic/TestRunnerCodeunit.al | 65 ++++++++++++++ .../TwoLevelFollowingEndsInExternalCall.al | 82 +++++++++++++++++ .../NoDiagnostic/VarParameterOnly.al | 61 +++++++++++++ ...stClientInitializeWithHttpClientHandler.cs | 90 +++++++++++++++++++ .../ALCops.ApplicationCopAnalyzers.resx | 9 ++ ...stClientInitializeWithHttpClientHandler.cs | 30 +++++++ .../DiagnosticDescriptors.cs | 10 +++ src/ALCops.ApplicationCop/DiagnosticIds.cs | 1 + 50 files changed, 3184 insertions(+) create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalBareInitializeInOneProcedureUsedInAnother.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalUsedWithoutInitialization.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalBareInitialize.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithAuthenticationAssigned.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithoutHandlerAssigned.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalGoodThenBareInitialize.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithAuthenticationOnly.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandler.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandlerAndAuthentication.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithoutParentheses.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalNoInitialization.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureBareInitialize.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureNeverInitialized.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/NamespacedFullyQualifiedType.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/PageActionTriggerLocal.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TableTriggerLocal.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/ThisReceiverProcedureCall.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TwoLocalsTwoDiagnostics.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedFromProcedureReturn.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedToGlobal.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ClearThenGet.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateResultPassedAsArgument.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateWithHandlerAssigned.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/DeclaredButUnused.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ExitWithRestClient.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/FactoryReturnedHandler.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalInitializedInSetupProcedure.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalPassedToOtherCodeunit.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/HandlerReceivedAsParameter.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/InterfaceTypedHandlerLocal.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ObsoletePendingObject.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnCodeunitNamedHttpClientHandler.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerAndAuthentication.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerCodeunit.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToEventPublisher.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToInterfaceMethod.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToOtherObjectProcedure.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToSameObjectProcedureInitializedWithHandler.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RecursiveSameObjectProcedures.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientIdMatchNameMismatch.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientNameMatchIdMismatch.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestCodeunit.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestRunnerCodeunit.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TwoLevelFollowingEndsInExternalCall.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/VarParameterOnly.al create mode 100644 src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/RestClientInitializeWithHttpClientHandler.cs create mode 100644 src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalBareInitializeInOneProcedureUsedInAnother.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalBareInitializeInOneProcedureUsedInAnother.al new file mode 100644 index 00000000..798e4543 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalBareInitializeInOneProcedureUsedInAnother.al @@ -0,0 +1,68 @@ +codeunit 50100 MyCodeunit +{ + var + RestClient: Codeunit "Rest Client"; + + procedure Setup() + begin + [|RestClient.Initialize()|]; + end; + + procedure DoSomething() + begin + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalUsedWithoutInitialization.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalUsedWithoutInitialization.al new file mode 100644 index 00000000..999512ab --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/GlobalUsedWithoutInitialization.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + var + [|RestClient|]: Codeunit "Rest Client"; + + procedure DoSomething() + begin + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalBareInitialize.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalBareInitialize.al new file mode 100644 index 00000000..da197adc --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalBareInitialize.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithAuthenticationAssigned.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithAuthenticationAssigned.al new file mode 100644 index 00000000..a6f67b9f --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithAuthenticationAssigned.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + Auth: Codeunit "Http Authentication Anonymous"; + begin + RestClient := [|RestClient.Create(Auth)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 2358 "Http Authentication Anonymous" implements "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithoutHandlerAssigned.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithoutHandlerAssigned.al new file mode 100644 index 00000000..4ab2e01c --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalCreateWithoutHandlerAssigned.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + begin + RestClient := [|RestClient.Create()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalGoodThenBareInitialize.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalGoodThenBareInitialize.al new file mode 100644 index 00000000..20635f01 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalGoodThenBareInitialize.al @@ -0,0 +1,73 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + MyHandler: Codeunit MyHttpClientHandler; + begin + RestClient.Initialize(MyHandler); + RestClient.Get('https://example.com'); + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithAuthenticationOnly.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithAuthenticationOnly.al new file mode 100644 index 00000000..9570f0a4 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithAuthenticationOnly.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + Auth: Codeunit "Http Authentication Anonymous"; + begin + [|RestClient.Initialize(Auth)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 2358 "Http Authentication Anonymous" implements "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandler.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandler.al new file mode 100644 index 00000000..c22f8c19 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandler.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + HttpClientHandler: Codeunit "Http Client Handler"; + begin + [|RestClient.Initialize(HttpClientHandler)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 2360 "Http Client Handler" implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandlerAndAuthentication.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandlerAndAuthentication.al new file mode 100644 index 00000000..139fa913 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithDefaultHandlerAndAuthentication.al @@ -0,0 +1,79 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + HttpClientHandler: Codeunit "Http Client Handler"; + Auth: Codeunit "Http Authentication Anonymous"; + begin + [|RestClient.Initialize(HttpClientHandler, Auth)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 2360 "Http Client Handler" implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} + +codeunit 2358 "Http Authentication Anonymous" implements "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithoutParentheses.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithoutParentheses.al new file mode 100644 index 00000000..69af3345 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalInitializeWithoutParentheses.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit "Rest Client"; + begin + [|RestClient.Initialize|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalNoInitialization.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalNoInitialization.al new file mode 100644 index 00000000..4046d0ec --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalNoInitialization.al @@ -0,0 +1,62 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureBareInitialize.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureBareInitialize.al new file mode 100644 index 00000000..08637e01 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureBareInitialize.al @@ -0,0 +1,68 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + Send(RestClient); + end; + + local procedure Send(RC: Codeunit "Rest Client") + begin + RC.Initialize(); + RC.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureNeverInitialized.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureNeverInitialized.al new file mode 100644 index 00000000..6e9ca9a4 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/LocalPassedToSameObjectProcedureNeverInitialized.al @@ -0,0 +1,67 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + Send(RestClient); + end; + + local procedure Send(var RC: Codeunit "Rest Client") + begin + RC.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/NamespacedFullyQualifiedType.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/NamespacedFullyQualifiedType.al new file mode 100644 index 00000000..4b18143d --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/NamespacedFullyQualifiedType.al @@ -0,0 +1,65 @@ +namespace MyPublisher.MyExtension.MyAppDomain; + +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + RestClient: Codeunit MyPublisher.MyExtension.MyAppDomain."Rest Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/PageActionTriggerLocal.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/PageActionTriggerLocal.al new file mode 100644 index 00000000..81414f27 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/PageActionTriggerLocal.al @@ -0,0 +1,83 @@ +page 50100 MyPage +{ + SourceTable = MyTable; + + actions + { + area(Processing) + { + action(MyAction) + { + trigger OnAction() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + RestClient.Get('https://example.com'); + end; + } + } + } +} + +table 50100 MyTable +{ + fields + { + field(1; MyField; Integer) + { + } + } +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TableTriggerLocal.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TableTriggerLocal.al new file mode 100644 index 00000000..5143a6f2 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TableTriggerLocal.al @@ -0,0 +1,69 @@ +table 50100 MyTable +{ + fields + { + field(1; MyField; Integer) + { + } + } + + trigger OnInsert() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/ThisReceiverProcedureCall.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/ThisReceiverProcedureCall.al new file mode 100644 index 00000000..ae36e953 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/ThisReceiverProcedureCall.al @@ -0,0 +1,67 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + this.Send(RestClient); + end; + + local procedure Send(var RC: Codeunit "Rest Client") + begin + RC.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TwoLocalsTwoDiagnostics.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TwoLocalsTwoDiagnostics.al new file mode 100644 index 00000000..6bb23f07 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/HasDiagnostic/TwoLocalsTwoDiagnostics.al @@ -0,0 +1,65 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClientA|]: Codeunit "Rest Client"; + RestClientB: Codeunit "Rest Client"; + begin + RestClientA.Get('https://example.com'); + [|RestClientB.Initialize()|]; + RestClientB.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedFromProcedureReturn.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedFromProcedureReturn.al new file mode 100644 index 00000000..c6afb00d --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedFromProcedureReturn.al @@ -0,0 +1,67 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + RestClient := GetClient(); + RestClient.Get('https://example.com'); + end; + + local procedure GetClient(): Codeunit "Rest Client" + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedToGlobal.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedToGlobal.al new file mode 100644 index 00000000..83d8c6bb --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/AssignedToGlobal.al @@ -0,0 +1,66 @@ +codeunit 50100 MyCodeunit +{ + var + GlobalRestClient: Codeunit "Rest Client"; + + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + GlobalRestClient := RestClient; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ClearThenGet.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ClearThenGet.al new file mode 100644 index 00000000..2dd89fc2 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ClearThenGet.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + Clear(RestClient); + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateResultPassedAsArgument.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateResultPassedAsArgument.al new file mode 100644 index 00000000..d5de5e65 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateResultPassedAsArgument.al @@ -0,0 +1,78 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + MyHandler: Codeunit MyHttpClientHandler; + Helper: Codeunit Helper; + begin + Helper.Use([|RestClient.Create(MyHandler)|]); + end; +} + +codeunit 50102 Helper +{ + procedure Use(RC: Codeunit "Rest Client") + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateWithHandlerAssigned.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateWithHandlerAssigned.al new file mode 100644 index 00000000..300de039 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/CreateWithHandlerAssigned.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + MyHandler: Codeunit MyHttpClientHandler; + begin + RestClient := [|RestClient.Create(MyHandler)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/DeclaredButUnused.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/DeclaredButUnused.al new file mode 100644 index 00000000..05a1bd21 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/DeclaredButUnused.al @@ -0,0 +1,61 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ExitWithRestClient.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ExitWithRestClient.al new file mode 100644 index 00000000..533c2c4d --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ExitWithRestClient.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + procedure GetClient(): Codeunit "Rest Client" + var + [|RestClient|]: Codeunit "Rest Client"; + begin + RestClient.SetBaseAddress('https://example.com'); + exit(RestClient); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/FactoryReturnedHandler.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/FactoryReturnedHandler.al new file mode 100644 index 00000000..597eaf8a --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/FactoryReturnedHandler.al @@ -0,0 +1,81 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + Factory: Codeunit Factory; + begin + [|RestClient.Initialize(Factory.GetHttpClientHandler())|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 50102 Factory +{ + procedure GetHttpClientHandler(): Interface "Http Client Handler" + var + MyHandler: Codeunit MyHttpClientHandler; + begin + exit(MyHandler); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalInitializedInSetupProcedure.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalInitializedInSetupProcedure.al new file mode 100644 index 00000000..201f049e --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalInitializedInSetupProcedure.al @@ -0,0 +1,77 @@ +codeunit 50100 MyCodeunit +{ + var + [|RestClient|]: Codeunit "Rest Client"; + + procedure Setup() + var + MyHandler: Codeunit MyHttpClientHandler; + begin + [|RestClient.Initialize(MyHandler)|]; + end; + + procedure DoSomething() + begin + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalPassedToOtherCodeunit.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalPassedToOtherCodeunit.al new file mode 100644 index 00000000..b6664e4d --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/GlobalPassedToOtherCodeunit.al @@ -0,0 +1,73 @@ +codeunit 50100 MyCodeunit +{ + var + [|RestClient|]: Codeunit "Rest Client"; + + procedure DoSomething() + var + Helper: Codeunit Helper; + begin + Helper.SetRestClient(RestClient); + RestClient.Get('https://example.com'); + end; +} + +codeunit 50102 Helper +{ + procedure SetRestClient(var RC: Codeunit "Rest Client") + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/HandlerReceivedAsParameter.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/HandlerReceivedAsParameter.al new file mode 100644 index 00000000..b35575b9 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/HandlerReceivedAsParameter.al @@ -0,0 +1,63 @@ +codeunit 50100 MyCodeunit +{ + procedure Call(Handler: Interface "Http Client Handler") + var + [|RestClient|]: Codeunit "Rest Client"; + begin + [|RestClient.Initialize(Handler)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/InterfaceTypedHandlerLocal.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/InterfaceTypedHandlerLocal.al new file mode 100644 index 00000000..83017884 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/InterfaceTypedHandlerLocal.al @@ -0,0 +1,73 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + MyHandler: Codeunit MyHttpClientHandler; + Handler: Interface "Http Client Handler"; + begin + Handler := MyHandler; + [|RestClient.Initialize(Handler)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ObsoletePendingObject.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ObsoletePendingObject.al new file mode 100644 index 00000000..dd24237b --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/ObsoletePendingObject.al @@ -0,0 +1,66 @@ +codeunit 50100 MyCodeunit +{ + ObsoleteState = Pending; + ObsoleteReason = 'Replaced by a newer implementation.'; + + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnCodeunitNamedHttpClientHandler.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnCodeunitNamedHttpClientHandler.al new file mode 100644 index 00000000..f003cf0d --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnCodeunitNamedHttpClientHandler.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + MyHandler: Codeunit "Http Client Handler"; + begin + [|RestClient.Initialize(MyHandler)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50103 "Http Client Handler" implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerAndAuthentication.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerAndAuthentication.al new file mode 100644 index 00000000..44c6a8ad --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerAndAuthentication.al @@ -0,0 +1,79 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + MyHandler: Codeunit MyHttpClientHandler; + Auth: Codeunit "Http Authentication Anonymous"; + begin + [|RestClient.Initialize(MyHandler, Auth)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 2358 "Http Authentication Anonymous" implements "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean + begin + end; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerCodeunit.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerCodeunit.al new file mode 100644 index 00000000..d5394cd8 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/OwnHandlerCodeunit.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + MyHandler: Codeunit MyHttpClientHandler; + begin + [|RestClient.Initialize(MyHandler)|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToEventPublisher.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToEventPublisher.al new file mode 100644 index 00000000..b6b44824 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToEventPublisher.al @@ -0,0 +1,68 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + OnBeforeSend(RestClient); + RestClient.Get('https://example.com'); + end; + + [IntegrationEvent(false, false)] + local procedure OnBeforeSend(var RC: Codeunit "Rest Client") + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToInterfaceMethod.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToInterfaceMethod.al new file mode 100644 index 00000000..43db88c4 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToInterfaceMethod.al @@ -0,0 +1,76 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + IClient: Interface "My Client"; + begin + IClient.Send(RestClient); + RestClient.Get('https://example.com'); + end; +} + +interface "My Client" +{ + procedure Send(var RC: Codeunit "Rest Client"); +} + +codeunit 50102 MyClient implements "My Client" +{ + procedure Send(var RC: Codeunit "Rest Client") + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToOtherObjectProcedure.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToOtherObjectProcedure.al new file mode 100644 index 00000000..a566e8b8 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToOtherObjectProcedure.al @@ -0,0 +1,71 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + Helper: Codeunit Helper; + begin + Helper.Send(RestClient); + RestClient.Get('https://example.com'); + end; +} + +codeunit 50102 Helper +{ + procedure Send(var RC: Codeunit "Rest Client") + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToSameObjectProcedureInitializedWithHandler.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToSameObjectProcedureInitializedWithHandler.al new file mode 100644 index 00000000..357e5f84 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/PassedToSameObjectProcedureInitializedWithHandler.al @@ -0,0 +1,77 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + Setup(RestClient); + RestClient.Get('https://example.com'); + end; + + local procedure Setup(var RC: Codeunit "Rest Client") + var + MyHandler: Codeunit MyHttpClientHandler; + begin + [|RC.Initialize(MyHandler)|]; + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RecursiveSameObjectProcedures.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RecursiveSameObjectProcedures.al new file mode 100644 index 00000000..aaec7fff --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RecursiveSameObjectProcedures.al @@ -0,0 +1,83 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + Ping(RestClient); + end; + + local procedure Ping(var RC: Codeunit "Rest Client") + begin + Pong(RC); + end; + + local procedure Pong(var RC: Codeunit "Rest Client") + var + MyHandler: Codeunit MyHttpClientHandler; + begin + Ping(RC); + [|RC.Initialize(MyHandler)|]; + RC.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} + +codeunit 50101 MyHttpClientHandler implements "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientIdMatchNameMismatch.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientIdMatchNameMismatch.al new file mode 100644 index 00000000..41bd8c3b --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientIdMatchNameMismatch.al @@ -0,0 +1,21 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "My Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "My Client" +{ + procedure Initialize() + begin + end; + + procedure Get(RequestUri: Text) + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientNameMatchIdMismatch.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientNameMatchIdMismatch.al new file mode 100644 index 00000000..77c69fae --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/RestClientNameMatchIdMismatch.al @@ -0,0 +1,21 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 50104 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Get(RequestUri: Text) + begin + end; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestCodeunit.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestCodeunit.al new file mode 100644 index 00000000..869f2997 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestCodeunit.al @@ -0,0 +1,65 @@ +codeunit 50100 MyCodeunit +{ + Subtype = Test; + + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestRunnerCodeunit.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestRunnerCodeunit.al new file mode 100644 index 00000000..5bc4d9c0 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TestRunnerCodeunit.al @@ -0,0 +1,65 @@ +codeunit 50100 MyCodeunit +{ + Subtype = TestRunner; + + trigger OnRun() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TwoLevelFollowingEndsInExternalCall.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TwoLevelFollowingEndsInExternalCall.al new file mode 100644 index 00000000..4d1f0452 --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/TwoLevelFollowingEndsInExternalCall.al @@ -0,0 +1,82 @@ +codeunit 50100 MyCodeunit +{ + procedure DoSomething() + var + [|RestClient|]: Codeunit "Rest Client"; + begin + A(RestClient); + RestClient.Get('https://example.com'); + end; + + local procedure A(var RC: Codeunit "Rest Client") + begin + B(RC); + end; + + local procedure B(var RC: Codeunit "Rest Client") + var + Helper: Codeunit Helper; + begin + Helper.Send(RC); + end; +} + +codeunit 50102 Helper +{ + procedure Send(var RC: Codeunit "Rest Client") + begin + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/VarParameterOnly.al b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/VarParameterOnly.al new file mode 100644 index 00000000..acd8024e --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/NoDiagnostic/VarParameterOnly.al @@ -0,0 +1,61 @@ +codeunit 50100 MyCodeunit +{ + procedure Call(var [|RestClient|]: Codeunit "Rest Client") + begin + [|RestClient.Initialize()|]; + RestClient.Get('https://example.com'); + end; +} + +codeunit 2350 "Rest Client" +{ + procedure Initialize() + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler") + begin + end; + + procedure Initialize(HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Initialize(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication") + begin + end; + + procedure Create(): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Create(HttpClientHandler: Interface "Http Client Handler"; HttpAuthentication: Interface "Http Authentication"): Codeunit "Rest Client" + begin + end; + + procedure Get(RequestUri: Text) + begin + end; + + procedure SetBaseAddress(Url: Text) + begin + end; +} + +interface "Http Client Handler" +{ + procedure Send(CurrHttpClientInstance: HttpClient; HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean; +} + +interface "Http Authentication" +{ + procedure IsAuthenticationRequired(): Boolean; +} diff --git a/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/RestClientInitializeWithHttpClientHandler.cs b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/RestClientInitializeWithHttpClientHandler.cs new file mode 100644 index 00000000..e4a5de6b --- /dev/null +++ b/src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/RestClientInitializeWithHttpClientHandler.cs @@ -0,0 +1,90 @@ +using RoslynTestKit; + +namespace ALCops.ApplicationCop.Test +{ + public class RestClientInitializeWithHttpClientHandler : NavCodeAnalysisBase + { + private AnalyzerTestFixture _fixture; + private string _testCasePath; + + [SetUp] + public void Setup() + { + _fixture = RoslynFixtureFactory.Create(); + + _testCasePath = Path.Combine( + Directory.GetParent( + Environment.CurrentDirectory)!.Parent!.Parent!.FullName, + Path.Combine("Rules", nameof(RestClientInitializeWithHttpClientHandler))); + } + + [Test] + [TestCase("LocalNoInitialization")] + [TestCase("LocalBareInitialize")] + [TestCase("LocalInitializeWithAuthenticationOnly")] + [TestCase("LocalInitializeWithDefaultHandler")] + [TestCase("LocalInitializeWithDefaultHandlerAndAuthentication")] + [TestCase("LocalCreateWithoutHandlerAssigned")] + [TestCase("LocalCreateWithAuthenticationAssigned")] + [TestCase("LocalGoodThenBareInitialize")] + [TestCase("LocalInitializeWithoutParentheses")] + [TestCase("GlobalUsedWithoutInitialization")] + [TestCase("GlobalBareInitializeInOneProcedureUsedInAnother")] + [TestCase("LocalPassedToSameObjectProcedureNeverInitialized")] + [TestCase("LocalPassedToSameObjectProcedureBareInitialize")] + [TestCase("ThisReceiverProcedureCall")] + [TestCase("PageActionTriggerLocal")] + [TestCase("TableTriggerLocal")] + [TestCase("NamespacedFullyQualifiedType")] + [TestCase("TwoLocalsTwoDiagnostics")] + public async Task HasDiagnostic(string testCase) + { + SkipTestIfVersionIsTooLow( + ["ThisReceiverProcedureCall"], + testCase, + "14.0", + "The 'this' self-reference keyword requires runtime version 14.0 (BC 2024 wave 2)."); + + var code = await File.ReadAllTextAsync(Path.Combine(_testCasePath, nameof(HasDiagnostic), $"{testCase}.al")) + .ConfigureAwait(false); + + _fixture.HasDiagnosticAtAllMarkers(code, DiagnosticIds.RestClientInitializeWithHttpClientHandler); + } + + [Test] + [TestCase("OwnHandlerCodeunit")] + [TestCase("OwnHandlerAndAuthentication")] + [TestCase("InterfaceTypedHandlerLocal")] + [TestCase("FactoryReturnedHandler")] + [TestCase("HandlerReceivedAsParameter")] + [TestCase("CreateWithHandlerAssigned")] + [TestCase("CreateResultPassedAsArgument")] + [TestCase("OwnCodeunitNamedHttpClientHandler")] + [TestCase("PassedToOtherObjectProcedure")] + [TestCase("PassedToInterfaceMethod")] + [TestCase("PassedToEventPublisher")] + [TestCase("TwoLevelFollowingEndsInExternalCall")] + [TestCase("GlobalPassedToOtherCodeunit")] + [TestCase("AssignedFromProcedureReturn")] + [TestCase("AssignedToGlobal")] + [TestCase("ExitWithRestClient")] + [TestCase("ClearThenGet")] + [TestCase("PassedToSameObjectProcedureInitializedWithHandler")] + [TestCase("GlobalInitializedInSetupProcedure")] + [TestCase("RecursiveSameObjectProcedures")] + [TestCase("VarParameterOnly")] + [TestCase("DeclaredButUnused")] + [TestCase("TestCodeunit")] + [TestCase("TestRunnerCodeunit")] + [TestCase("ObsoletePendingObject")] + [TestCase("RestClientIdMatchNameMismatch")] + [TestCase("RestClientNameMatchIdMismatch")] + public async Task NoDiagnostic(string testCase) + { + var code = await File.ReadAllTextAsync(Path.Combine(_testCasePath, nameof(NoDiagnostic), $"{testCase}.al")) + .ConfigureAwait(false); + + _fixture.NoDiagnosticAtAllMarkers(code, DiagnosticIds.RestClientInitializeWithHttpClientHandler); + } + } +} diff --git a/src/ALCops.ApplicationCop/ALCops.ApplicationCopAnalyzers.resx b/src/ALCops.ApplicationCop/ALCops.ApplicationCopAnalyzers.resx index 82a9f3d2..8b3cd6ec 100644 --- a/src/ALCops.ApplicationCop/ALCops.ApplicationCopAnalyzers.resx +++ b/src/ALCops.ApplicationCop/ALCops.ApplicationCopAnalyzers.resx @@ -459,6 +459,15 @@ The "Telemetry" and "Feature Telemetry" codeunits route each message to the "Telemetry Logger" registered for the calling app's publisher. When no logger is registered, the message is not forwarded anywhere: "Telemetry Loggers Impl." logs warning 0000G7K and discards it. Implement the "Telemetry Logger" interface and register it in a subscriber to "Telemetry Loggers".OnRegisterTelemetryLogger. + + Rest Client variable is not initialized with a custom Http Client Handler + + + Rest Client variable '{0}' is not initialized with a custom Http Client Handler + + + A "Rest Client" that is initialized without a handler, or with the System Application's default "Http Client Handler" codeunit, sends its requests through the System Application, so the external-call permission, outgoing web service telemetry and test mocking attribute the call to the System Application instead of the calling app. Pass a codeunit that implements "Http Client Handler" to Initialize or Create. + Reserve Enum value zero (0) for empty value diff --git a/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs b/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs new file mode 100644 index 00000000..a8222fbe --- /dev/null +++ b/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs @@ -0,0 +1,30 @@ +using System.Collections.Immutable; +using ALCops.Common.Reflection; +using Microsoft.Dynamics.Nav.CodeAnalysis; +using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics; + +namespace ALCops.ApplicationCop.Analyzers; + +[DiagnosticAnalyzer] +public sealed class RestClientInitializeWithHttpClientHandler : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create(DiagnosticDescriptors.RestClientInitializeWithHttpClientHandler); + + public override void Initialize(AnalysisContext context) => + context.RegisterSymbolAction( + AnalyzeObject, + EnumProvider.SymbolKind.Codeunit, + EnumProvider.SymbolKind.Table, + EnumProvider.SymbolKind.TableExtension, + EnumProvider.SymbolKind.Page, + EnumProvider.SymbolKind.PageExtension, + EnumProvider.SymbolKind.Report, + EnumProvider.SymbolKind.ReportExtension, + EnumProvider.SymbolKind.Query, + EnumProvider.SymbolKind.XmlPort); + + private static void AnalyzeObject(SymbolAnalysisContext ctx) + { + } +} diff --git a/src/ALCops.ApplicationCop/DiagnosticDescriptors.cs b/src/ALCops.ApplicationCop/DiagnosticDescriptors.cs index e3c1a9a4..faff6c88 100644 --- a/src/ALCops.ApplicationCop/DiagnosticDescriptors.cs +++ b/src/ALCops.ApplicationCop/DiagnosticDescriptors.cs @@ -325,6 +325,16 @@ public static class DiagnosticDescriptors description: ApplicationCopAnalyzers.TableDataAccessUnusedPermissionsDescription, helpLinkUri: GetHelpUri(DiagnosticIds.TableDataAccessUnusedPermissions)); + public static readonly DiagnosticDescriptor RestClientInitializeWithHttpClientHandler = new( + id: DiagnosticIds.RestClientInitializeWithHttpClientHandler, + title: ApplicationCopAnalyzers.RestClientInitializeWithHttpClientHandlerTitle, + messageFormat: ApplicationCopAnalyzers.RestClientInitializeWithHttpClientHandlerMessageFormat, + category: Category.Design, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: ApplicationCopAnalyzers.RestClientInitializeWithHttpClientHandlerDescription, + helpLinkUri: GetHelpUri(DiagnosticIds.RestClientInitializeWithHttpClientHandler)); + public static readonly DiagnosticDescriptor RestClientRequiresHttpClientHandler = new( id: DiagnosticIds.RestClientRequiresHttpClientHandler, title: ApplicationCopAnalyzers.RestClientRequiresHttpClientHandlerTitle, diff --git a/src/ALCops.ApplicationCop/DiagnosticIds.cs b/src/ALCops.ApplicationCop/DiagnosticIds.cs index b5abfe92..ca284840 100644 --- a/src/ALCops.ApplicationCop/DiagnosticIds.cs +++ b/src/ALCops.ApplicationCop/DiagnosticIds.cs @@ -37,4 +37,5 @@ public static class DiagnosticIds public static readonly string TableDataAccessUnusedPermissions = "AC0032"; public static readonly string RestClientRequiresHttpClientHandler = "AC0033"; public static readonly string TelemetryRequiresTelemetryLogger = "AC0034"; + public static readonly string RestClientInitializeWithHttpClientHandler = "AC0035"; } \ No newline at end of file From 07b47ed13b5acc6dfa318b21142b711695602547 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 27 Sep 2026 20:02:12 +0200 Subject: [PATCH 2/3] feat(AC0035): report Rest Client variables not initialized with a custom Http Client Handler Co-Authored-By: Claude Opus 5.5 --- ...c0034-required-interface-implementation.md | 2 +- ...ent-initialize-with-http-client-handler.md | 53 ++++ ...stClientInitializeWithHttpClientHandler.cs | 282 ++++++++++++++++++ 3 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 .claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md diff --git a/.claude/rules/diagnostics/ac0033-ac0034-required-interface-implementation.md b/.claude/rules/diagnostics/ac0033-ac0034-required-interface-implementation.md index 94af6283..0287f271 100644 --- a/.claude/rules/diagnostics/ac0033-ac0034-required-interface-implementation.md +++ b/.claude/rules/diagnostics/ac0033-ac0034-required-interface-implementation.md @@ -20,7 +20,7 @@ Registers `RegisterCompilationStartAction` with an inner `RegisterSymbolAction` | Decision | Rationale | |---|---| | One analyzer with a hard-coded pair table (trigger codeunits → required interface → descriptor) | Both rules are the same "uses X, implements no Y" check; a future pair is one row plus one ID. Two separate analyzers would duplicate the walk and the index. | -| Existence check only: some codeunit implements the interface | Tracking whether each variable is passed to `Initialize`/`Create` (or the logger registered via `OnRegisterTelemetryLogger`) needs a per-procedure flow walk; that is a separate follow-up rule, not this one. | +| Existence check only: some codeunit implements the interface | Tracking whether each variable is passed to `Initialize`/`Create` (or the logger registered via `OnRegisterTelemetryLogger`) needs a per-procedure flow walk; AC0035 does that for the Rest Client (`ac0035-rest-client-initialize-with-http-client-handler.md`). | | Coverage counts codeunits declared in the compiling module only (`GetDeclaredApplicationObjectSymbols`), obsolete and test codeunits included | Dependency implementations would need a cross-module scan and make the result depend on what happens to be referenced; a shared-library architecture suppresses via ruleset or pragma. | | Trigger codeunit matched on object id AND name, per id/name pair (2350 "Rest Client"; 8711 Telemetry; 8703 "Feature Telemetry") | Id alone matches unrelated same-id objects outside the System Application; name alone matches any same-named codeunit. Namespace or module checks were rejected so stubbed or namespaced System Application builds still match. | | Interface matched by name only (`SemanticFacts.IsSameName`) | The implementing codeunit's `ImplementedInterfaces` points at the dependency interface; comparing module or namespace adds nothing a real app would get wrong. | diff --git a/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md b/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md new file mode 100644 index 00000000..b9b69240 --- /dev/null +++ b/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md @@ -0,0 +1,53 @@ +--- +paths: + - "src/ALCops.ApplicationCop/**/RestClientInitializeWithHttpClientHandler*" + - "src/ALCops.ApplicationCop.Test/Rules/RestClientInitializeWithHttpClientHandler/**" +--- + +# AC0035: RestClientInitializeWithHttpClientHandler + +## Purpose + +Reports a `Codeunit "Rest Client"` variable that is used or initialized without a custom `"Http Client Handler"`: `Initialize`/`Create` without a handler argument, with the System Application's default codeunit 2360 `"Http Client Handler"`, or no initialization at all before use. Such a client sends through the System Application, so the external-call permission, outgoing web-service telemetry and test mocking belong to the System Application instead of the calling app. AC0033 checks that the app has a handler at all; this rule checks that each variable receives one. + +Registers `RegisterSymbolAction` on Codeunit, Table, TableExtension, Page, PageExtension, Report, ReportExtension, Query and XmlPort; main type `TrackedWalker` (an `OperationWalker` per root, sharing one `RootState`). + +## Design decisions + +| Decision | Rationale | +|---|---| +| Roots are locals (any method or trigger body) and globals of the object; parameters and return values are never roots | The caller owns a parameter's initialization and the callee of a return value decides what to do with it; reporting them would flag every helper that receives a ready client. | +| Good = an argument bound to an `Interface "Http Client Handler"` parameter whose static type is not codeunit 2360 `"Http Client Handler"` (id and name) | Interface-typed values, factory results, parameters and codeunits from any app cannot be proven to be the default handler; tracing their origin was rejected as unbounded. | +| Silence (root becomes unknown) when it is passed to anything not followable (another object, a method without source, an interface method, an event, a built-in such as `Clear`), assigned from anything but `Root.Create(...)`, assigned to another variable, returned via `exit`, or an invocation on it is invalid | Past those points the rule no longer sees every call on the instance; reporting anyway was rejected because it would flag clients initialized elsewhere. | +| A root passed to a procedure declared inside the same object syntax is followed into the callee, `var` and by-value alike, unlimited depth, cycle-safe | Codeunit variables are reference types, so a by-value callee initializes the caller's instance too. Containment is checked on syntax (same tree, span contains the callee), not by comparing `GetContainingApplicationObjectTypeSymbol()`, which is null for request-page procedures. | +| Location: the first default `Initialize`/`Create` found directly on the root; otherwise the variable's name | The call is where the fix goes. A default call inside a followed callee reports at the root's declaration, because the callee's call also serves other callers. | +| A default call is reported even when a good call exists elsewhere on the same root | `Initialize()` after `Initialize(MyHandler)` replaces the handler; ordering analysis was rejected as too costly for the gain. | +| No ordering analysis and no request-method list: any other member invocation marks the root used | A list of `Get`/`Post`/`Send`... would miss new members; "used before initialization" order is not tracked (a good initialization anywhere counts). | +| Roots discovered on symbols (`LocalVariables`, `GetMembers()`), with a body-text pre-filter only for globals | A declaration can spell the type `Codeunit 2350`, so text cannot find roots; a reference to a global always spells its name, so skipping bodies that do not contain it is sound and saves binding. | +| One symbol action per object instead of a code-block action | A global's initialization and use live in different bodies; a per-object callback analyzes and reports in one place (`sdk-analysis-scope.md`) and binds only objects that declare a root. | +| Warning, enabled by default, `Category.Design`, no settings, no CodeFix, no version gate | Same impact class as AC0033; the right handler codeunit cannot be generated; every SDK member used exists at ns2.0 / AL 12.0 (nav-sdk-docs `reference/`). | + +## Deliberate non-reports + +- Parameters and return values of type `Codeunit "Rest Client"` (not roots). +- A root passed to `Clear` or any other built-in, to another object, an interface method or an event publisher (unknown). +- `Codeunit::"Rest Client"` object references without a variable. +- `array[n] of Codeunit "Rest Client"`, `List of [...]` and interface-typed variables: the variable type is not the codeunit symbol. +- A declared but never used root without an `Initialize`/`Create` call. +- Test and test-runner codeunits, obsolete objects and locals of obsolete procedures. + +## Known issues + +- A same-named codeunit 2360 from another module would count as the default handler (id and name match only); accepted. + +## SDK facts + +- `RestClient.Initialize;` without parentheses in statement position binds to an `IInvocationExpression` whose `Syntax` is the `MemberAccessExpressionSyntax` (verified with the `LocalInitializeWithoutParentheses` fixture, net10.0 SDK). +- `ISymbol.GetLocation()` on a local or global variable is the name identifier, not the whole declaration (verified by dumping fixture diagnostics). +- `IArgument.Parameter` is null when no parameter could be matched (docs: nav-sdk-docs `docs/40-operations/invocation-and-arguments.md`); arguments bound to an interface parameter are wrapped in `IConversionExpression`, so values are compared after `UnwrapConversions()`. +- The SDK has no `SymbolEqualityComparer`; `ISymbol.Equals` is the only equality contract (docs: nav-sdk-docs `docs/20-symbols/symbol-hierarchy.md`). + +## Test notes + +- `ThisReceiverProcedureCall` is gated on 14.0 (the `this` keyword). +- Fixtures declare their own System Application stubs with the real ids (2350 `"Rest Client"`, 2360 `"Http Client Handler"`, 2358 `"Http Authentication Anonymous"`); no stub implements both interfaces, so `Initialize`/`Create` overloads resolve unambiguously. A user procedure named `Run` collides with the built-in codeunit `Run` (AL0440). diff --git a/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs b/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs index a8222fbe..e8215372 100644 --- a/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs +++ b/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs @@ -1,13 +1,35 @@ using System.Collections.Immutable; +using ALCops.Common.Extensions; using ALCops.Common.Reflection; using Microsoft.Dynamics.Nav.CodeAnalysis; using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics; +using Microsoft.Dynamics.Nav.CodeAnalysis.Semantics; +using Microsoft.Dynamics.Nav.CodeAnalysis.Syntax; +using Microsoft.Dynamics.Nav.CodeAnalysis.Text; namespace ALCops.ApplicationCop.Analyzers; [DiagnosticAnalyzer] public sealed class RestClientInitializeWithHttpClientHandler : DiagnosticAnalyzer { + private const int RestClientId = 2350; + private const string RestClientName = "Rest Client"; + private const int DefaultHandlerId = 2360; + private const string HandlerName = "Http Client Handler"; + + // Member kinds whose own members can declare code (triggers, nested controls, actions, dataitems, nodes). + // Keys are left out on purpose: in a tableextension they enumerate the target table's fields, which would + // attribute base-table field triggers to the extension. + private static readonly ImmutableHashSet DescendKinds = ImmutableHashSet.Create( + EnumProvider.SymbolKind.Field, + EnumProvider.SymbolKind.Control, + EnumProvider.SymbolKind.Action, + EnumProvider.SymbolKind.Change, + EnumProvider.SymbolKind.ReportDataItem, + EnumProvider.SymbolKind.XmlPortNode, + EnumProvider.SymbolKind.RequestPage, + EnumProvider.SymbolKind.RequestPageExtension); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptors.RestClientInitializeWithHttpClientHandler); @@ -26,5 +48,265 @@ public override void Initialize(AnalysisContext context) => private static void AnalyzeObject(SymbolAnalysisContext ctx) { + if (ctx.IsObsolete()) + return; + + if (ctx.Symbol is not IContainerSymbol container) + return; + + if (ctx.Symbol is ICodeunitTypeSymbol codeunit && + (codeunit.Subtype == EnumProvider.CodeunitSubtypeKind.Test || + codeunit.Subtype == EnumProvider.CodeunitSubtypeKind.TestRunner)) + return; + + CancellationToken ct = ctx.CancellationToken; + + // Roots are found on symbols only: the declaration may spell the type as `Codeunit 2350`. + var methods = new List(); + CollectMethods(container, methods, ct); + + var globalRoots = container.GetMembers().OfType().Where(v => IsRestClient(v.Type)).ToList(); + bool hasLocalRoot = methods.Exists(m => m.LocalVariables.Any(v => IsRestClient(v.Type))); + if (globalRoots.Count == 0 && !hasLocalRoot) + return; + + if (ctx.Symbol.DeclaringSyntaxReference?.GetSyntax(ct) is not ObjectSyntax objectSyntax) + return; + + SemanticModel model = ctx.Compilation.GetSemanticModel(objectSyntax.SyntaxTree); + + foreach (IMethodSymbol method in methods) + { + if (method.IsObsolete()) + continue; + + foreach (IVariableSymbol local in method.LocalVariables) + { + ct.ThrowIfCancellationRequested(); + + if (!IsRestClient(local.Type) || GetBody(method, ct) is not { } body) + continue; + + var state = new RootState(); + Walk(local, body, state, model, objectSyntax, ct); + Report(ctx, local, state); + } + } + + foreach (IVariableSymbol global in globalRoots) + { + var state = new RootState(); + foreach (IMethodSymbol method in methods) + { + ct.ThrowIfCancellationRequested(); + + if (state.Unknown) + break; + + // Sound pre-filter: every reference to the global spells its name in the body. + if (GetBody(method, ct) is not { } body || + body.ToString().IndexOf(global.Name, SemanticFacts.NameEqualityComparison) < 0) + continue; + + Walk(global, body, state, model, objectSyntax, ct); + } + + Report(ctx, global, state); + } + } + + private static void Walk(ISymbol root, BlockSyntax body, RootState state, SemanticModel model, ObjectSyntax objectSyntax, CancellationToken ct) + { + if (model.GetOperation(body, ct) is { } operation) + new TrackedWalker(root, true, state, model, objectSyntax, new HashSet(), ct).Visit(operation); + } + + private static void Report(SymbolAnalysisContext ctx, IVariableSymbol root, RootState state) + { + if (state.Unknown) + return; + + Location? location = null; + if (state.Default) + location = state.DefaultLocation ?? root.GetLocation(); + else if (state.Used && !state.Good) + location = root.GetLocation(); + + if (location is not null) + ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.RestClientInitializeWithHttpClientHandler, location, root.Name)); + } + + private static void CollectMethods(IContainerSymbol container, List methods, CancellationToken ct) + { + foreach (ISymbol member in container.GetMembers()) + { + ct.ThrowIfCancellationRequested(); + + if (member is IMethodSymbol method) + methods.Add(method); + else if (member is IContainerSymbol nested && DescendKinds.Contains(member.Kind)) + CollectMethods(nested, methods, ct); + } + } + + private static BlockSyntax? GetBody(IMethodSymbol method, CancellationToken ct) => + (method.DeclaringSyntaxReference?.GetSyntax(ct) as MethodOrTriggerDeclarationSyntax)?.Body; + + private static bool IsRestClient(ITypeSymbol? type) => IsCodeunit(type, RestClientId, RestClientName); + + private static bool IsCodeunit(ITypeSymbol? type, int id, string name) => + type is not null && + type.NavTypeKind == EnumProvider.NavTypeKind.Codeunit && + (type.OriginalDefinition as ICodeunitTypeSymbol ?? type as ICodeunitTypeSymbol) is { } codeunit && + codeunit.Id == id && + SemanticFacts.IsSameName(codeunit.Name, name); + + private sealed class RootState + { + public bool Used { get; set; } + public bool Unknown { get; set; } + public bool Good { get; set; } + public bool Default { get; set; } + public Location? DefaultLocation { get; set; } + } + + // Tracks one Rest Client variable (the root, or the parameter it was passed into) through a body. + // Anything the walker cannot follow makes the root unknown, which silences it. + private sealed class TrackedWalker( + ISymbol tracked, + bool direct, + RootState state, + SemanticModel model, + ObjectSyntax objectSyntax, + HashSet visited, + CancellationToken ct) : OperationWalker + { + public override void VisitInvocationExpression(IInvocationExpression operation) + { + if (state.Unknown) + return; + ct.ThrowIfCancellationRequested(); + + if (IsTracked(operation.Instance)) + { + if (operation.IsInvalid) + { + state.Unknown = true; + return; + } + + // A discarded `RestClient.Create(Handler);` also initializes the receiver itself. + if (SemanticFacts.IsSameName(operation.TargetMethod.Name, "Initialize") || + SemanticFacts.IsSameName(operation.TargetMethod.Name, "Create")) + ClassifyInitialization(operation); + else + state.Used = true; + } + + for (int i = 0; i < operation.Arguments.Length; i++) + { + if (IsTracked(operation.Arguments[i].Value) && !TryFollow(operation.TargetMethod, i)) + { + state.Unknown = true; + return; + } + } + + base.VisitInvocationExpression(operation); + } + + public override void VisitAssignmentStatement(IAssignmentStatement operation) + { + if (state.Unknown) + return; + + if (IsTracked(operation.Target)) + { + // Only `RestClient := RestClient.Create(...)` keeps the instance known. + if (operation.Value.UnwrapConversions() is not IInvocationExpression invocation || + !IsTracked(invocation.Instance) || + !SemanticFacts.IsSameName(invocation.TargetMethod.Name, "Create")) + { + state.Unknown = true; + return; + } + } + else if (IsTracked(operation.Value)) + { + state.Unknown = true; + return; + } + + base.VisitAssignmentStatement(operation); + } + + public override void VisitExitStatement(IExitStatement operation) + { + if (state.Unknown) + return; + + if (IsTracked(operation.ReturnedValue)) + { + state.Unknown = true; + return; + } + + base.VisitExitStatement(operation); + } + + private void ClassifyInitialization(IInvocationExpression operation) + { + // IArgument.Parameter is null when binding failed. + IArgument? handlerArgument = operation.Arguments.FirstOrDefault(a => + a.Parameter?.ParameterType is { } type && + type.NavTypeKind == EnumProvider.NavTypeKind.Interface && + SemanticFacts.IsSameName(type.Name, HandlerName)); + + if (handlerArgument is not null && + !IsCodeunit(handlerArgument.Value.UnwrapConversions().Type, DefaultHandlerId, HandlerName)) + { + state.Good = true; + return; + } + + state.Default = true; + if (direct && state.DefaultLocation is null) + state.DefaultLocation = operation.Syntax.GetLocation(); + } + + // Follows the tracked variable into a procedure declared inside the same object syntax. Syntax + // containment also covers request-page procedures, whose containing application object is null. + private bool TryFollow(IMethodSymbol target, int argumentIndex) + { + if (target.MethodKind != EnumProvider.MethodKind.Method || + target.IsEvent || + argumentIndex >= target.Parameters.Length || + target.DeclaringSyntaxReference?.GetSyntax(ct) is not MethodOrTriggerDeclarationSyntax { Body: { } body } callee || + callee.SyntaxTree != objectSyntax.SyntaxTree || + !objectSyntax.Span.Contains(callee.Span)) + return false; + + if (!visited.Add(target)) + return true; + + try + { + if (model.GetOperation(body, ct) is not { } operation) + return false; + + new TrackedWalker(target.Parameters[argumentIndex], false, state, model, objectSyntax, visited, ct).Visit(operation); + return true; + } + finally + { + visited.Remove(target); + } + } + + // The SDK has no SymbolEqualityComparer; ISymbol.Equals compares the declared symbols. + private bool IsTracked(IOperation? operation) => + operation is not null && + operation.UnwrapConversions().GetSymbolSafe() is { } symbol && + symbol.Equals(tracked); } } From 859f0fd0bdabdc4dbfef4d8242b8383a65a8e5bf Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 27 Sep 2026 20:20:29 +0200 Subject: [PATCH 3/3] fix(AC0035): treat unbound calls as unknown and cache body text for the global pre-filter A call that fails to bind may pair the root with the wrong parameter, so following it could track a symbol that never appears in the callee; the root is now silenced instead. The body text used by the global pre-filter is computed once per method rather than once per global and method pair. The rule doc records the deliberate DescendKinds duplication. Co-Authored-By: Claude Fable 5.1 --- ...ent-initialize-with-http-client-handler.md | 2 ++ ...stClientInitializeWithHttpClientHandler.cs | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md b/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md index b9b69240..3a6a1a52 100644 --- a/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md +++ b/.claude/rules/diagnostics/ac0035-rest-client-initialize-with-http-client-handler.md @@ -25,6 +25,7 @@ Registers `RegisterSymbolAction` on Codeunit, Table, TableExtension, Page, PageE | No ordering analysis and no request-method list: any other member invocation marks the root used | A list of `Get`/`Post`/`Send`... would miss new members; "used before initialization" order is not tracked (a good initialization anywhere counts). | | Roots discovered on symbols (`LocalVariables`, `GetMembers()`), with a body-text pre-filter only for globals | A declaration can spell the type `Codeunit 2350`, so text cannot find roots; a reference to a global always spells its name, so skipping bodies that do not contain it is sound and saves binding. | | One symbol action per object instead of a code-block action | A global's initialization and use live in different bodies; a per-object callback analyzes and reports in one place (`sdk-analysis-scope.md`) and binds only objects that declare a root. | +| `DescendKinds` and the member descent are copied from `RequiredInterfaceImplementation` instead of shared through Common | Two call sites do not yet justify a Common helper; extracting one is a follow-up that touches both analyzers. | | Warning, enabled by default, `Category.Design`, no settings, no CodeFix, no version gate | Same impact class as AC0033; the right handler codeunit cannot be generated; every SDK member used exists at ns2.0 / AL 12.0 (nav-sdk-docs `reference/`). | ## Deliberate non-reports @@ -39,6 +40,7 @@ Registers `RegisterSymbolAction` on Codeunit, Table, TableExtension, Page, PageE ## Known issues - A same-named codeunit 2360 from another module would count as the default handler (id and name match only); accepted. +- A call that fails to bind (for example a root passed where an `Integer` is expected) makes the root unknown, so a default initialization reachable only through that call is not reported; the compiler error is the signal there. ## SDK facts diff --git a/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs b/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs index e8215372..0e07729f 100644 --- a/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs +++ b/src/ALCops.ApplicationCop/Analyzers/RestClientInitializeWithHttpClientHandler.cs @@ -93,10 +93,20 @@ private static void AnalyzeObject(SymbolAnalysisContext ctx) } } + if (globalRoots.Count == 0) + return; + + var bodies = new List<(BlockSyntax Body, string Text)>(); + foreach (IMethodSymbol method in methods) + { + if (GetBody(method, ct) is { } body) + bodies.Add((body, body.ToString())); + } + foreach (IVariableSymbol global in globalRoots) { var state = new RootState(); - foreach (IMethodSymbol method in methods) + foreach ((BlockSyntax body, string text) in bodies) { ct.ThrowIfCancellationRequested(); @@ -104,8 +114,7 @@ private static void AnalyzeObject(SymbolAnalysisContext ctx) break; // Sound pre-filter: every reference to the global spells its name in the body. - if (GetBody(method, ct) is not { } body || - body.ToString().IndexOf(global.Name, SemanticFacts.NameEqualityComparison) < 0) + if (text.IndexOf(global.Name, SemanticFacts.NameEqualityComparison) < 0) continue; Walk(global, body, state, model, objectSyntax, ct); @@ -203,9 +212,11 @@ public override void VisitInvocationExpression(IInvocationExpression operation) state.Used = true; } + // A call that failed to bind may pair the argument with the wrong parameter. for (int i = 0; i < operation.Arguments.Length; i++) { - if (IsTracked(operation.Arguments[i].Value) && !TryFollow(operation.TargetMethod, i)) + if (IsTracked(operation.Arguments[i].Value) && + (operation.IsInvalid || !TryFollow(operation.TargetMethod, i))) { state.Unknown = true; return;