Convenient method generation
This page documents how to customize method generation for the emitters. For an overview of the setup, please visit the setup page.
Default behaviors
By default, any language code generator will generate both protocol methods and convenient methods.
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("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
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
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
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
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()}