Azure / azure-sdk-for-net

This repository is for active development of the Azure SDK for .NET. For consumers of the SDK we recommend visiting our public developer docs at https://learn.microsoft.com/dotnet/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-net.
MIT License
5.35k stars 4.71k forks source link

Enable generation of a single ClientOptions type for a library #29223

Closed heaths closed 9 months ago

heaths commented 2 years ago

The DPG-generated clients have 2 sets of constructors like so:

    public partial class ConversationAnalysisAuthoringClient
    {
        private const string AuthorizationHeader = "Ocp-Apim-Subscription-Key";
        private readonly AzureKeyCredential _keyCredential;
        private const string AuthorizationHeader0 = "Ocp-Apim-Subscription-Key";
        private readonly AzureKeyCredential _keyCredential0;
        private readonly HttpPipeline _pipeline;
        private readonly Uri _endpoint;
        private readonly string _apiVersion;

        /// <summary> The ClientDiagnostics is used to provide tracing support for the client library. </summary>
        internal ClientDiagnostics ClientDiagnostics { get; }

        /// <summary> The HTTP pipeline for sending and receiving REST requests and responses. </summary>
        public virtual HttpPipeline Pipeline => _pipeline;

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient for mocking. </summary>
        protected ConversationAnalysisAuthoringClient()
        {
        }

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, new ConversationAnalysisClientOptions())
        {
        }

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, new ConversationAnalysisClientOptions())
        {
        }

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <param name="options"> The options for configuring the client. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential, ConversationAnalysisClientOptions options)
        {
            Argument.AssertNotNull(endpoint, nameof(endpoint));
            Argument.AssertNotNull(credential, nameof(credential));
            options ??= new ConversationAnalysisClientOptions();

            ClientDiagnostics = new ClientDiagnostics(options, true);
            _keyCredential0 = credential;
            _pipeline = HttpPipelineBuilder.Build(options, Array.Empty<HttpPipelinePolicy>(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential0, AuthorizationHeader0) }, new ResponseClassifier());
            _endpoint = endpoint;
            _apiVersion = options.Version;
        }

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <param name="options"> The options for configuring the client. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential, ConversationAnalysisClientOptions options)
        {
            Argument.AssertNotNull(endpoint, nameof(endpoint));
            Argument.AssertNotNull(credential, nameof(credential));
            options ??= new ConversationAnalysisClientOptions();

            ClientDiagnostics = new ClientDiagnostics(options, true);
            _keyCredential0 = credential;
            _pipeline = HttpPipelineBuilder.Build(options, Array.Empty<HttpPipelinePolicy>(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential0, AuthorizationHeader0) }, new ResponseClassifier());
            _endpoint = endpoint;
            _apiVersion = options.Version;
        }

You can also see two sets of authorization headers. This happens when I have an autorest.md file like so:

# Generated code configuration

Run `dotnet build /t:GenerateCode` to generate code.

``` yaml
# The title here is used to generate the single ClientOptions class name.
title: Conversations
license-header: MICROSOFT_MIT_NO_VERSION

input-file:
- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/e7f37e4e43b1d12fd1988fda3ed39624c4b23303/specification/cognitiveservices/data-plane/Language/preview/2022-05-15-preview/analyzeconversations.json
- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/e7f37e4e43b1d12fd1988fda3ed39624c4b23303/specification/cognitiveservices/data-plane/Language/preview/2022-05-15-preview/analyzeconversations-authoring.json

data-plane: true
model-namespace: false
clear-output-folder: true

modelerfour:
  lenient-model-deduplication: true

Customizations

Customizations that should eventually be added to central autorest configuration.

General customizations

Support automatically generating code for key credentials.

directive:
- from: swagger-document
  where: $.securityDefinitions
  transform: |
    $["AzureKey"] = $["apim_key"];
    delete $["apim_key"];

- from: swagger-document
  where: $.security
  transform: |
    $ = [
        {
          "AzureKey": []
        }
    ];

This would result in 2 separate client classes by design - a `ConversationAnalysisClient` and `ConversationAnalysisAuthoringClient`. Just like we shipped the Question Answering SDK, we want to use 1 `ConversationAnalysisClientOptions`, so I added a class like so:

```c#
    [CodeGenModel("ConversationsClientOptions")]
    public partial class ConversationAnalysisClientOptions : ClientOptions

If I process these input-files in a batch I can probably work around this, but I imagine this wasn't by design. I also don't know how the DPG generator came up with ConversationsClientOptions - this isn't evident from either of the swaggers - but it's not the name we want. It seems from some minimal testing that this was a big cause of the duplication error.

heaths commented 2 years ago

Seems using a batch in autorest.md can't quite work around this either. I have in my autorest.md:

title: Conversations
license-header: MICROSOFT_MIT_NO_VERSION

batch:
- input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/e7f37e4e43b1d12fd1988fda3ed39624c4b23303/specification/cognitiveservices/data-plane/Language/preview/2022-05-15-preview/analyzeconversations.json
  clear-output-folder: true

- input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/e7f37e4e43b1d12fd1988fda3ed39624c4b23303/specification/cognitiveservices/data-plane/Language/preview/2022-05-15-preview/analyzeconversations-authoring.json

data-plane: true
model-namespace: false

modelerfour:
  lenient-model-deduplication: true

I also have to suppress the ConversationAnalysisAuthoringClientOptions that the generator wants to make, so I have in my ConversationAnslysisAuthoringClient.cs file:

[assembly: CodeGenSuppressType("ConversationAnalysisAuthoringClientOptions")]

namespace Azure.AI.Language.Conversations
{
    [CodeGenClient("ConversationalAnalysisAuthoringClient")]
    public partial class ConversationAnalysisAuthoringClient
    {
        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, new ConversationAnalysisClientOptions())
        {
        }

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <param name="options"> The options for configuring the client. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential, ConversationAnalysisClientOptions options)
        {
            Argument.AssertNotNull(endpoint, nameof(endpoint));
            Argument.AssertNotNull(credential, nameof(credential));
            options ??= new ConversationAnalysisClientOptions();

            ClientDiagnostics = new ClientDiagnostics(options, true);
            _keyCredential = credential;
            _pipeline = HttpPipelineBuilder.Build(options, Array.Empty<HttpPipelinePolicy>(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential, AuthorizationHeader) }, new ResponseClassifier());
            _endpoint = endpoint;
            _apiVersion = options.Version;
        }

        /// <summary>
        /// Gets the service endpoint for this client.
        /// </summary>
        public virtual Uri Endpoint => _endpoint;
    }
}

This almost works, but the generator is still generating an additional constructor:

        /// <summary> Initializes a new instance of ConversationAnalysisAuthoringClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.api.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <param name="options"> The options for configuring the client. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisAuthoringClient(Uri endpoint, AzureKeyCredential credential, ConversationAnalysisAuthoringClientOptions options)
        {
            Argument.AssertNotNull(endpoint, nameof(endpoint));
            Argument.AssertNotNull(credential, nameof(credential));
            options ??= new ConversationAnalysisAuthoringClientOptions();

            ClientDiagnostics = new ClientDiagnostics(options, true);
            _keyCredential = credential;
            _pipeline = HttpPipelineBuilder.Build(options, Array.Empty<HttpPipelinePolicy>(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential, AuthorizationHeader) }, new ResponseClassifier());
            _endpoint = endpoint;
            _apiVersion = options.Version;
        }

This can't compile because ConversationAnalysisAuthoringClientOptions doesn't exist. I could probably make a partial class with that name and make it internal for now, which then lets me use the [CodeGenSuppress] attribute that requires Type parameters. Can't use that now because the ConversationAnalysisAuthoringClientOptions type doesn't exist.

heaths commented 2 years ago

Seems I was able to work around this if I add the class attribute:

    [CodeGenModel("ConversationAnalysisAuthoringClientOptions")]
    public partial class ConversationAnalysisClientOptions : ClientOptions

But I still have to define the 2 non-default constructors for the ConversationAnalysisProjectAuthoringClient.

jsquire commented 2 years ago

@annelo-msft: Would you please be so kind as to triage?

AlexanderSher commented 2 years ago

@heaths why do you need lenient-model-deduplication: true?

heaths commented 2 years ago

No idea. It's just always been in so may different autorest.md files for SDKs I've worked on I've just always included it. Could that be causing the problem?

FWIW, long after I opened this and my laptop battery died, I think I know why the headers were duplicated: each swagger was written more or less to stand alone, so each has its own security and securityDefinitions sections. In one test when I included both in a single input-file array that likely resulted in that duplication. I've since reseparated them into separate batch array items, which was pretty much intended when they were written anyway.

Also, I don't necessarily dislike the chosen common ConversationsClientOptions (picked from the last component of the package name or namespace, perhaps?), but we already established a pattern of using the inference client's options for the project client, expecting that nothing needs to be added and the project client will get far less use. In fact, even before DPG, we always planned to ship the project clients as LLCs across languages.

annelo-msft commented 2 years ago

@heaths, I believe Alexander is trying to understand whether you need this, because removing it might solve the problem. It would be good to understand whether you actually need it or not -- it sounds like this flag's intended usage is only for extreme edge cases.

AlexanderSher commented 2 years ago

FYI: https://github.com/Azure/autorest/issues/3786 https://github.com/Azure/autorest.modelerfour/pull/381

annelo-msft commented 2 years ago

@heaths, would you be willing to try it without the lenient-model-deduplication: true flag and see if that solves the problem without introducing other problems?

@lmazuel / @mikekistler, could you help us understand if @heaths is providing valid input to the DPG generator? Do you know how other languages are handling duplication across the two CLU swagger files?

- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/e7f37e4e43b1d12fd1988fda3ed39624c4b23303/specification/cognitiveservices/data-plane/Language/preview/2022-05-15-preview/analyzeconversations.json
- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/e7f37e4e43b1d12fd1988fda3ed39624c4b23303/specification/cognitiveservices/data-plane/Language/preview/2022-05-15-preview/analyzeconversations-authoring.json
heaths commented 2 years ago

Normally if there are multiple swaggers this is what you'd do: there's examples of this all over the repo. input-file can be either a string or string array; however, I'm not longer doing that because of the security duplication and because the swaggers were written to be used in a batch, per my pervious guidance with Question Answering. See my current PR: https://github.com/Azure/azure-sdk-for-net/pull/29144/files#diff-7655c2c616c79de0a6e38e23d436eb50f6e0e34c1e612f3b4fb41a4295f2f9cbL10

See https://github.com/Azure/autorest/blob/d021dadda293256d3bf18f6c057ba474aaab37af/docs/user/literate-file-formats/configuration.md#configuration-element-conventions for docs. This is also the norm in the specs repo like https://github.com/Azure/azure-rest-api-specs/blob/main/specification/keyvault/data-plane/readme.md#tag-package-73.

mikekistler commented 2 years ago

This seems to me to be a specific instance of the general problem of autorest smashing together two distinct REST API docs. This is completely non-standard and to my knowledge completely undocumented, so there's no telling whether this is "valid" or not.

It would be great if we could simply disallow multiple input files for DPG.

heaths commented 2 years ago

It would be great if we could simply disallow multiple input files for DPG.

Several services separate their REST API into separate swagger different feature teams work on to more easily avoid merge conflicts. The swaggers here are one variation that just happens to be written as distinct swaggers, but that's only one variation. Swaggers can be written to use multiple files but as one cohesive REST API, in which case multiple input-file values is necessary.

mikekistler commented 2 years ago

I think it is possible to split an API definition over multiple files in a completely standard way -- using $refs -- that would not require multiple input-file values.

heaths commented 2 years ago

This might actually be an instance of Azure/autorest.csharp#1497. At any rate, I was able to work around it but it involved a number of steps:

  1. Define partial class with desired, common name e.g., ConversationAnalysisClientOptions.
  2. Add [assembly: CodeGenSuppressType("ConversationAuthoringClientOptions")] to some class file.
  3. Define partial class for other client that defines both constructors, which also means hand-coding the otherwise generated constructor implementation and keeping that up to date (not ideal).
  4. Add [CodeGenModel("ConversationAuthoringClientOptions")] to class you defined in 1 above.

All together it ended up looking like this:

// ConversationAuthoringClient.cs
using System;
using Azure.Core;
using Azure.Core.Pipeline;

[assembly: CodeGenSuppressType("ConversationAuthoringClientOptions")]

namespace Azure.AI.Language.Conversations.Authoring
{
    public partial class ConversationAuthoringClient
    {
        /// <summary>
        /// Initializes a new instance of ConversationAnalysisProjectsClient.
        /// </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAuthoringClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, new ConversationAnalysisClientOptions())
        {
        }

        /// <summary>
        /// Initializes a new instance of ConversationAnalysisProjectsClient.
        /// </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <param name="options"> The options for configuring the client. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAuthoringClient(Uri endpoint, AzureKeyCredential credential, ConversationAnalysisClientOptions options)
        {
            Argument.AssertNotNull(endpoint, nameof(endpoint));
            Argument.AssertNotNull(credential, nameof(credential));
            options ??= new ConversationAnalysisClientOptions();

            ClientDiagnostics = new ClientDiagnostics(options, true);
            _keyCredential = credential;
            _pipeline = HttpPipelineBuilder.Build(options, Array.Empty<HttpPipelinePolicy>(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential, AuthorizationHeader) }, new ResponseClassifier());
            _endpoint = endpoint;
            _apiVersion = options.Version;
        }

        /// <summary>
        /// Gets the service endpoint for this client.
        /// </summary>
        public virtual Uri Endpoint => _endpoint;
    }
}

// ConversationAnalysisClientOptions.cs
using System;
using Azure.Core;

namespace Azure.AI.Language.Conversations
{
    /// <summary>
    /// Client options for <see cref="ConversationAnalysisClient"/>.
    /// </summary>
    [CodeGenModel("ConversationAuthoringClientOptions")]
    public partial class ConversationAnalysisClientOptions : ClientOptions
    {
        internal string Version { get; }

        /// <summary>
        /// Initializes new instance of <see cref="ConversationAnalysisClientOptions"/>.
        /// </summary>
        public ConversationAnalysisClientOptions(ServiceVersion version = LatestVersion)
        {
            Version = version switch
            {
                ServiceVersion.V2022_05_01 => "2022-05-01",
                _ => throw new NotSupportedException()
            };

            this.ConfigureLogging();
        }
    }
}

This wasn't intuitive to figure out and took some playing around. Making it easier in cases we want this behavior would be great.

annelo-msft commented 2 years ago

Thanks for all the effort you put into this, @heaths! Since you are not blocked on this, I have moved the ask to DPG v1.1.

I do think it makes sense to make this easier for DPG library authors :)

Is it correct to summarize the ask as "Enable generation of a single ClientOptions type for a library"? If so, I will update the title of this issue to reflect that.

heaths commented 2 years ago

Is it correct to summarize the ask as "Enable generation of a single ClientOptions type for a library"? If so, I will update the title of this issue to reflect that.

After all the internal discussions on this, yes, I think that's a good way to summarize the ask now. Unifying to a single type - whether in a batch or just multiple input-files (though, the latter is probably easier) was not intuitive. I had to get inventive, and even with extensive experience with these attributes it took a lot of trial and error.

heaths commented 2 years ago

@KrzysztofCwalina requested a shorter client options name like ConversionClientOptions but since I can't use two [CodeGenModel] I suggested - and he approved - that I use the default ConversationsClientOptions (plural) that you generate.

In doing so, I removed the batch from my autorest.md:

-batch:
-- input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/725f4ca360426a32d20e81eb945065e62c285d6a/specification/cognitiveservices/data-plane/Language/stable/2022-05-01/analyzeconversations.json
-  clear-output-folder: true
-
-- input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/725f4ca360426a32d20e81eb945065e62c285d6a/specification/cognitiveservices/data-plane/Language/stable/2022-05-01/analyzeconversations-authoring.json
-  namespace: Azure.AI.Language.Conversations.Authoring
+input-file:
+- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/725f4ca360426a32d20e81eb945065e62c285d6a/specification/cognitiveservices/data-plane/Language/stable/2022-05-01/analyzeconversations.json
+- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/725f4ca360426a32d20e81eb945065e62c285d6a/specification/cognitiveservices/data-plane/Language/stable/2022-05-01/analyzeconversations-authoring.json
+clear-output-folder: true

This alone resulted in 2 constructors - the 2-param one that doesn't take a ClientOptions - being generated e.g.,


        /// <summary> Initializes a new instance of ConversationAnalysisClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, new ConversationsClientOptions())
        {
        }

        /// <summary> Initializes a new instance of ConversationAnalysisClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, new ConversationsClientOptions())
        {
        }

        /// <summary> Initializes a new instance of ConversationAnalysisClient. </summary>
        /// <param name="endpoint"> Supported Cognitive Services endpoint (e.g., https://&lt;resource-name&gt;.cognitiveservices.azure.com). </param>
        /// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
        /// <param name="options"> The options for configuring the client. </param>
        /// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> or <paramref name="credential"/> is null. </exception>
        public ConversationAnalysisClient(Uri endpoint, AzureKeyCredential credential, ConversationsClientOptions options)
        {
            Argument.AssertNotNull(endpoint, nameof(endpoint));
            Argument.AssertNotNull(credential, nameof(credential));
            options ??= new ConversationsClientOptions();

            ClientDiagnostics = new ClientDiagnostics(options, true);
            _keyCredential0 = credential;
            _pipeline = HttpPipelineBuilder.Build(options, Array.Empty<HttpPipelinePolicy>(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential0, AuthorizationHeader0) }, new ResponseClassifier());
            _endpoint = endpoint;
            _apiVersion = options.Version;
        }

I'll update the issue title as I think this is caused by just having multiple clients take the same ClientOptions. I can probably work around this by taking control of the constructors myself (manual vs. generated) but would be ideally done right by the generator.

heaths commented 2 years ago

I think I know what's happening. After fixing up some code, I noticed that there's two sets of headers generated for auth:

        private const string AuthorizationHeader = "Ocp-Apim-Subscription-Key";
        private readonly AzureKeyCredential _keyCredential;
        private const string AuthorizationHeader0 = "Ocp-Apim-Subscription-Key";
        private readonly AzureKeyCredential _keyCredential0;

The 2 duplicate constructors might be caused by whatever is leading to this.

That said, the _keyCredential* fields aren't even used outside the constructor. The field is set and then used to construct the pipeline. This problem would also be solved - and memory saved - by not assigning this to a field. Afterall, what use does it have after the pipeline is generated?

That said, I'm not sure how I can fix this cleanly. I could perhaps use the field in throw-away code, but that's a waste of computing resources.

/cc @KrzysztofCwalina

pallavit commented 9 months ago

@lirenhe not sure if this issue is fixed or still valid, could you please help triage and assign to the right folks.

lirenhe commented 9 months ago

@pshao25, could you take a look? Seems this is related to the Swagger path, does typespec input also have this problem?

pshao25 commented 9 months ago

@heaths Would you check whether this solves problem? https://github.com/Azure/azure-sdk-for-net/pull/40886

heaths commented 9 months ago

PR #40886 is correct and signed off and merged it, but how did that work? Or is that the default now, which is also fine? If so, we can close this as resolved.

pshao25 commented 9 months ago

I didn't do any fix just removed the customization. It's been quite a long time so I think it was fixed by someone. I'll close this and if there still have problems please let me know.