microsoft / microsoft/typespec
Have an explicit way of defining the api version parameter
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
# Explicit way to define api version parameter/value
Currently our emitters make the assumption the api version parameter is named `api-version` (with maybe a few other alternatives). This doesn't allow for alternate names.
A second issue is that this has to be used as a parameter. You cannot hard code that value in anyway.
## Part 1: Define a known type correspdoning to the current api version
### Option 1: Use the Version enum itself
Currently the `Version` enum is not actually being mutated to only contain the current api version member but we could change that.
```tsp
@versioned(Versions)
@server("https://my-domain.com/api/{version}", "Main entry point", {version: Versions})
namespace MyNS;
enum Versions {v1, v2}
```
[playground](https://typespec.io/playground/?e=%40typespec%2Fopenapi3&c=aW1wb3J0ICJAdHlwZXNwZWMvaHR0cCI7CtIZdmVyc2lvbmluZyI7Cgp1c2luZyBIdHRwO8cMVskgOwoKQHNlcnZpY2UKQMc3ZWQoxyFzKcYeZXIoCiAgIsRwczovL215LWRvbWFpbi5jb20vYXBpL3vHO30iLMQpTWFpbiBlbnRyeSBwb2ludMUWewogICAgxyk66ACEc8QbfQopCm5hbWVzcGFjZSBNeU5TOwoKZW51bckmxT52McQtdjIsCn0K&options=%7B%7D&vs=%7B%7D)
currently produces:
```yaml
#--- v1.yaml
servers:
- url: https://my-domain.com/api/{version}", "Main entry point")
description: Main entry point
parameters:
version:
default: ''
enum:
- v1
- v2
#--- v2.yaml
servers:
- url: https://my-domain.com/api/{version}", "Main entry point")
description: Main entry point
parameters:
version:
default: ''
enum:
- v1
- v2
```
but if the versions enum is mutated it would produce:
```yaml
#--- v1.yaml
servers:
- url: https://my-domain.com/api/{version}", "Main entry point")
description: Main entry point
parameters:
version:
default: ''
enum:
- v1
#--- v2.yaml
servers:
- url: https://my-domain.com/api/{version}", "Main entry point")
description: Main entry point
parameters:
version:
default: ''
enum:
- v2
```
emitters can also rely in parallel on this to know this is the api version and treat it specially if needed.
#### Issues with this approach
- For Azure templates that are composed and inject api version parameter automatically there is not way to get the `Versions` enum automatically. It should need to be passed to every interface factory/operations making it quite cumbersome.
- Versions is in a sense a special configuration enum and might not really make sense to be used as a type in the APIs
### Option 2: Define an `apiVersion` scalar
```tsp
scalar apiVersion extends string {
init current();
}
```
This scalar can be specially treated by emitters to know it is the api version parameter.
This solve the above problem allowing this parameter to be automatically added to operation templates.
As option 1 also makes sense to just work, this could be seen as an alternative to option 1. Emitters could check for both.
If worried about general usage, this scalar could be defined in `Azure.Core` instead.
## Part 2: Replacing the api version parameter with the version in the URL
In many cases users do not want the api version to be exposed as a parameter but directly encoded in the URL.
In the example above the openapi should be
```yaml
#--- v1.yaml
servers:
- url: https://my-domain.com/api/v1", "Main entry point")
description: Main entry point
#--- v2.yaml
servers:
- url: https://my-domain.com/api/v2", "Main entry point")
description: Main entry point
```
### Option 1: Emitter option
As emitters commonly drop constants parameter and set the value behind the scene this feels similar and is in a sense a problem specific to the openapi emitter.
```yaml
options:
@typespec/openapi3:
api-version-parameter: "flatten"
```
This would not only produce the following openapi for server params
```yaml
#--- v1.yaml
servers:
- url: https://my-domain.com/api/v1", "Main entry point")
description: Main entry point
#--- v2.yaml
servers:
- url: https://my-domain.com/api/v2", "Main entry point")
description: Main entry point
```
but also in query param cases
```tsp
@route("/test/{param}")
op test(param: string, @query apiVersion: Versions): void;
```
would produce
```yaml
#--- v1.yaml
paths:
/test/{param}?api-version=v1:
get:
...
#--- v2.yaml
paths:
/test/{param}?api-version=v2:
get:
...
```
### Option 2: String interpolation
Instead of using a parameter just include the version in the URL directly using string interpolation.
```tsp
@versioned(Versions)
@server("https://my-domain.com/api/${Versions}", "Main entry point")
namespace MyNS;
enum Versions {v1, v2}
```
```tsp
@route("/test/{param}?api-version=${Versions}")
op test(param: string): void;
```
Problem with this is that now this string template is not rendered as a string as neither `Versions` enum or the `apiVersion.current()` scalar are string values.
This would require the `@route` and `@server` decorator to allow `StringTemplate` on top of string value so it can specially handle this case.
Contributor guide
Assessment
This issue has not been assessed yet.