-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ArrayEncodedMap #2750
base: main
Are you sure you want to change the base?
Add ArrayEncodedMap #2750
Conversation
It's a common pattern in Porter to represent a map of items, with a unique key, as a list in the porter.yaml or a file representation of the resource because the UX for autocomplete is better. We can't change that now, parameters and credentials for example will always be a list, but we can use a map in Porter's code so that it's easier to work with. I've added ArrayEncodedMap, a data structure that reads in a list and stores it in-memory as a map: - name: a value: stuff - name: b value: things We can use this as a base data structure initially for Parameter and Credential Sets, storing the mapping of the parameter/credential name to the secret resolution strategy. Over time, it may make sense to use it for the other data structures in porter.yaml as well. It has support for iterating as a map, or as a sorted list of values. The sorted list is good for unit tests, and anytime we need the output to be stable. The map isn't exposed directly and I have a funky "best you can do with Go" iterator to help keep our for loops looking pretty normal. Signed-off-by: Carolyn Van Slyck <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall. I left a few questions
// Use ItemsUnsafe() to directly manipulate the backing items map. | ||
func (m *ArrayEncodedMap[T, K]) Items() map[string]T { | ||
if m == nil { | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return an empty map
here, so callers don't blow up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this even reachable? If the object is nil then referencing any method on the object should result in a panic before it hits the implementation correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never mind just clarified my own question. Go can call methods on nil structs :)
return nil | ||
} | ||
|
||
if m.items == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it ever possible to have items
be nil
?
// UnmarshalRaw is the common Marshal implementation between YAML and JSON. | ||
func (m *ArrayEncodedMap[T, K]) UnmarshalRaw(raw []K) error { | ||
if m == nil { | ||
*m = ArrayEncodedMap[T, K]{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
won't this panic? seems like you'd need to set the value of the pointer to valid memory
*m = ArrayEncodedMap[T, K]{} | |
m = &ArrayEncodedMap[T, K]{} |
|
||
// UnmarshalYAML unmarshals the items in the specified YAML. | ||
func (m *ArrayEncodedMap[T, K]) UnmarshalYAML(value *yaml.Node) error { | ||
var raw []K |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here RE checking m == nil
What does this change
It's a common pattern in Porter to represent a map of items, with a unique key, as a list in the porter.yaml or a file representation of the resource because the UX for autocomplete is better. We can't change that now, parameters and credentials for example will always be a list, but we can use a map in Porter's code so that it's easier to work with.
I've added ArrayEncodedMap, a data structure that reads in a list and stores it in-memory as a map:
value: stuff
value: things
We can use this as a base data structure initially for Parameter and Credential Sets, storing the mapping of the parameter/credential name to the secret resolution strategy. Over time, it may make sense to use it for the other data structures in porter.yaml as well.
It has support for iterating as a map, or as a sorted list of values. The sorted list is good for unit tests, and anytime we need the output to be stable. The map isn't exposed directly and I have a funky "best you can do with Go" iterator to help keep our for loops looking pretty normal.
What issue does it fix
Refactoring work for PEP003 as I found working with parameter sets as maps made parameter resolution much more straightforward.
Notes for the reviewer
This is a replacement for my original PR #2734, which was adding helper methods to work with the set of values by key, but was still storing the values in a slice.
I will follow-up with another PR after this is merged to use it in ParameterSet/CredentialSet. You can see what that looks like in #2749. I wanted to give us a chance to bikeshed this first so that it's not super hard to rework with suggestions. Once we hook it up to ps/cs, the changeset gets pretty large.
Alternative names to consider... 😁
Checklist
Reviewer Checklist