-
There should be no need for customizations. A "raw" service specification should be enough to generate a client library.
-
The cost of customizations should be proportional to the amount of customizations we are making. It must not have a steep cost cliff when we want to customize a small aspect of the generated code (a.k.a. "the cost of the first customization should be low").
A set of client code generation decorators and operation templates are introduced into a @azure-tools/cadl-dpg
library.
For minor, targeted customizations, augment (@@
) decorators in a side car file can be used.
For more extensive customizations, a side car file with a parallel set of interfaces that cherry-pick operations can be used.
The following rules apply when interpreting a service specification for client code generation purposes:
-
Unless an explicit
@client
is defined, there is always an implicit global default client instance to which each operation group and method is expected to be associated with.This allows for an empty side-car file.
-
If there is a single
@client
defined, it acts as the default client instance, and all operation groups and methods are associated with it unless overridden. -
If there are multiple
@client
instances, operation groups and methods have to be explicitly associated with the client instance using@extendsClient
or being members of a client interface.
Public decorators are intended for usage by service description authors and client customization side-cars.
Designates the given interface as a client. The apiVersions
parameter indicates which API service API versions should be used to generate code.
Marks the given interface or operation as part of an operation group. If applied to an interface, it will apply to all methods in the interface unless there the operation is explicitly marked with @operationGroup
(most specific marking wins).
Marks the given operation or interface as being a member of the provided client interface. Requires the extends
interface to have been marked with @Dpg.client
. Typical usage is as an augment decorator from a side-car file.
A Constructor
operation indicates that client code generators are expected to provide an affordance to create/initialize a client using the given signature.
A ClientAccessor
operation indcates that an operation as a (sub) client accessor. Client code generators are expected to provide an affordance to, from the given (parent) client access a sub/child client.
The private decorators are generally intended for marking the operation templates. Service spec authors to not use the private decorators, but emitters will use the corresponding accessors to access marked entities.
Marks an operation as a constructor. Applied to the Constructor<TParams>
operation template.
Marks an operation as a (sub) client accessor. Applied to the ClientAccessor<TParams, TClient>
operation template
- Augmentation decorator (
@@
) - Ability to reference all elements that can be decorated (e.g. operation parameters, return value)
// main.cadl
namespace Azure.Stuff;
op oneOp(): void;
op twoOp(): string;
interface Lifetime {
op create(): Thing;
op delete(): void;
}
client.cadl:
@@operationGroup("things", Lifetime)
Resulting Python codegen:
class ThingsOperationGroup:
def create(self) -> Thing: ...
def delete(self) -> None: ...
class Client:
things: ThingsOperationGroup
def one_op(self) -> None: ...
def two_op(self) -> str: ...
main.cadl
op create(): Model;
op train(): void;
op infer(modelId: string): string;
client.cadl
import "./main.cadl";
@Dpg.client
interface AdminClient {
createModel is create;
trainModel is train;
getInferrenceClient is Dpg.ClientAccessor<{modelId: string}, ModelInferrenceClient>;
}
@Dpg.client
interface ModelInferrenceClient {
constructor is Dpg.Constructor<{modelId: string}>;
// Issue; does it matter that the signature should change/client level
// params are excluded? Too much magic?
infer is infer;
}
Resulting Python codegen from cadl compile client.cadl
class AdminClient:
def create_model(self) -> Thing: ...
def train_model(self) -> None: ...
def get_inferrence_client(self) -> ModelInferrenceClient: ...
class ModelInferrenceClient:
def __init__(self, model_id: string): ...
def infer(self) -> string: ...
cadl compile client.cadl
-
The interaction between client constructor parameters and their mapping/binding to operation parameters is currently implicit (e.g. the parameters should "move" from operations to the client constructor). We could require linking of parameters from the constructor to operations, but that would be very verbose.
-
How to associate operation group with client. Operation template "accessor"?
-
(Minor) misspelling an operation group name introduces a new operation group.
- How do we specify the version/tell the emitter which version set (enum) to enumerate over.
- Provide version enum to
client
decorator.
- Provide version enum to
Notes from cadl/dpg sync: