Skip to content

Versioning

This doc details what emitters will generate for versioned specs

Single api Version

If there is just one api version in the spec, we will generate the api surface area for that one version.

import "@typespec/versioning";
import "@typespec/http";
using TypeSpec.Versioning;
using TypeSpec.Http;
@versioned(My.Service.Versions)
@service
namespace My.Service;
enum Versions {
v2023_11_01: "2023-11-01",
}
model StableModel {
stableFeature: string;
}
op stableFunctionality(@body stableModel: StableModel): void;
import pytest
from my.service import MyServiceClient, models
client = MyServiceClient(endpoint=..., credential=...)
# client's api_version will be "2023-11-01"
stable_model = models.StableModel(stable_feature="present")
print(stable_model)
client.stable_functionality(stable_model) # call goes through
preview_client = MyServiceClient(endpoint=..., credential=..., api_version="2023-11-01")
# python allows you to override the api version, even if only one version is defined in the spec

Multiple api versions

The configuration flag api-version allows you to toggle the behavior that our emitters will generate.

We will get the versioning information from the Versions enum that you pass to the @versioned decorator from the @typespec/versioning library.

NOTE: The ordering of the values in the Versions enum is very important. We use this information to determine the order of versions. Our default value will be the last entry in the Versions list

Default

By default our emitters will only generate the surface used by the latest api version if there are multiple defined. This includes generating only the models used in the surface area of the latest api version.

Documentation and enums showing the available api versions will still include all of the known api versions, meaning there will be documentation for both the preview and stable releases.

For the below example, all languages will generate the api surface of default version v2023_11_01. There will be no generation of the operation previewFunctionality, and we will also not generate the PreviewModel because it’s only used in previewFunctionality, and therefore is not used in the api surface of v2023_11_01.

import "@typespec/versioning";
import "@typespec/http";
using TypeSpec.Versioning;
using TypeSpec.Http;
@versioned(My.Service.Versions)
@service
namespace My.Service;
enum Versions {
v2023_11_01_preview: "2023-11-01-preview",
v2023_11_01: "2023-11-01",
}
model PreviewModel {
betaFeature: string;
}
model StableModel {
stableFeature: string;
}
@added(Versions.v2023_11_01_preview)
@removed(Versions.v2023_11_01)
op previewFunctionality(@body previewModel: PreviewModel): void;
op stableFunctionality(@body stableModel: StableModel): void;
import pytest
from my.service import MyServiceClient, models
client = MyServiceClient(endpoint=..., credential=...)
# client's api_version will be "2023-11-01"
stable_model = models.StableModel(stable_feature="present")
print(stable_model)
client.stable_functionality(stable_model) # call goes through
with pytest.expect(ImportError):
preview_model = models.PreviewModel(preview_functionality="not present")
with pytest.expect(AttributeError):
client.preview_functionality({"previewFunctionality": "not present"})

Override to a specific version

You can override the signature to return the api surface area for a specific api version.

In this example, you can see how this change is made in tspconfig.yaml, and we are going to override to return the preview api surface area for our spec. The preview api surface area contains all of the functionality.

tspconfig.yaml
options:
"@azure-tools/typespec-python":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-csharp":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-ts":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-java":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-go":
api-version: "2023-11-01-preview"
import "@typespec/versioning";
import "@typespec/http";
using TypeSpec.Versioning;
using TypeSpec.Http;
@versioned(My.Service.Versions)
@service
namespace My.Service;
enum Versions {
v2023_11_01_preview: "2023-11-01-preview",
v2023_11_01: "2023-11-01",
}
model PreviewModel {
betaFeature: string;
}
model StableModel {
stableFeature: string;
}
@added(Versions.v2023_11_01_preview)
@removed(Versions.v2023_11_01)
op previewFunctionality(@body previewModel: PreviewModel): void;
op stableFunctionality(@body stableModel: StableModel): void;
import pytest
from my.service import MyServiceClient, models
preview_client = MyServiceClient(endpoint=..., credential=...)
# client's api_version will be "2023-11-01-preview"
stable_model = models.StableModel(stable_feature="present")
print(stable_model)
preview_client.stable_functionality(stable_model) # call goes through
preview_model = models.PreviewModel(preview_functionality="present")
# the model is generated as part of the api surface
preview_client.preview_functionality(preview_model) # call goes through

Override to return all

You can also override the signature to return the combined api surface area of all of the separate api versions. Different languages have different support for versioning validation.

In the following examples, you can observe how this change is made in tspconfig.yaml.

tspconfig.yaml
options:
"@azure-tools/typespec-python":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-csharp":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-ts":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-java":
api-version: "2023-11-01-preview"
"@azure-tools/typespec-go":
api-version: "2023-11-01-preview"
import "@typespec/versioning";
import "@typespec/http";
using TypeSpec.Versioning;
using TypeSpec.Http;
@versioned(My.Service.Versions)
@service
namespace My.Service;
enum Versions {
v2023_11_01_preview: "2023-11-01-preview",
v2023_11_01: "2023-11-01",
}
model PreviewModel {
betaFeature: string;
}
model StableModel {
stableFeature: string;
}
@added(Versions.v2023_11_01_preview)
@removed(Versions.v2023_11_01)
op previewFunctionality(@body previewModel: PreviewModel): void;
op stableFunctionality(@body stableModel: StableModel): void;
import pytest
from my.service import MyServiceClient, models
client = MyServiceClient(endpoint=..., credential=...)
# client's api_version will be "2023-11-01"
stable_model = models.StableModel(stable_feature="present")
print(stable_model)
client.stable_functionality(stable_model) # call goes through
preview_model = models.PreviewModel(preview_functionality="present")
# the model is generated as part of the api surface
with pytest.expect(ValueError) as ex:
client.preview_functionality(preview_model)
assert "preview_functionality is not available in api version 2023-11-01" in str(ex)
preview_client = MyServiceClient(endpoint=..., credential=..., api_version="2023-11-01-preview")
preview_client.preview_functionality(preview_model) # call goes through

Overriding the Client Api Version Parameter

By default, we find api version parameters in specs based off of names. There is special logic we do with api version parameters:

  1. These api version parameters get elevated up to the client level (if the service is versioned)
  2. We auto-add api version information to next links when paging
  3. We set the client default for these parameters to be the default api version for your service.

There are cases where you have an api-versioning parameter without the explicit name api-version. In these cases, you can use the @isApiVersion decorator to override and explicitly say whether that parameter is an api version param or not.

import "@typespec/versioning";
import "@typespec/http";
import "@azure-tools/typespec-client-generator-core";
using TypeSpec.Versioning;
using TypeSpec.Http;
using Azure.ClientGenerator.Core;
@versioned(My.Service.Versions)
@service
namespace My.Service;
enum Versions {
v2023_11_01: "2023-11-01",
v2024_04_01: "2024-04-01",
}
op get(
@isApiVersion
@header("x-ms-version")
version: string,
): void;
from my.service import MyServiceClient
client = MyServiceClient(endpoint=..., credential=...)
print(client.version) # == "2024-04-01", since that is the default
client_with_specified_api_version = MyServiceClient(endpoint=..., credential=..., version="2023-11-01")
print(client.version) # == "2023-11-01", since we specified
retval = client.get() # version is elevated onto the client