-
Notifications
You must be signed in to change notification settings - Fork 62
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 IP and Country Named Locations #24
Conversation
@alexwilcox9 Thanks for this PR, this is off to a great start! For readonly fields, I believe if you use It's a great question whether or not we should implement multiple clients here. Given the API exposes these subtypes via a single endpoint, and supports other methods via that endpoint, I'm leaning towards the latter and having a single This client could have methods like OData metadata is populated when parsing API responses but there's currently no support for automagically handling these fields in requests, so for the The models could use struct embedding to implement a base type NamedLocation struct {
ID *string `json:"id,omitempty"`
CreatedDateTime *time.Time `json:"createdDateTime,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
ModifiedDateTime *time.Time `json:"modifiedDateTime,omitempty"`
}
type CountryNamedLocation struct {
*NamedLocation
CountriesAndRegions *[]string `json:"countriesAndRegions,omitempty"`
IncludeUnknownCountriesAndRegions *bool `json:"includeUnknownCountriesAndRegions,omitempty"`
} In terms of getting this support into the Terraform AzureAD provider, at present it would first need to be added to this SDK, so thanks for submitting this here! FYI there's another PR #23 in progress for conditional access policies which looks fairly solid, to avoid any duplicate efforts there :) |
HI @manicminer thanks for the review! I haven't encountered embedding before so this is a useful tip. If I create a list function that returns a slice of Cheers for the heads up on that other PR, luckily it looks like we've managed to avoid overlap so far! |
The common list function could return an interface which would allow the user to use type assertion it to discover the actual type, e.g. // models
type NamedLocation interface {}
type BaseNamedLocation struct {
// common fields
}
type CountryNamedLocation struct {
*BaseNamedLocation
// country-specific fields
}
type IPNamedLocation struct {
*BaseNamedLocation
// ip-specific fields
}
// clients
type NamedLocationClient struct {}
func (c NamedLocationClient) List() (*[]NamedLocation, error) {
r := CountryNamedLocation{
BaseNamedLocation: &BaseNamedLocation{},
}
return &[]NamedLocation{r}, nil
}
func main() {
locs, _ := NamedLocationClient{}.List()
if locs != nil {
for _, l := range *locs {
fmt.Printf("%+v ", l)
if _, ok := l.(CountryNamedLocation); ok {
fmt.Println("is a CountryNamedLocation")
} else if _, ok := l.(IPNamedLocation); ok {
fmt.Println("is an IPNamedLocation")
}
}
}
} (I renamed In this example the locations in the result are being tested to see which type they are. These are just suggestions, if it feels too clunky when you are implementing it, feel free to try something different! :) |
Thanks again for your tips, I'm definitely getting closer So I've tried changing the list function to return an interface but I'm not having much luck with the type assertion In the list function I am unmarshalling the response into a slice of the interface
And in the test I try the type assertion but it never seems to match
Here's the output from the test
Any input on this would be greatly appreciated, scratching my head on this one! |
@alexwilcox9 In the client List() function, you are unmarshaling the response into a slice of interface{} and the model structs aren't used. I'm assuming the response json has @odata.type set for each element of the array to indicate which type of object it is? If so, you could use the odata struct to pull out any useful odata, then determine the type for each one and attempt to unmarshal it using the appropriate model struct, before appending to a slice of NamedLocation and returning that. Something like (no error checking, totally untested, may not even compile, pretty much pseudocode): var result struct {
NamedLocations *[]json.RawMessage `json:"value"`
}
json.Unmarshal(respBody, &data)
if result.NamedLocations == nil {
return
}
ret := make([]NamedLocation, 0, len(result.NamedLocations))
for _, namedLocation := range *result.NamedLocations {
var o odata.OData
json.Unmarshal(namedLocation, &o)
switch o.Type {
case "#microsoft.graph.countryNamedLocation":
var loc CountryNamedLocation
json.Unmarshal(namedLocation, &loc)
ret = append(ret, loc)
case "#microsoft.graph.ipNamedLocation":
var loc IPNamedLocation
json.Unmarshal(namedLocation, &loc)
ret = append(ret, loc)
}
}
return ret This essentially starts off by unmarshaling the containing array into a slice of RawMessage (which is just a byteslice alias), so you can loop through the items and unmarshal them individually. Then for each one it unmarshals using the OData struct (in this project) to inspect the I think this will work, but shout out if it doesn't make sense or I've overlooked something :) |
@manicminer I think I've got it! Thanks for the example code, I messed around with the pointers until it stopped erroring and it seems to work 😂
Not sure why the linting is failing as that passes on my machine... |
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.
Hi @alexwilcox9, thanks for the latest changes, this is looking great!
Can you rename NamedLocationClient
to its plural form NamedLocationsClient
? I also have a few comments on the client and its tests. If we can get these looked at then I think this will be ready to merge :)
Co-authored-by: Tom Bamford <[email protected]>
Add nil check Co-authored-by: Tom Bamford <[email protected]>
Co-authored-by: Tom Bamford <[email protected]>
Co-authored-by: Tom Bamford <[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.
@alexwilcox9 Thanks for the changes! I resolved the merge conflict and pluralized the client name - this LGTM, let's get it merged :D
@manicminer Thanks for that! I got so focused on all the other points I totally missed the first one on pluralising! |
No worries, thanks for all your work! :) |
Hi @manicminer
First attempt at contributing to any GoLang project so if there are obvious seeming mistakes I apologise in advance!
In this PR I've added named location (both country and IP based) and I'm planning to continue on to add full conditional policies if all goes well.
I have a couple of questions that I'm hoping you can help with:
Thanks!