microsoft / typespec

https://typespec.io/
MIT License
4.45k stars 209 forks source link

[Feature] Support apiVersion parameter in sub-client #4803

Open chunyu3 opened 4 days ago

chunyu3 commented 4 days ago

When there are two-level clients , the top-level client has no operations with apiVersion paramter, but there are operations which has api-version parameter in sub-client.

e.g.

@versioned(Versions)
@service({
  title: "hello world",
})
@doc("This is a sample typespec project.")
@server(
  "{unbrandedTypeSpecUrl}",
  "Endpoint Service",
  {
    unbrandedTypeSpecUrl: string,
  }
)
namespace UnbrandedTypeSpec;

@route("/hello")
@doc("hello world.")
op hello(): void;

interface ClientWithApiVersion {
  @route("/WithApiVersion")
  @doc("Return hi again")
  @get
  @convenientAPI(true)
  WithApiVersion(@header p1: string, @query apiVersion: string): void;
}

How apiVersion be defined?

chunyu3 commented 4 days ago

Option 1: bump apiVersion field to the top-level client, and it will not be a paramter in the GetAccessor method

//parent client
public partial class UnbrandedTypeSpecClient
{
        private readonly Uri _endpoint;
        private const string AuthorizationHeader = "my-api-key";
        /// <summary> A credential used to authenticate to the service. </summary>
        private readonly ApiKeyCredential _keyCredential;
        private ClientWithApiVersion _cachedClientWithApiVersion;
        private string _apiVersion;

       public virtual ClientWithApiVersion GetClientWithApiVersionClient()
        {
            return Volatile.Read(ref _cachedClientWithApiVersion) ?? Interlocked.CompareExchange(ref _cachedClientWithApiVersion, new ClientWithApiVersion(Pipeline, _keyCredential, _endpoint, _apiVersion), null) ?? _cachedClientWithApiVersion;
        }
}

//sub-client
public partial class ClientWithApiVersion
{
        private readonly Uri _endpoint;
        private const string AuthorizationHeader = "my-api-key";
        /// <summary> A credential used to authenticate to the service. </summary>
        private readonly ApiKeyCredential _keyCredential;
        private readonly string _apiVersion;

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

        internal ClientWithApiVersion(ClientPipeline pipeline, ApiKeyCredential keyCredential, Uri endpoint, string apiVersion)
        {
            _endpoint = endpoint;
            Pipeline = pipeline;
            _keyCredential = keyCredential;
            _apiVersion = apiVersion;
}

Option 2: apiVersion only be the field of the sub-client, and there will be an paramter in the GetAccessor method

//parent client
public partial class UnbrandedTypeSpecClient
{
        private readonly Uri _endpoint;
        private const string AuthorizationHeader = "my-api-key";
        /// <summary> A credential used to authenticate to the service. </summary>
        private readonly ApiKeyCredential _keyCredential;
        private ClientWithApiVersion _cachedClientWithApiVersion;

       public virtual ClientWithApiVersion GetClientWithApiVersionClient(string aipVersion)
        {
            return Volatile.Read(ref _cachedClientWithApiVersion) ?? Interlocked.CompareExchange(ref _cachedClientWithApiVersion, new ClientWithApiVersion(Pipeline, _keyCredential, _endpoint, apiVersion), null) ?? _cachedClientWithApiVersion;
        }
}

//sub client
public partial class ClientWithApiVersion
{
        private readonly Uri _endpoint;
        private const string AuthorizationHeader = "my-api-key";
        /// <summary> A credential used to authenticate to the service. </summary>
        private readonly ApiKeyCredential _keyCredential;
        private readonly string _apiVersion;

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

        internal ClientWithApiVersion(ClientPipeline pipeline, ApiKeyCredential keyCredential, Uri endpoint, string apiVersion)
        {
            _endpoint = endpoint;
            Pipeline = pipeline;
            _keyCredential = keyCredential;
            _apiVersion = apiVersion;
}
chunyu3 commented 4 days ago

I prefer to Option 1. because apiVersion is the service apiVersion, it should be in ClientOptions, all the client in one service should share the same apiVersion(this is the current design) and should not alllow to set value other than the one in the ClientOptions.