Basic methods
This page documents the default generation logic for client’s basic method as well as how to customize it. For advanced paging, long-running or multipart operations, see the Paging Operations, Long-Running Operations and Multipart Operations pages.
Convenience and protocol methods
Section titled “Convenience and protocol methods”Default behaviors
Section titled “Default behaviors”By default, any language code generator will generate both protocol methods and convenient methods for an operation.
NOTE: Python and Typescript don’t have a separation of convenient/protocol methods.
namespace PetStoreNamespace;
/** This is the input I need */@resource("output")model OutputModel { /** Id of this object */ @key @visibility(Lifecycle.Read) name: string;}
/** Read my resource */op GetModel is ResourceRead<OutputModel>;
class OutputModel: name: str = rest_field(readonly=True)response: OutputModel = client.get(name="name")
namespace PetStoreNamespace.Models{ public partial class OutputModel { public string Name { get; } }}
namespace PetStoreNamespace{ public partial class PetStoreNamespaceClient { // protocol method public virtual async Task<Response> GetModelAsync(string name, RequestContext context) {} public virtual Response GetModel(string name, RequestContext context) {} // convenience method public virtual async Task<Response<OutputModel>> GetModelAsync(string name, CancellationToken cancellationToken = default) {} public virtual Response<OutputModel> GetModel(string name, CancellationToken cancellationToken = default) {} }}
interface OutputModel { name: string;}
const model: OutputModel = await client.path("/petStore/model/{name}").get();
package petstorenamespace.models;public final class OutputModel { public String getName()}
package petstorenamespace;public final class PetStoreNamespaceClient { // protocol method public Response<BinaryData> getModelWithResponse(String name, RequestOptions requestOptions) // convenient method public OutputModel getModel(String name)}
Customizations
Section titled “Customizations”The detailed generation configuration of protocol and/or convenient methods that can be done:
As emitters global parameters:
generate-protocol-methods
: boolean flag to shift the entire generation for the process (true
by default)generate-convenience-methods
: boolean flag to shift the entire generation for the process (true
by default)
To set global emitters parameters, read the documentation of emitters configuration.
For fine tuning, the set of decorators @protocolAPI
and @convenientAPI
can be used. They take a required boolean as parameter.
Shifting the generation of protocol and convenience on and off
Section titled “Shifting the generation of protocol and convenience on and off”This can be achieved with the augment operator and the emitter package
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@@convenientAPI(PetStoreNamespace.GetModel, false);
# Python do not change behavior based on protocolAPI or convenientAPI
// Model class is not generated.// Convenient method is not generated.
namespace PetStoreNamespace{ public partial class PetStoreNamespaceClient { // protocol method public virtual async Task<Response> GetModelAsync(string name, RequestContext context) {} public virtual Response GetModel(string name, RequestContext context) {} }}
// Typescript do not change behavior based on protocolAPI or convenientAPI
// Model class is not generated.// Convenient method is not generated.
public final class PetStoreNamespaceClient { // protocol method public Response<BinaryData> getModelWithResponse(String name, RequestOptions requestOptions)}
Make methods private/internal
Section titled “Make methods private/internal”Sometimes it may be useful to still generate the method, but to make it private, so it can be re-used by a manual code wrapper.
The two possible value for the Access
enum are internal
and public
.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@@access(PetStoreNamespace.GetModel, "internal");
# can't import form models directlyfrom petstorenamespace.models import GetModel # will report error
// Model class is internalnamespace PetStoreNamespace.Models{ internal partial class OutputModel { public string Name { get; } }}// Client method is internalnamespace PetStoreNamespace{ public partial class PetStoreNamespaceClient { //protocol method internal virtual async Task<Response> GetModelAsync(string name, RequestContext context) {} internal virtual Response GetModel(string name, RequestContext context) {} //convenience method internal virtual async Task<Response<OutputModel>> GetModelAsync(string name, CancellationToken cancellationToken = default) {} internal virtual Response<OutputModel> GetModel(string name, CancellationToken cancellationToken = default) {} }}
// Typescript do not change behavior based on protocolAPI or convenientAPI
// Model class resides in implementation package.// Client method is package private.
package petstorenamespace.implementation.models;public final class OutputModel { public String getName()}
package petstorenamespace;public final class PetStoreNamespaceClient { // protocol method Response<BinaryData> getModelWithResponse(String name, RequestOptions requestOptions) // convenient method OutputModel getModel(String name)}
Decide the usage of a model
Section titled “Decide the usage of a model”Models can be used for input, output, or both at the same time. In some languages, this changes the way the API is exposed for those models.
By default, the code generator will infer the usage based on the TypeSpec. If this inference doesn’t
correspond to expectation, this can be customized with the usage
decorator. Possible values are
input
and output
, and can be combined with Usage.input | Usage.output
.
NOTE: If a model is never used, it will not be generated. Assigning a usage will force generation.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
// This model is input only@@usage(Azure.OpenAI.AzureCognitiveSearchIndexFieldMappingOptions, Usage.input);// This models is input/output@@usage(Azure.OpenAI.ImageGenerations, Usage.input | Usage.output);
# Python doesn't generate different code based on usage# However, the model may not be generated if it's never used# In that case, set a usage for the model
// If a model is input-only, it has one public constructor with all required properties as parameters.// The required properties only have getter. Optional properties have both getter and setter.// A collection property which is not nullable only has getter whatever it is required or not.namespace Azure.AI.OpenAI.Models{ public partial class AzureCognitiveSearchIndexFieldMappingOptions { public AzureCognitiveSearchIndexFieldMappingOptions()
public string TitleFieldName { get; set; } public string UrlFieldName { get; set; } public string FilepathFieldName { get; set; } public IList<string> ContentFieldNames { get; } public string ContentFieldSeparator { get; set; } public IList<string> VectorFieldNames { get; } public IList<string> ImageVectorFieldNames { get; } }}// If a model is output-only, it does not have any public constructor, and all properties only have getter, no setter.
// If a model is roundtrip (input + output), it has one public constructor with all required properties as parameters.// All properties except colletion properties which are not nullable will have both getter and setter.// A collection property which is not nullable only has getter.namespace Azure.AI.OpenAI.Models{ public partial class ImageGenerations { public ImageGenerations(DateTimeOffset created, IEnumerable<ImageGenerationData> data)
public DateTimeOffset Created { get; set; } public IList<ImageGenerationData> Data { get; } }}
// JS doesn't generate different code based on usage// However, the model may not be generated if it's never used// In that case, set a usage for the model
// If a model class is output-only, its constructor is not public.// Adding Usage.input to it would make its constructor public.
package azure.openai.models;
public final class AzureCognitiveSearchIndexFieldMappingOptions { public AzureCognitiveSearchIndexFieldMappingOptions() public String getTitleField() public AzureCognitiveSearchIndexFieldMappingOptions setTitleField(String titleField) ...}
public final class ImageGenerations { public ImageGenerations(OffsetDateTime createdAt, List<ImageGenerationData> data) public OffsetDateTime getCreatedAt() public List<ImageGenerationData> getData()}
Basic method signature
Section titled “Basic method signature”Simple methods
Section titled “Simple methods”model User { firstName: string; lastName: string;}
@getop get(): User;
@postop post(@body body: User): void;
def get() -> User: ...
@overloaddef post(body: User, **kwargs: Any) -> None: ...
@overloaddef post(body: JSON, **kwargs: Any) -> None: ...
@overloaddef post(body: IO[bytes], **kwargs: Any) -> None: ...
def post(body: [User, JSON, IO[bytes]], **kwargs: Any) -> None: ...
//protocol methodpublic virtual async Task<Response> GetUserAsync(RequestContext context){}public virtual Response GetUser(RequestContext context){}//convenience methodpublic virtual async Task<Response<User>> GetUser(CancellationToken cancellationToken = default)public virtual Response<User> GetUser(CancellationToken cancellationToken = default)
//protocol methodpublic virtual async Task<Response> PostAsync(RequestContent content, RequestContext context = null)public virtual Response Post(RequestContent content, RequestContext context = null)//convenience methodpublic virtual async Task<Response> PostAsync(User user, CancellationToken cancellationToken = default)public virtual Response Post(User user, CancellationToken cancellationToken = default)
// from user experience perspective
// RLCexport interface GetUser200Response extends HttpResponse { status: "200"; body: User;}
export type DemoServiceContext = Client & { path: { /** Resource for '/users' has methods for the following verbs: get */ (path: "/users"): { get( options: RequestParameters, ): StreamableMethod<GetUser200Response | GetUserDefaultResponse>; };
/** Resource for '/users' has methods for the following verbs: post */ (path: "/users"): { post( options: { body: User; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular Api Layerexport async function get(): Promise<User>;export async function post(body: User, options: PostOptionalParams): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { get(options: GetUserOptionalParams): Promise<User>; post(body: User, options: PostOptionalParams): Promise<void>;}
public User get();public void post(User user);
type ClientGetOptions struct {}
type ClientGetResponse struct {}
func (client *Client) Get(ctx context.Context, options *ClientGetOptions) (ClientGetResponse, error)
type ClientPostOptions struct {}
type ClientPostResponse struct {}
func (client *Client) Post(ctx context.Context, user User, options *ClientPostOptions) (ClientPostResponse, error)
Spread cases
Section titled “Spread cases”Please exercise caution when using the spread feature.
- The model to be spread should have fewer than 6 settable properties. See simple methods.
- The model to be spread should remain stable across api-versions. Adding an optional property across api-versions could result in one additional method overload in SDK client.
- The model to be spread should not be used in JSON Merge Patch.
alias User = { firstName: string; lastName: string;};
@postop upload(...User): void;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
def upload(first_name: str, last_name: str) -> None: ...
//protocol methodpublic virtual async Task<Response> UploadAsync(RequestContent content, RequestContext context = null)public virtual Response Upload(RequestContent content, RequestContext context = null)//convenience methodpublic virtual async Task<Response> UploadAsync(string firstName, string lastName, CancellationToken cancellationToken = default)public virtual Response Upload(string firstName, string lastName, CancellationToken cancellationToken = default)
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/completions' has methods for the following verbs: post */ (path: "/users"): { post( options: { body: { firstName: string; lastName: string; }; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular Api Layerexport async function upload( firstName: string, lastName: string, options: UploadOptionalParams,): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { upload(firstName: string, lastName: string, options: UploadOptionalParams): Promise<void>;}
public void upload(String firstName, String lastName);
type ClientUploadOptions struct {}
type ClientUploadResponse struct {}
func (client *Client) Upload(ctx context.Context, firstName string, lastName string, options *ClientUploadOptions) (ClientUploadResponse, error)
Alias with @header/@query/@path properties
Section titled “Alias with @header/@query/@path properties”alias User = { @path id: string; firstName: string; lastName: string;};
op upload(...User): void;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
def upload(self, id: str, first_name: str, last_name: str, *, content_type: str = "application/json") -> None: ...
//protocol methodpublic virtual async Task<Response> UploadAsync(string id, RequestContent content, RequestContext context = null)public virtual Response Upload(string id, RequestContent content, RequestContext context = null)//convenience methodpublic virtual async Task<Response> UploadAsync(string id, string firstName, string lastName, CancellationToken cancellationToken = default)public virtual Response Upload(string id, string firstName, string lastName, CancellationToken cancellationToken = default)
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/completions' has methods for the following verbs: post */ ( path: "/users/{id}", id, ): { post( options: { body: { firstName: string; lastName: string; }; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular Api Layerexport async function upload( id: string, firstName: string, lastName: string, options: UploadOptionalParams,): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { upload( id: string, firstName: string, lastName: string, options: UploadOptionalParams, ): Promise<void>;}
public void upload(String id, String firstName, String lastName);
type ClientUploadOptions struct {}
type ClientUploadResponse struct {}
func (client *Client) Upload(ctx context.Context, id string, firstName string, lastName string, options *ClientUploadOptions) (ClientUploadResponse, error)
Named model
Section titled “Named model”model User { firstName: string; lastName: string;}
op upload(...User): void;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
def upload(self, first_name: str, last_name: str, *, content_type: str = "application/json") -> None: ...
public partial class User{ public User(string firstName, string lastName) { } public string FirstName { get; } public string LastName { get; }}//protocol methodpublic virtual async Task<Response> UploadAsync(RequestContent content, RequestContext context = null)public virtual Response Upload(RequestContent content, RequestContext context = null)//convenience methodpublic virtual async Task<Response> UploadAsync(User user, CancellationToken cancellationToken = default)public virtual Response Upload(string firstName, string lastName, CancellationToken cancellationToken = default)
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/completions' has methods for the following verbs: post */ (path: "/users"): { post( options: { body: { firstName: string; lastName: string; }; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular Api Layerexport async function upload( firstName: string, lastName: string, options: UploadOptionalParams,): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { upload(firstName: string, lastName: string, options: UploadOptionalParams): Promise<void>;}
public void upload(String firstName, String lastName);
type ClientUploadOptions struct {}
type ClientUploadResponse struct {}
func (client *Client) Upload(ctx context.Context, firstName string, lastName string, options *ClientUploadOptions) (ClientUploadResponse, error)
Model with @body
property
Section titled “Model with @body property”model User { firstName: string; lastName: string;}
model UserRequest { @body user: User;}
op upload(...UserRequest): void;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
def upload(self, body: [User, JSON, IO[bytes]], *, content_type: str = "application/json") -> None: ...
public partial class User{ public User(string firstName, string lastName){} public string FirstName { get; } public string LastName { get; }}//protocol methodpublic virtual async Task<Response> UploadAsync(RequestContent content, RequestContext context = null)public virtual Response Upload(RequestContent content, RequestContext context = null)//convenience methodpublic virtual async Task<Response> UploadAsync(User user, CancellationToken cancellationToken = default)public virtual Response Upload(User user, CancellationToken cancellationToken = default)
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/completions' has methods for the following verbs: post */ (path: "/users"): { post( options: { body: User; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular Api Layerexport async function upload(user: User, options: UploadOptionalParams): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { upload(user: User, options: UploadOptionalParams): Promise<void>;}
// Model class@Immutablepublic final class User implements JsonSerializable<User> { public User(String firstName, String lastName); public String getFirstName(); public String getLastName();}
// Client APIpublic void upload(User user);
type User struct { firstName *string lastName *string}
type ClientUploadOptions struct {}
type ClientUploadResponse struct {}
func (client *Client) Upload(ctx context.Context, user User, options *ClientUploadOptions) (ClientUploadResponse, error)
Model with @header/@query/@path properties
Section titled “Model with @header/@query/@path properties”model BlobProperties { @path name: string;
@header testHeader: string;}
@route("blob_properties/{name}")op getBlobProperties(...BlobProperties): void;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
def get_blob_properties(self, name: str, *, test_header: string, content_type: str = "application/json") -> None: ...
//protocol methodpublic virtual async Task<Response> GetBlobPropertiesAsync(string name, string testHeader, RequestContext context = null)public virtual Response GetBlobProperties(string name, string testHeader, RequestContext context = null)
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/completions' has methods for the following verbs: post */ (path: "/blob_properties/{name}", name): { post( options: { headers: { "test-header": string; } & RawHttpHeaders } & RequestParameters ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular Optionsexport interface GetBlobPropertiesOptionalParams extends OperationOptions { requestOptions: { headers: { test-header: string } & RawHttpHeadersInput }}
// Modular api layerexport async function getBlobProperties(name: string, options: GetBlobPropertiesOptionalParams): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { getBlobProperties(name: string, options: GetBlobPropertiesOptionalParams): Promise<void>}
public void getBlobProperties(String name, String testHeader);
type ClientGetBlobPropertiesOptions struct {}
type ClientGetBlobPropertiesResponse struct {}
func (client *Client) GetBlobProperties(ctx context.Context, name string, testHeader string, options *ClientGetBlobPropertiesOptions) (ClientGetBlobPropertiesResponse, error)
Model mixed with normal and @header/@query/@path properties
Section titled “Model mixed with normal and @header/@query/@path properties”model Schema { @header contentType: "application/json"; schema: bytes;}
@postop register(...Schema): void;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
class Schema: schema: bytes
def register(self, body: [Schema, JSON, IO[bytes]], *, content_type: str = "application/json") -> None: ...
public partial class Schema{ public Schema(BinaryData schemaProperty){} public BinaryData SchemaProperty { get; }}//protocol methodpublic virtual async Task<Response> RegisterAsync(RequestContent content, RequestContext context = null)public virtual Response Register(RequestContent content, RequestContext context = null)//convenience methodpublic virtual async Task<Response> RegisterAsync(Schema schema, CancellationToken cancellationToken = default)public virtual Response Register(Schema schema, CancellationToken cancellationToken = default)
// from user experience perspective
export type DemoServiceContext = Client & { path: { /** Resource for '/completions' has methods for the following verbs: post */ (path: "/schemaRegister"): { post( options: { headers: { "content-type": "application/json"; } & RawHttpHeaders; body: { schema: string; }; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular modelexport interface Schema { schema: string;}
// Modular api layerexport async function register( body: Schema, options: GetBlobPropertiesOptionalParams,): Promise<void>;
// Modular classical client layerexport class DemoServiceClient { getBlobProperties(body: Schema, options: GetBlobPropertiesOptionalParams): Promise<void>;}
// Model class@Immutablepublic final class Schema implements JsonSerializable<Schema> { public Schema(byte[] schema); public byte[] getSchema();}
// Client APIpublic void register(Schema schema);
type ClientRegisterOptions struct {}
type ClientRegisterResponse struct {}
func (client *Client) Register(ctx context.Context, schema []byte, options *ClientRegisterOptions) (ClientRegisterResponse, error)
Using Azure.Core.ResourceOperations template
Section titled “Using Azure.Core.ResourceOperations template”Resource create and update operations are not impacted by spread since they all have explicit defined body parameter. Only resource action operations are impacted by spread.
If the action parameter is a model, then the model will be spread.
@resource("widgets")model Widget { @key("widgetName") name: string;}
model RepairInfo { problem: string; contact: string;}
model RepairResult { reason: string; info: string;}
alias Operations = Azure.Core.ResourceOperations<{}>;
op scheduleRepairs is Operations.ResourceAction<Widget, RepairInfo, RepairResult>;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
class RepairInfo: problem: str contact: str
class RepairResult: reason: str info: str
def scheduleRepairs(self, widget_name: str, problem: str, contact: str, *, content_type: str = "application/json") -> RepairResult: ...
// from user experience perspective
export interface RepairInfo { problem: string; contact: string;}
export type WidgetServiceContext = Client & { path: { ( path: "/widgets/{widgetName}:scheduleRepairs", widgetName: string, ): { post( options: { body: RepairInfo; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular api layerexport async function scheduleRepairs( context: Client, widgetName: string, problem: string, contact: string, options: ScheduleRepairsOptionalParams = { requestOptions: {} },): Promise<RepairResult>;
// Modular classical client layerexport class WidgetServiceClient { scheduleRepairs( widgetName: string, problem: string, contact: string, options: ScheduleRepairsOptionalParams = { requestOptions: {} }, ): Promise<RepairResult>;}
public RepairResult scheduleRepairs(String widgetName, String problem, String contact);
type ClientScheduleRepairsOptions struct {}
type RepairResult struct { reason *string info *string}
type ClientScheduleRepairsResponse struct { RepairResult}
func (client *Client) ScheduleRepairs(ctx context.Context, widgetName string, problem string, contact string, options *ClientScheduleRepairsOptions) (ClientScheduleRepairsResponse, error)
If you want to keep the model, you have two options to prevent spreading:
@override
- If you don’t want to do client customizations, you could use a wrapper to explicitly set the body to prevent spread.
@override
The @override
decorator allows you to replace the default client method generated by TCGC based on your service definition.
If your service definition spreads a model into the method signature, but you prefer that the generated client SDKs maintain the model intact, the @override
decorator provides a solution.
Additionally, you can specify a language scope
to limit the changes to a specific language emitter.
namespace Widget.Client;
op scheduleRepairs( body: RepairInfo, `api-version`: Azure.Core.Foundations.ApiVersionParameter,): RepairResult;
@@override(Widget.Service.scheduleRepairs, Widget.Client.scheduleRepairs);
- wrapper
If you prefer not to implement customizations in client.tsp
, you can add @bodyRoot
to the input in main.tsp
prior to passing the model to the template.
namespace Widget.Service;
@resource("widgets")model Widget { @key("widgetName") name: string;}
model RepairInfo { problem: string; contact: string;}
model RepairResult { reason: string; info: string;}
alias Operations = Azure.Core.ResourceOperations<{}>;
alias BodyParameter< T, TName extends valueof string = "body", TDoc extends valueof string = "Body parameter."> = { @doc(TDoc) @friendlyName(TName) @bodyRoot body: T;};
op scheduleRepairs is Operations.ResourceAction<Widget, BodyParameter<RepairInfo>, RepairResult>;
For Python, we will also generate the overloads described in the HTTP Post section, but will omit them for brevity.
class RepairInfo: problem: str contact: str
class RepairResult: reason: str info: str
def scheduleRepairs(self, body: [Schema, JSON, IO[bytes]], *, content_type: str = "application/json") -> RepairResult: ...
// from user experience perspective
export interface RepairInfo { problem: string; contact: string;}
export type WidgetServiceContext = Client & { path: { ( path: "/widgets/{widgetName}:scheduleRepairs", widgetName: string, ): { post( options: { body: RepairInfo; } & RequestParameters, ): StreamableMethod<PostUserDefaultResponse>; }; };};
// Modular api layerexport async function scheduleRepairs( context: Client, widgetName: string, body: RepairInfo, options: ScheduleRepairsOptionalParams = { requestOptions: {} },): Promise<RepairResult>;
// Modular classical client layerexport class WidgetServiceClient { scheduleRepairs( widgetName: string, body: RepairInfo, options: ScheduleRepairsOptionalParams = { requestOptions: {} }, ): Promise<RepairResult>;}
// Model class@Immutablepublic final class RepairInfo implements JsonSerializable<RepairInfo> { public RepairInfo(String problem, String contact); public String getProblem(); public String getContact();}
@Immutablepublic final class RepairResult implements JsonSerializable<RepairResult> { public String getReason(); public String getInfo();}
// Client APIpublic RepairResult scheduleRepairs(String widgetName, RepairInfo body);
type RepairInfo struct { problem *string contact *string}
type ClientScheduleRepairsOptions struct {}
type RepairResult struct { reason *string info *string}
type ClientScheduleRepairsResponse struct { RepairResult}
func (client *Client) ScheduleRepairs(ctx context.Context, widgetName string, body RepairInfo, options *ClientScheduleRepairsOptions) (ClientScheduleRepairsResponse, error)
One path for multiple input/output
Section titled “One path for multiple input/output”The following sections documents emitter behavior and customization when you use union operator |
or @sharedRoute
to express multiple input/output for a given path.
Default behaviors
Section titled “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
Section titled “Customizations”Merge @sharedRoute
operations into one.
Section titled “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
Conditional request headers
Section titled “Conditional request headers”The following sections document how to generate client libraries for conditional request headers such as If-Match
and If-None-Match
.
model Response { name: string;}
@getop get( /** The request should only proceed if an entity matches this string. */ @header("If-Match") ifMatch?: string,
/** The request should only proceed if no entity matches this string. */ @header("If-None-Match") ifNoneMatch?: string,): Response;
from azure.core import MatchConditions
def get(self, *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None) -> Response:
# TODO
# TODO
import com.azure.core.http.MatchConditions;
public Response get(MatchConditions matchConditions);
type ClientGetOptions struct { // The request should only proceed if an entity matches this string. IfMatch *string
// The request should only proceed if no entity matches this string. IfNoneMatch *string}
func (client *Client) Get(ctx context.Context, options *ClientGetOptions) (ClientGetResponse, error)