question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Two clients using shared `ClientOptions` class results in duplicate 2-param constructors on each client

See original GitHub issue

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:

    [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.

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:17 (17 by maintainers)

github_iconTop GitHub Comments

1reaction
annelo-msftcommented, Jun 18, 2022

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.

1reaction
jsquirecommented, Jun 10, 2022

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

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to deal with 2 almost identical constructors [duplicate]
If I have 2 constructors in my custom class and one of them takes an extra argument and does everything the first one...
Read more >
Item 14: Minimize Duplicate Initialization Logic
This version of MyClass specifies the default value for both parameters. Users could specify different values for either or both parameters.
Read more >
Constructors - C# programming guide
Object. Base instance constructors run. Any instance constructors, starting with Object.Object through each base class to the direct base class.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found