One path for multiple input/output
This page documents emitter behavior and customization when you use union operator |
or @sharedRoute
to express multiple input/output for a given path.
Default behaviors
The simplest way to express a combination of input in TypeSpec is to use the union operator |
.
At a glance, JS and Python supports natively union, while Java and C# will use overloads.
@service({ title: "Analyze", version: "v1",})namespace Analyze;@route("/analyze")@postop analyze(@query mode: "strict" | "lenient", @body image: bytes): AnalyzeResult;
model CompletionInput { input: string | string[];}
@route("/completions")@postop completions(@body input: CompletionInput): CompletionResult;
def analyze(image: bytes, *, mode: Literal["strict", "lenient"]) -> AnalyzeResult: ...
class CompletionInput: input: Union[str, List[str]] = rest_field(readonly=True)
def completions(input: CompletionInput) -> CompletionResult: ...
// Union "strict" | "lenient" will be generate as extensible enumnamespace Analyze.Models{ public readonly partial struct Mode : IEquatable<Mode> { public static Mode Strict { get; } = new Mode(StrictValue); public static Mode Lenient { get; } = new Mode(LenientValue); }}
// other union which cannot be convert to enum will generate as BinaryDatanamespace Analyze.Models{ public partial class CompletionInput { public CompletionInput(BinaryData input) public BinaryData Input { get; } }}namespace Analyze{ public partial class AnalyzeClient { //protocol method public virtual async Task<Response> AnalyzeAsync(string mode, RequestContent content, RequestContext context = null) {} public virtual Response Analyze(string mode, RequestContent content, RequestContext context = null) {} //convenience method public virtual async Task<Response<AnalyzeResult>> AnalyzeAsync(Mode mode, BinaryData image, CancellationToken cancellationToken = default) {} public virtual Response<AnalyzeResult> Analyze(Mode mode, BinaryData image, CancellationToken cancellationToken = default) {}
//protocol method public virtual async Task<Response> CompletionsAsync(RequestContent content, RequestContext context = null) {} public virtual Response Completions(RequestContent content, RequestContext context = null) {} //convenience method public virtual async Task<Response<CompletionResult>> CompletionsAsync(CompletionInput input, CancellationToken cancellationToken = default) {} public virtual Response<CompletionResult> Completions(CompletionInput input, CancellationToken cancellationToken = default) {}
}}
// from user experience perspective
export interface CompletionInput { input: string | string[];}
export type DemoServiceContext = Client & { path: { /** Resource for '/analyze' has methods for the following verbs: post */ (path: "/analyze"): { post( options: { body: string; queryParameters: { mode: "strict" | "lenient"; }; } & RequestParameters, ): StreamableMethod<Analyze200Response | AnalyzeDefaultResponse>; }; /** Resource for '/completions' has methods for the following verbs: post */ (path: "/completions"): { post( options: { body: CompletionInput; } & RequestParameters, ): StreamableMethod<Completions200Response | CompletionsDefaultResponse>; }; };};
public enum Mode { STRICT("strict"), LENIENT("lenient");}
public final class CompletionInput { public CompletionInput(BinaryData input) public BinaryData getInput()}
public final class AnalyzeClient { public Response<BinaryData> analyzeWithResponse(String mode, BinaryData image, RequestOptions requestOptions) public Response<BinaryData> completionsWithResponse(BinaryData input, RequestOptions requestOptions) public AnalyzeResult analyze(Mode mode, byte[] image) public CompletionResult completions(CompletionInput input)}
Using union implies that the entire combination of possible input is valid. If you have a specific set of combination, or connection between input and output,
you must use @sharedRoute
. By default, codegen will generate one method per operation name.
@sharedRoute@route("/foo")op a(x: int32): float;
@sharedRoute@route("/foo")op b(x: string): int64;
def a(x: int) -> float: # code
def b(x: string) -> int: # code
//protocolpublic Response A(RequestContent content, RequestContext context);public Response B(RequestContent content, RequestContext context);
//conveniencepublic Response<float> A(int x, CancellationToken token);public Response<long> B(string x, Cancellation token);
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/foo' has methods for the following verbs: post */ (path: "/foo"): { post( options?: { body?: { x: number; }; } & RequestParameters, ): StreamableMethod<A200Response>; post( options?: { body?: { x: string; }; } & RequestParameters, ): StreamableMethod<B200Response>; }; };};
public final class Client { public Response<BinaryData> aWithResponse(BinaryData request, RequestOptions requestOptions) public Response<BinaryData> bWithResponse(BinaryData request, RequestOptions requestOptions) public double a(int x) public long b(String x)}
Customizations
Merge @sharedRoute
operations into one.
If your shared routes are actually one unique semantic operation, you may want to configure codegen to use a unique name. This is simply done by renaming both operations to the same name using @clientName
@sharedRoute@route("/foo")op a(x: int) : float
@sharedRoute@route("/foo")op b(x: string) : int64
// client.tspimport "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@@clientName(a, "Foo");@@clientName(b, "Foo");
@overloaddef foo(x: int) -> float: ...
@overloaddef foo(x: string) -> int: ...
def foo(x: string | int) -> float | int: # Code here
//protocolpublic Response Foo(RequestContent content, RequestContext context);
//conveniencepublic Response<float> Foo(int x, CancellationToken token);public Response<long> Foo(string x, Cancellation token);
JS RLC is not in the business of customization with client.tsp
NOT_SUPPORTED