bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Proposal - simplifying resource referencing (part 1)

Open anthony-c-martin opened this issue 4 years ago • 19 comments

Proposal - simplifying resource referencing (part 1)

Part 1 / Part 2

Problem statement

Passing around / obtaining references to resources in a type-safe manner is overly complex. Rather than inventing non-type-safe mechanisms to refer to resources or resource properties, we should provide a first-class syntax for doing so, with full type-safety and editor support.

New 'resource' function

The az namespace will expose an additional function named resource, which can be used to obtain a reference to a resource within the current scope.

The resource() function takes the first parameter a string (following the resource type format), and a variable number of arguments based on the number of qualified types in the type string. The semantics and validation for the type string parameter behave exactly as those for the type string in resource declarations.

Objects of type scope will also expose a resource() function, which can be used similarly to obtain a reference to a resource at a different scope.

Examples

Global function

var server = resource('Microsoft.Sql/servers@2020-02-02-preview', serverName)
// 'server' can now be used to access resource properties.
var serverProp = server.properties.someProp

Scope function

var otherRg = resourceGroup('otherRgName')
var otherRgServer = otherRg.resource('Microsoft.Sql/servers@2020-02-02-preview', serverName)
// 'otherRgServer' is a reference to a resource in another scope.
var otherRgServerProp = otherRgServer.properties.someProp

Use to set parent property

// here we can inline the function to provide a reference to the parent resource
resource mySubnet 'Microsoft.Network/virtualNetworks/subnets@2020-08-01' = {
  parent: resource('Microsoft.Network/virtualNetworks@2020-08-01', vnetName)
  name: 'mySubnet'
  ...
}

Use to set scope property

// here we can inline the function to set a scope for the extension resource
resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2020-01-01' = {
  scope: resource('Microsoft.Compute/virtualMachines@2020-01-01', vmName)
  name: 'diags'
  ...
}

Notes

  1. This provides the same functionality as the existing keyword, but as a more convenient one-liner.

The 'child' function on a resource

All resources will expose an additional function named child, which can be used to obtain a reference to a child resource of a given type.

The child() function takes a string for the first parameter (following the nested resource type format), and a second argument for the child resource name.

Examples

Obtaining child resource references

resource myVnet 'Microsoft.Network/virtualNetworks@2020-06-01' = {
  name: 'myVnet'
  ... 
}

// inheriting the same API version
var subnetRef1 = myVnet.child('subnets', subnetName)

// obtaining a reference with a different API version
var subnetRef2 = myVnet.child('subnets@2020-08-01', subnetName)

EDIT 5/28/21: Added info on child() function, removed proposal for extension resources

anthony-c-martin avatar Apr 13 '21 17:04 anthony-c-martin

Exposing it as a method has a really nice effect of reducing the number of parameters that you have to pass to the resource() function and improves composability.

Even though the name resource() is very generic, I would prefer for us to have one function instead of parent() and child(). The experience will be more seamless because the user doesn't have to think about choosing the right function - it's always resource().

majastrz avatar Apr 13 '21 20:04 majastrz

I lean towards parent and child. with resource it's not possible to get parent reference.

as for extension resources in option 1, they start with provider namespaces like Microsoft. can we use that as something which will separate extension from child?

miqm avatar Apr 13 '21 21:04 miqm

@miqm that's a good point. I'm inclined to using different functions as it's unambiguous. Also I'd prefer the function names to be more explicit:

// child resource
var myChild = storageAcc.childResource('blobServices', 'default')
// extension resource
var myExt = storageAcc.extensionResource('Microsoft.Insights/diagnosticSettings@2020-01-01', 'diags')
// parent resource
var storageAcc = blobServices.parentResource()

shenglol avatar Apr 14 '21 02:04 shenglol

Oh my bad. I read things too quickly and mixed up parent() and child() with the child() and extension() discussion we had previously ☹.

We have two types of "hierarchies" here (resource nesting and resource extending) and two directions of travel (up and down). In the worst case, we end up with 4 functions.

For resource nesting, parent() and child() absolutely make sense. The language is clear, unambiguous, and symmetric. One of these also matches the parent property name, which is great because they are related semantically.

Where it gets confusing for me is what do we call traversing the extension "hierarchy". What is the name of a resource being extended relative to an extension? extendee()? Definitely not fond of that ☹. The other direction seems fine with extension(). What also doesn't make naming easy here that the property that specifies the extendee resource on an extension resource is called scope.

The other wrinkle is that resource extending also typically does not use parent/child terminology in any of our docs and other content. Could we fix that/teach users about it? I think the answer is yes here, but would it be going too far?

Going up or down the hierarchy, it's technically feasible to distinguish between nesting and extending by inspecting the resource types (aka counting the / chars, etc.) because they are never expressions and are always available.

But does it make sense to combine nesting and extending into a pair of functions if we've already split parent and scope? Or do we stick with it and introduce separate pairs of functions for traversal of each type of hierarchy? I have a really strong preference for consistency in that regard. (Either have 2 pairs of functions or combine scope and parent.)

So I think the big choice we need to make is between these options (ignore names for now):

  1. parent()/child() for everything
  2. parent()/child()/extension()/extendee()

After that, things should fall into place IMO.

With choice 1 above, there's also a tiny risk that we may have missed some rare case by combining the two hierarchy traversals. I can't think of anything, but we would be abstracting concepts that are technically separate in the runtime.

@shenglol's point about more explicit function names (*resource()) applies to all the choices and is something we should consider.

majastrz avatar Apr 14 '21 03:04 majastrz

idea: what about hving a method for traversing downwards (child, extension) and property (parent, scope) for going upwards? scope in case of classic resources would return resource group. scope of resource group returns subscription.

miqm avatar Apr 14 '21 04:04 miqm

It's also worth thinking about what this means for the nested resource scenario. I know that @marcre was keen for us to try to unify deploying children & deploying extensions - e.g.:

Child:

resource vnet 'Microsoft.Network/virtualNetworks@2020-08-01' = {
  name: vnetName

  resource subnet 'subnets' = {
    name: subnetName

  }
}

Extension:

resource vm 'Microsoft.Compute/virtualMachines@2020-12-01' = {
  name: vmName

  resource diags 'microsoft.insights/diagnosticSettings@2015-07-01' = {
    name: diagsName

  }
}

If we were to enable this scenario, then conceptually (in Bicep at least), it might also feel confusing to people that with nested resources, there's a single hierarchy, but outside of nested resources, there are 2 hierarchies. e.g. using the above two examples:

// these two references are equivalent
var subnet = vnet.child('subnets', subnetName)
var subnet2 = vnet::subnet

// these two references are equivalent, but why do I have to remember to use a different function?
var diags = vm.extension('microsoft.insights/diagnosticSettings@2015-07-01', diagsName)
var diags2 = vm::diags

To me, I feel like this leads to two options:

  1. Support extensions in nested resources, unify parent/child & scope/extension into a single hierarchy/set of functions.
  2. Do not support extensions in nested resources, have two separate hierarchies & sets of functions for traversing.

I'm curious to hear whether this changes anything, and if so, which option (or other suggestion!) would be preferred.

anthony-c-martin avatar Apr 14 '21 15:04 anthony-c-martin

I prefer option 2. Although option 1 feels simpler, in fact it's obscuring the underlying concepts (e.g. the differences between child and extension resources) to a degree that I think it could be more confusing than helpful overall.

johndowns avatar Apr 15 '21 20:04 johndowns

I've revised this spec to remove references to extension resources. There's some benefit to providing similar functionality for them, but I feel like child references are going to be more useful - and it feels we can safely punt this decision.

anthony-c-martin avatar May 29 '21 01:05 anthony-c-martin

I did a bit of digging; some implementation notes:

  1. Bicep's resource analysis and codegen heavily utilizes the fact that every resource is represented by a ResourceSymbol. We'll need to introduce indirection to change this in order to handle references to resources obtained through functions (resource() & child()), as well as other means (param -> #2246 & var -> #3594).
  2. I suggest an initial refactor to add a ResourceMetadata proxy class around ResourceSymbol, and replace relevant usages of ResourceSymbol.
  3. Introduce a means of having ResourceMetadata be generated by a non-ResourceSymbol while still able to provide all the necessary information for codegen and validation (e.g. type info, name fields, IsExistingResource(), ancestry etc).
  4. At this point it should be safe to add resource() / child() functions and have these references behave the same way as references obtained with the resource keyword. We will probably want to generate ResourceMetadata info lazily, but should be careful to collect all instances prior to codegen for IDE validation.

anthony-c-martin avatar Jul 26 '21 14:07 anthony-c-martin

Makes sense to me. We need to make sure that it's thread safe (the registry work will add new concurrency fun).

If the resource metadata can be efficiently calculated on-demand for any arbitrary place where a resource reference can arise and if that calculation understands its dependencies and can request them on-demand as well, then you don't really need the ability to collect them all.

majastrz avatar Jul 26 '21 21:07 majastrz

Adding a link to this discussion that covers a feature that is available in ARM templates, however is not available in Bicep.

https://github.com/Azure/bicep/discussions/5805 https://github.com/Azure/bicep/discussions/5805#discussioncomment-2077671

Discussion covers the ability to create a Scope from strings.

We currently have open discussions on passing Resources, however I wonder if it would be possible to pass a Scope or have a scope function to have parity with ARM templates?

brwilkinson avatar Jan 30 '22 20:01 brwilkinson

Posting on this issue as mine #9468 has since been closed. There are some limitations to having different resources using different scopes in the same module that might limit this, but I'd like to see a mechanism where, even if it's just more syntactical sugar on the dev's part, I can use overloads on a resource type in Bicep and assign to a variable for use elsewhere in the same module.

For example:

Global

var server = resource('Microsoft.Sql/servers@2020-02-02-preview', serverName)

Scoped

var server = resource(subscriptionId, resourceGroupName, 'Microsoft.Sql/servers@2020-02-02-preview', serverName) //Note the optional override passing the subscription ID and resource group name into the resource()

Personally, this comes up a lot when I need to do some sort of DNS validation and need to modify a DNS zone or need to pull a secret from a Key Vault in another subscription/resource group and being able to specify that other scope in one place without modularizing every single child operation that has different scopes would simplify the deployments at dev time.

I'm pretty happy with the child syntax as it'd eliminate the need to do multiple for loops across sets of like-resources.

WhitWaldo avatar Jan 12 '23 20:01 WhitWaldo

Posting here instead of a new issue to cite an inconsistency that I hope this issue would fix. Especially when looping through objects/arrays in order to derive values for resource references, I frequently opt first to do something like the following just as a matter of writing the idea down linearly:

@description('Used to identify the details of a specific certificate')
type certificateMapping = {
  @description('The subject value of the certificate')
  subject: string
  @description('The name of the secret in the Key Vault')
  secretName: string
  @description('The thumbprint of the certificate')
  thumbprint: string
  @description('The identifier of the Key Vault instance')
  keyVault: keyVaultIdentifier
}

@description('Used to identify a Key Vault and where it\'s deployed to')
type keyVaultIdentifier = {
  @description('The name of the Key Vault')
  name: string
  @description('The ID of the subscription the Key Vault is associated with')
  subscriptionId: string
  @description('The name of the resource group the Key Vault is associated with')
  resourceGroupName: string
}

param CertificateSubjects certificateMapping[]

resource KeyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = [for (c, i) in CertificateSubjects: {
  name: c.keyVault.name
  scope: resourceGroup(c.keyVault.subscriptionId, c.keyVault.resourceGroupName)
}

resource Secret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' existing = [for (c, i) in CertificateSubjects: {
  name: c.secretName
  parent: KeyVault[i]
}

This will throw an exception in the compiler:

Unhandled exception. System.NotImplementedException: Mismatch between count of index expressions and inaccessible symbols during array access index replacement. at Bicep.Core.Emit.ExpressionConverter.CreateConverterForIndexReplacement(SyntaxBase nameSyntax, SyntaxBase indexExpression, SyntaxBase newContext) in C:__w\1\s\bicep\src\Bicep.Core\Emit\ExpressionConverter.cs:line 281 at Bicep.Core.Emit.ExpressionEmitter.EmitExpression(SyntaxBase resourceNameSyntax, SyntaxBase indexExpression, SyntaxBase newContext) in C:__w\1\s\bicep\src\Bicep.Core\Emit\ExpressionEmitter.cs:line 100 at Bicep.Core.Emit.ScopeHelper.<>c__DisplayClass7_0.<EmitResourceOrModuleScopeProperties>b__0() in C:__w\1\s\bicep\src\Bicep.Core\Emit\ScopeHelper.cs:line 367 at Bicep.Core.Emit.PositionTrackingJsonTextWriter.WritePropertyWithPosition(SyntaxBase keyPosition, String name, Action valueFunc) in C:__w\1\s\bicep\src\Bicep.Core\Emit\PositionTrackingJsonTextWriter.cs:line 110 at Bicep.Core.Emit.ExpressionEmitter.EmitPropertyInternal(LanguageExpression expressionKey, Action valueFunc, SyntaxBase location, Boolean skipCopyCheck) in C:__w\1\s\bicep\src\Bicep.Core\Emit\ExpressionEmitter.cs:line 467 at Bicep.Core.Emit.ExpressionEmitter.EmitProperty(String name, Action valueFunc) in C:__w\1\s\bicep\src\Bicep.Core\Emit\ExpressionEmitter.cs:line 445 at Bicep.Core.Emit.ScopeHelper.EmitResourceOrModuleScopeProperties(SemanticModel semanticModel, ScopeData scopeData, ExpressionEmitter expressionEmitter, SyntaxBase newContext) in C:__w\1\s\bicep\src\Bicep.Core\Emit\ScopeHelper.cs:line 367 at Bicep.Core.Emit.ScopeHelper.EmitResourceScopeProperties(SemanticModel semanticModel, ScopeData scopeData, ExpressionEmitter expressionEmitter, SyntaxBase newContext) in C:__w\1\s\bicep\src\Bicep.Core\Emit\ScopeHelper.cs:line 327 at Bicep.Core.Emit.TemplateWriter.<>c__DisplayClass43_0.<EmitResource>b__0() in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 748 at Bicep.Core.Emit.PositionTrackingJsonTextWriter.WriteObjectWithPosition(SyntaxBase sourcePosition, Action propertiesFunc) in C:__w\1\s\bicep\src\Bicep.Core\Emit\PositionTrackingJsonTextWriter.cs:line 99 at Bicep.Core.Emit.TemplateWriter.EmitResource(PositionTrackingJsonTextWriter jsonWriter, DeclaredResourceMetadata resource, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 639 at Bicep.Core.Emit.TemplateWriter.EmitResources(PositionTrackingJsonTextWriter jsonWriter, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 597 at Bicep.Core.Emit.TemplateWriter.GenerateTemplateWithoutHash(PositionTrackingJsonTextWriter jsonWriter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 140 at Bicep.Core.Emit.TemplateWriter.Write(SourceAwareJsonTextWriter writer) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 104 at Bicep.Core.Emit.TemplateWriter.<>c__DisplayClass45_0.<EmitModule>b__0() in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 916 at Bicep.Core.Emit.PositionTrackingJsonTextWriter.WriteObjectWithPosition(SyntaxBase sourcePosition, Action propertiesFunc) in C:__w\1\s\bicep\src\Bicep.Core\Emit\PositionTrackingJsonTextWriter.cs:line 99 at Bicep.Core.Emit.TemplateWriter.EmitModule(PositionTrackingJsonTextWriter jsonWriter, ModuleSymbol moduleSymbol, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 841 at Bicep.Core.Emit.TemplateWriter.EmitResources(PositionTrackingJsonTextWriter jsonWriter, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 607 at Bicep.Core.Emit.TemplateWriter.GenerateTemplateWithoutHash(PositionTrackingJsonTextWriter jsonWriter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 140 at Bicep.Core.Emit.TemplateWriter.Write(SourceAwareJsonTextWriter writer) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 104 at Bicep.Core.Emit.TemplateWriter.<>c__DisplayClass45_0.<EmitModule>b__0() in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 916 at Bicep.Core.Emit.PositionTrackingJsonTextWriter.WriteObjectWithPosition(SyntaxBase sourcePosition, Action propertiesFunc) in C:__w\1\s\bicep\src\Bicep.Core\Emit\PositionTrackingJsonTextWriter.cs:line 99 at Bicep.Core.Emit.TemplateWriter.EmitModule(PositionTrackingJsonTextWriter jsonWriter, ModuleSymbol moduleSymbol, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 841 at Bicep.Core.Emit.TemplateWriter.EmitResources(PositionTrackingJsonTextWriter jsonWriter, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 607 at Bicep.Core.Emit.TemplateWriter.GenerateTemplateWithoutHash(PositionTrackingJsonTextWriter jsonWriter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 140 at Bicep.Core.Emit.TemplateWriter.Write(SourceAwareJsonTextWriter writer) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 104 at Bicep.Core.Emit.TemplateWriter.<>c__DisplayClass45_0.<EmitModule>b__0() in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 916 at Bicep.Core.Emit.PositionTrackingJsonTextWriter.WriteObjectWithPosition(SyntaxBase sourcePosition, Action propertiesFunc) in C:__w\1\s\bicep\src\Bicep.Core\Emit\PositionTrackingJsonTextWriter.cs:line 99 at Bicep.Core.Emit.TemplateWriter.EmitModule(PositionTrackingJsonTextWriter jsonWriter, ModuleSymbol moduleSymbol, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 841 at Bicep.Core.Emit.TemplateWriter.EmitResources(PositionTrackingJsonTextWriter jsonWriter, ExpressionEmitter emitter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 607 at Bicep.Core.Emit.TemplateWriter.GenerateTemplateWithoutHash(PositionTrackingJsonTextWriter jsonWriter) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 140 at Bicep.Core.Emit.TemplateWriter.Write(SourceAwareJsonTextWriter writer) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateWriter.cs:line 104 at Bicep.Core.Emit.TemplateEmitter.<>c__DisplayClass8_0.<Emit>b__0() in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateEmitter.cs:line 68 at Bicep.Core.Emit.TemplateEmitter.EmitOrFail(Func`1 write) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateEmitter.cs:line 122 at Bicep.Core.Emit.TemplateEmitter.Emit(Stream stream) in C:__w\1\s\bicep\src\Bicep.Core\Emit\TemplateEmitter.cs:line 59 at Bicep.Cli.Services.CompilationWriter.ToStream(Compilation compilation, Stream stream) in C:__w\1\s\bicep\src\Bicep.Cli\Services\CompilationWriter.cs:line 38 at Bicep.Cli.Services.CompilationWriter.ToFile(Compilation compilation, String outputPath) in C:__w\1\s\bicep\src\Bicep.Cli\Services\CompilationWriter.cs:line 28 at Bicep.Cli.Commands.BuildCommand.RunAsync(BuildArguments args) in C:__w\1\s\bicep\src\Bicep.Cli\Commands\BuildCommand.cs:line 71 at Bicep.Cli.Program.RunAsync(String[] args) in C:__w\1\s\bicep\src\Bicep.Cli\Program.cs:line 75 at Bicep.Cli.Program.Main(String[] args) in C:__w\1\s\bicep\src\Bicep.Cli\Program.cs:line 64 at Bicep.Cli.Program.<Main>(String[] args)

And frankly, that doesn't make a lot of sense that it's not supported since I'm just iterating through a loop to get the Key Vault reference for that element, then also its Secret reference, using the same index to then point to the Key Vault resource.

This works fine though:

@description('Used to identify the details of a specific certificate')
type certificateMapping = {
  @description('The subject value of the certificate')
  subject: string
  @description('The name of the secret in the Key Vault')
  secretName: string
  @description('The thumbprint of the certificate')
  thumbprint: string
  @description('The identifier of the Key Vault instance')
  keyVault: keyVaultIdentifier
}

@description('Used to identify a Key Vault and where it\'s deployed to')
type keyVaultIdentifier = {
  @description('The name of the Key Vault')
  name: string
  @description('The ID of the subscription the Key Vault is associated with')
  subscriptionId: string
  @description('The name of the resource group the Key Vault is associated with')
  resourceGroupName: string
}

param CertificateSubjects certificateMapping[]

//Maintains a reference to the certificate in the Key Vault for this subject
resource Secret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' existing = [for (c, i) in CertificateSubjects: {
  name: '${c.keyVault.name}/${c.secretName}'
  scope: resourceGroup(c.keyVault.subscriptionId, c.keyVault.resourceGroupName)
}]

Even though logically it's doing the same thing in my mind. I know there's an attempt to ensure there's only one way to do things in Bicep, but this would be another instance where I would argue consistency should trump One Best Approach, especially since the former arguably is more "Bicep-y" than the latter which requires non-trivial knowledge that you can reference children in that manner all in one resource and skip over the parent/child relationship.

Thanks for the continued consideration!

WhitWaldo avatar Jan 14 '23 20:01 WhitWaldo

The docs page points here for the experimental feature resourceTypedParamsAndOutputs

Was a little tricky to find the details here.

This change implements functionality for declaring strongly type parameters and outputs using resource types.

Example:

param storage resource 'Microsoft.Storage/storageAccounts@2020-01-01'

This declares a parameter that can be interacted with as-if it were an 'existing' resource type declaration of the provided type.

In addition you can do the same with outputs:

output out resource 'Microsoft.Storage/storageAccounts@2020-01-01' = foo

or using type inference (outputs):

output out resource = foo

These features together allow you to pass resources across module boundaries, and as command line parameters (using the resource ID).

dciborow avatar Apr 08 '23 05:04 dciborow

Hi anthony-c-martin, this issue has been marked as stale because it was labeled as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. Thanks for contributing to bicep! :smile: :mechanical_arm:

ghost avatar May 19 '23 04:05 ghost

This bot should close as not planned, not completed... Or just not close things

onionhammer avatar May 22 '23 16:05 onionhammer

This bot should close as not planned, not completed... Or just not close things

Agreed. https://github.com/Azure/bicep/issues/10874

StephenWeatherford avatar Jun 01 '23 17:06 StephenWeatherford

Is this still happening?

jachin84 avatar May 23 '24 22:05 jachin84

Don't have a clear ETA as there is a dependency on a lower-level improvement to how we process expressions, but this is very much still on our committed list!

alex-frankel avatar Jun 11 '24 22:06 alex-frankel

Don't have a clear ETA as there is a dependency on a lower-level improvement to how we process expressions, but this is very much still on our committed list!

Also intered on this, noticed this issue is very popular now. Specially regarding RoleAssignments as a module. Hope this get the proper attention a an ETA soon. Thanks for all support.

guimatheus92 avatar Nov 05 '24 00:11 guimatheus92

This unfortunately won't get done in the current semester, but it's working its way up to the top of the list. Added it to our next semester which runs from March '25 - September '25.

alex-frankel avatar Nov 08 '24 23:11 alex-frankel

+1 for this. Simplifies things and is more logical. And, although I could use this in various places, RoleAssignments is a big win! Do we nave a new ETA?

lee-40square avatar Feb 25 '25 03:02 lee-40square

I've been following this for quite a while and was wondering if it will get picked up this semester?

mrwalters1988 avatar Apr 28 '25 17:04 mrwalters1988

We are closing on our "Bicep 1.0" list, which we aim to complete by September. This issue is on the (very) short list of candidates for that, so I am optimistic this will be done by then, but we'll see. Appreciate the patience from everyone as always.

cc @SydneyhSmith

alex-frankel avatar Apr 28 '25 17:04 alex-frankel

After some discussion we plan to do part 2 (#2246) first and then consider if this work is still needed. Please keep adding specific scenarios here so we can continue to track importance 😄

SydneyhSmith avatar May 06 '25 19:05 SydneyhSmith

After some discussion we plan to do part 2 (#2246) first and then consider if this work is still needed. Please keep adding specific scenarios here so we can continue to track importance 😄

The plan is to do part 2 as part of v.1.0 until September? Thanks for the news.

guimatheus92 avatar May 06 '25 20:05 guimatheus92