You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I wanted to propose some emitter design guidelines that can make it easier to write a new emitter and provide consistency across different emitter designs. I hope this can eventually be incorporated in the writing an emitter section of the TypeSpec documentation.
Reasonable correctness over absolute precision: Emitters should prioritize practical solutions for most use cases, accepting that some edge cases might not be perfectly handled. The aim is to emit a wide range of schemas reliably, with a focus on reasonable correctness rather than absolute precision. This would mean leaning towards generating a best effort output rather than failing the entire emitter process.
Native TSP Representation and emitter specific decorators: All protocol concepts supported by the emitter should be described using the following guidelines:
The decorators, data types, and concepts provided by the standard TSP library should be used to represent features in the output protocol rather than requiring emitter specific decorators as long as they remain true to the TSP library's emitter-agnostic intentions and do not make expressing certain concepts in TSP less flexible due to specific emitter requirements.
If a direct translation of an output protocol feature is not available in TSP, emitters should introduce emitter-specific decorators to map or transform existing TSP feature(s) to an emitted protocol specific concept. For instance, the @field decorator in the Protobuf emitter adds information needed to determine the order of fields in Protobuf. The emitter specific decorators should be a part of the emitter rather than the TSP library if they are needed to describe a protocol specific concept.
If a concept cannot be adequately represented with the current TSP language capabilities and decorators, or a better representation in the TSP language would benefit several emitters, the TSP language should be extended to accommodate it. For instance, TSP could support a distinction between input and output types benefitting several language emitters and GraphQL.
All TSP features/concepts should either be translated by the emitter into the output protocol or ignored if they don’t apply to the output protocol (e.g. extends for GraphQL).
Emitter-Specific Decorators Correctness: These decorators should not alter the model exclusively for a specific protocol. They should be linked to a representation in TSP that translates directly across other protocols. This way, the emitter-specific decorator can be ignored for other protocols while still accurately reflecting the developer’s intention. For instance, the GraphQL specific @compose decorator should be tightly coupled with the types used in the spread operator as we want the final type in OpenAPI and Protobuf to be composed of the same types as in GraphQL. Similarly, OpenAPI specific decorators should not work in isolation and change the shape of the model only for OpenAPI. For instance, @oneOf has no effect in GraphQL emission as TSP unions always match with just one variant in GraphQL, so there is no need for a special @oneOf decorator in GraphQL.
Handling Incompatibilities: All emitters should handle cases where TSP representations or types are incompatible with the output protocol. Doing this will facilitate the re-use of the same TSP schema across multiple protocols. Emitters should not ignore incompatible types as that would result in an incorrect schema in a multiprotocol environment. Options include:
Emitting an output protocol type equivalent to the unknown type in TSP. For example, converting unsupported GraphQL input unions to an Any type rather than failing.
Coerce incompatible types to a compatible type in the output protocol using deterministic rules, such as expanding child unions at the top-level in GraphQL.
Emitters should not apply overly opinionated transformations when dealing with incompatible types as such approaches may not always be effective or meaningful. For instance, converting a datetime scalar in TSP to a specific type in GraphQL that assumes that all datetime scalars will be represented a certain way.
Reflecting Developer Intentions: Emitted types should reflect the developer's intentions in TSP, guiding the design for type emission in the output protocol. For instance, operations in TSP represent an endpoint that takes in parameters and has a return type. For endpoints that operate on a given entity like a Widget, the REST library in TSP provides the @route decorator to indicate GET operation on /widgets/{widget_id}/parts/{part_id}. Similarly, a GraphQL specific decorator can indicate a field called part on the Widget type that takes in a partId parameter and returns a Part type. In both these cases, the emitter is reflecting the developer’s intention of querying a Part based on an id on a specific Widget.
Spec Compliance and evolution: Emitters should generate a spec compliant output protocol schema without additional plugins or extensions. For instance, the graphql emitter should generate a schema in accordance with the published GraphQL specification. Practically, this means that even if there is a widely accepted and open source library or pattern for a commonly encountered feature, the emitter should not rely on it when generating the output. Emitters should evolve to incorporate any changes to the specification.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi team,
I wanted to propose some emitter design guidelines that can make it easier to write a new emitter and provide consistency across different emitter designs. I hope this can eventually be incorporated in the writing an emitter section of the TypeSpec documentation.
@field
decorator in the Protobuf emitter adds information needed to determine the order of fields in Protobuf. The emitter specific decorators should be a part of the emitter rather than the TSP library if they are needed to describe a protocol specific concept.input
andoutput
types benefitting several language emitters and GraphQL.extends
for GraphQL).@compose
decorator should be tightly coupled with the types used in the spread operator as we want the final type in OpenAPI and Protobuf to be composed of the same types as in GraphQL. Similarly, OpenAPI specific decorators should not work in isolation and change the shape of the model only for OpenAPI. For instance,@oneOf
has no effect in GraphQL emission as TSP unions always match with just one variant in GraphQL, so there is no need for a special@oneOf
decorator in GraphQL.unknown
type in TSP. For example, converting unsupported GraphQL input unions to anAny
type rather than failing.datetime
scalar in TSP to a specific type in GraphQL that assumes that alldatetime
scalars will be represented a certain way.operations
in TSP represent an endpoint that takes in parameters and has a return type. For endpoints that operate on a given entity like aWidget
, the REST library in TSP provides the@route
decorator to indicate GET operation on/widgets/{widget_id}/parts/{part_id}
. Similarly, a GraphQL specific decorator can indicate a field calledpart
on theWidget
type that takes in apartId
parameter and returns aPart
type. In both these cases, the emitter is reflecting the developer’s intention of querying aPart
based on an id on a specificWidget
.Beta Was this translation helpful? Give feedback.
All reactions