The built-in Bicep local extension `TypeDefinitionBuilder` needs to be more flexible for a better authoring experience
- Dominant language
- Bicep
- Stars
- 3.6k
- Forks
- 830
- Avg merge
- 1d 21m
- Merged PRs (30d)
- 79
Description
## **Is your feature request related to a problem? Please describe.**
While trying to upgrade from 0.36 extensions to 0.37, I have found the bundled type generator to be extremely limiting. This was already the case with the samples from <0.37 where only the most basic types were included but so far the only types supported in the bundled generator have not changed and only include serialization of:
- Primitives (integer, boolean, strings)
- Objects
- Enumerations
This would work for basic extensions. However, more complete extensions requires complex schemas and typing similar to what can be seen in Azure types. Those missing elements missing from the current generator are:
## Nullable types
Currently, it is impossible to use any primitive null types as the generator currently throw on those (`int?`, `bool?`) and while objetcs or enums can be marked as nullable on the C# side. This contract is not respected on the bicep schema.
### Use-case
Currently, it is possible for extension authors to return null values as shown by the samples.
```csharp
[ResourceType("MyResource")]
public class MyResource : MyResourceIdentifiers
{
[TypeProperty("The resource operation type", ObjectTypePropertyFlags.Required)]
[JsonConverter(typeof(JsonStringEnumConverter))]
public OperationType? Operation { get; set; }
[TypeProperty("The text output")]
public string? Output { get; set; }
}
```
```bicep
targetScope = 'local'
extension myextension
param inputText string
resource foo 'MyResource' = {
name: inputText
operation: 'Reverse'
}
output outputText string = foo.output
```
However, because the generator does not serialized `output` as an `UnionType` of `StringType` and `NullType`, the user does not receive any expected warning that a value like `foo.output` could be null. This creates a gap where the user does not receive the recommendation to use `foo.?output` and forces extension authors to create stop gaps like informing about the nullability of their properties through the description of the property.
## Dictionary support
No dictionary of any kind is currently supported by the type generator
### Use-case
When the extension wants any formatted user provided mapping, dictionary are the best format to do so. For example:
- A list of key:value metadata
- A list of files to move with their destination
- A list of containers to create with their properties, where the container names should be unique
Currently, extensions have to use workaround such as multiple arrays and additional server side validation to ensure the unicity of keys
## String, integer and array validation attributes
All strings, integers and arrays are created as "unbounded" types. Bicep provides some built-in validation by allowing to specify the minimum length of a string, a regex pattern to respect, ... for various types. However, extension authors do not have access to those since strings are always referencing the bicep type:
```json
{
"$type": "StringType",
"sensitive": false, // Can be set to true through TypePropertyAttribute
"minLength": null,
"maxLength": null,
"pattern": null
}
```
### Use-case
- A basic regex pattern to validate an email address
- Limiting a list of tags to the maximum amount of tags the service that the extension interacts with supports
- Prevent typos and potentially harmful inputs (ie: entering `cpu: 500` instead of `cpu: 500m`)
Having access as an extension author to the ability to set those properties would reduce the amount of validation required, increase the reliability of the extension and speed-up the authoring speed of users by reducing errors.
## No support for discriminated / union types
An extremely useful feature once added to the <0.37 type generator was the use of `UnionType`. Instead of having a massive object with 50 properties to cover all possible types an object could have. You could have a basic type with a few common properties and several other types for the more specific properties this `UnionType` can have. Greatly reducing the amount of code in a single class.
### Use-case
- Simpler configuration experience. Instead of gaving a type with the properties `authenticationMethod`, `token` on a service that supports both EntraID and token authentication. You can hide the `token` property if they decide to use `EntraId` as their authentication method for the service
- A script resource with different options depending on the operating system or CLI tool chosen
## **Describe the solution you'd like**
I have made some example changes that can be done to the current `TypeDefinitionBuilder` to fill-in for some of these missing features https://github.com/GABRIELNGBTUC/bicep/tree/feature/extend-type-generator
The 0.37 changes are a great improvement, however I cannot see myself develop a complete extension on this version unless most of the current limitations shown above are removed.
I believe the expectation of the other extension authors is to have a level of control on the bicep type schema as close as what is currently possible by the Azure teams. But today we are very far from this.
Contributor guide
Research direction
Start with the built-in Bicep local extension TypeDefinitionBuilder and compare its current behavior with the example changes in the linked feature branch. Define the scope for nullable, dictionary, validation, and union or discriminated types, then verify that generated schemas provide the requested authoring diagnostics and constraints.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100