Skip to content

Commit

Permalink
Rewrite versions related functions (GoogleCloudPlatform#10181)
Browse files Browse the repository at this point in the history
  • Loading branch information
zli82016 authored and balanaguharsha committed May 2, 2024
1 parent 2d34727 commit 5b03481
Show file tree
Hide file tree
Showing 7 changed files with 632 additions and 81 deletions.
136 changes: 70 additions & 66 deletions mmv1/api/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
package api

import (
"log"
"strings"

"github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product"
"github.com/GoogleCloudPlatform/magic-modules/mmv1/google"
"golang.org/x/exp/slices"
Expand Down Expand Up @@ -50,7 +53,7 @@ type Product struct {

// The API versions of this product

Versions []product.Version
Versions []*product.Version

// The base URL for the service API endpoint
// For example: `https://www.googleapis.com/compute/v1/`
Expand All @@ -77,6 +80,9 @@ func (p *Product) Validate() {
for _, o := range p.Objects {
o.ProductMetadata = p
}

p.SetApiName()
p.SetDisplayName()
}

// def validate
Expand All @@ -101,73 +107,82 @@ func (p *Product) Validate() {
// check :versions, type: Array, item_type: Api::Product::Version, required: true
// end

// // ====================
// // Custom Getters
// // ====================
// ====================
// Custom Setters
// ====================

// // The name of the product's API; "compute", "accesscontextmanager"
// def api_name
// name.downcase
// end
func (p *Product) SetApiName() {
// The name of the product's API; "compute", "accesscontextmanager"
p.ApiName = strings.ToLower(p.Name)
}

// The product full name is the "display name" in string form intended for
// users to read in documentation; "Google Compute Engine", "Cloud Bigtable"
func (p Product) GetDisplayName() string {
func (p *Product) SetDisplayName() {
if p.DisplayName == "" {
return google.SpaceSeparated(p.Name)
p.DisplayName = google.SpaceSeparated(p.Name)
}

return p.DisplayName
}

// // Most general version that exists for the product
// // If GA is present, use that, else beta, else alpha
// def lowest_version
// Version::ORDER.each do |ordered_version_name|
// @versions.each do |product_version|
// return product_version if ordered_version_name == product_version.name
// end
// end
// raise "Unable to find lowest version for product //{display_name}"
// end

// def version_obj(name)
// @versions.each do |v|
// return v if v.name == name
// end
// ====================
// Version-related methods
// ====================

// Most general version that exists for the product
// If GA is present, use that, else beta, else alpha
func (p Product) lowestVersion() *product.Version {
for _, orderedVersionName := range product.ORDER {
for _, productVersion := range p.Versions {
if orderedVersionName == productVersion.Name {
return productVersion
}
}
}

// raise "API version '//{name}' does not exist for product '//{@name}'"
// end
log.Fatalf("Unable to find lowest version for product %s", p.DisplayName)
return nil
}

// // Get the version of the object specified by the version given if present
// // Or else fall back to the closest version in the chain defined by Version::ORDER
// def version_obj_or_closest(name)
// return version_obj(name) if exists_at_version(name)
func (p Product) versionObj(name string) *product.Version {
for _, v := range p.Versions {
if v.Name == name {
return v
}
}

// // versions should fall back to the closest version to them that exists
// name ||= Version::ORDER[0]
// lower_versions = Version::ORDER[0..Version::ORDER.index(name)]
log.Fatalf("API version '%s' does not exist for product '%s'", name, p.Name)
return nil
}

// lower_versions.reverse_each do |version|
// return version_obj(version) if exists_at_version(version)
// end
// Get the version of the object specified by the version given if present
// Or else fall back to the closest version in the chain defined by product.ORDER
func (p Product) VersionObjOrClosest(name string) *product.Version {
if p.ExistsAtVersion(name) {
return p.versionObj(name)
}

// raise "Could not find object for version //{name} and product //{display_name}"
// end
// versions should fall back to the closest version to them that exists
if name == "" {
name = product.ORDER[0]
}

// def exists_at_version_or_lower(name)
// // Versions aren't normally going to be empty since products need a
// // base_url. This nil check exists for atypical products, like _bundle.
// return true if @versions.nil?
lowerVersions := make([]string, 0)
for _, v := range product.ORDER {
lowerVersions = append(lowerVersions, v)
if v == name {
break
}
}

// name ||= Version::ORDER[0]
// return false unless Version::ORDER.include?(name)
for i := len(lowerVersions) - 1; i >= 0; i-- {
if p.ExistsAtVersion(lowerVersions[i]) {
return p.versionObj(lowerVersions[i])
}
}

// (0..Version::ORDER.index(name)).each do |i|
// return true if exists_at_version(Version::ORDER[i])
// end
// false
// end
log.Fatalf("Could not find object for version %s and product %s", name, p.DisplayName)
return nil
}

func (p *Product) ExistsAtVersionOrLower(name string) bool {
if !slices.Contains(product.ORDER, name) {
Expand All @@ -192,20 +207,9 @@ func (p *Product) ExistsAtVersion(name string) bool {
return false
}

// def exists_at_version(name)
// // Versions aren't normally going to be empty since products need a
// // base_url. This nil check exists for atypical products, like _bundle.
// return true if @versions.nil?

// @versions.any? { |v| v.name == name }
// end

// // Not a conventional setter, so ignore rubocop's warning
// // rubocop:disable Naming/AccessorMethodName
// def set_properties_based_on_version(version)
// @base_url = version.base_url
// end
// // rubocop:enable Naming/AccessorMethodName
func (p *Product) SetPropertiesBasedOnVersion(version *product.Version) {
p.BaseUrl = version.BaseUrl
}

// // ====================
// // Debugging Methods
Expand Down
8 changes: 8 additions & 0 deletions mmv1/api/product/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

package product

import (
"golang.org/x/exp/slices"
)

// require 'api/object'

var ORDER = []string{"ga", "beta", "alpha", "private"}
Expand Down Expand Up @@ -50,3 +54,7 @@ type Version struct {
// def <=>(other)
// ORDER.index(name) <=> ORDER.index(other.name) if other.is_a?(Version)
// end

func (v *Version) CompareTo(other *Version) int {
return slices.Index(ORDER, v.Name) - slices.Index(ORDER, other.Name)
}
129 changes: 129 additions & 0 deletions mmv1/api/product_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package api

import (
"reflect"
"testing"

"github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product"
)

func TestProductLowestVersion(t *testing.T) {
t.Parallel()

cases := []struct {
description string
obj Product
expected string
}{
{
description: "lowest version is ga",
obj: Product{
Versions: []*product.Version{
&product.Version{
Name: "beta",
BaseUrl: "beta_url",
},
&product.Version{
Name: "ga",
BaseUrl: "ga_url",
},
&product.Version{
Name: "alpha",
BaseUrl: "alpha_url",
},
},
},
expected: "ga",
},
{
description: "lowest version is ga",
obj: Product{
Versions: []*product.Version{
&product.Version{
Name: "beta",
BaseUrl: "beta_url",
},
&product.Version{
Name: "alpha",
BaseUrl: "alpha_url",
},
},
},
expected: "beta",
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.description, func(t *testing.T) {
t.Parallel()

versionObj := tc.obj.lowestVersion()

if got, want := versionObj.Name, tc.expected; !reflect.DeepEqual(got, want) {
t.Errorf("expected %v to be %v", got, want)
}
})
}
}

func TestProductVersionObjOrClosest(t *testing.T) {
t.Parallel()

cases := []struct {
description string
obj Product
input string
expected string
}{
{
description: "closest version object to ga",
obj: Product{
Versions: []*product.Version{
&product.Version{
Name: "beta",
BaseUrl: "beta_url",
},
&product.Version{
Name: "ga",
BaseUrl: "ga_url",
},
},
},
input: "ga",
expected: "ga",
},
{
description: "closest version object to alpha",
obj: Product{
Versions: []*product.Version{
&product.Version{
Name: "beta",
BaseUrl: "beta_url",
},
&product.Version{
Name: "ga",
BaseUrl: "ga_url",
},
},
},
input: "alpha",
expected: "beta",
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.description, func(t *testing.T) {
t.Parallel()

versionObj := tc.obj.VersionObjOrClosest(tc.input)

if got, want := versionObj.Name, tc.expected; !reflect.DeepEqual(got, want) {
t.Errorf("expected %v to be %v", got, want)
}
})
}
}
38 changes: 38 additions & 0 deletions mmv1/api/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package api

import (
"github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product"
"github.com/GoogleCloudPlatform/magic-modules/mmv1/api/resource"
"github.com/GoogleCloudPlatform/magic-modules/mmv1/provider/terraform"
)
Expand Down Expand Up @@ -292,3 +293,40 @@ func (r *Resource) setResourceMetada(properties []*Type) {
property.ResourceMetadata = r
}
}

// ====================
// Version-related methods
// ====================

func (r Resource) MinVersionObj() *product.Version {
if r.MinVersion != "" {
return r.ProductMetadata.versionObj(r.MinVersion)
} else {
return r.ProductMetadata.lowestVersion()
}
}

func (r Resource) NotInVersion(version *product.Version) bool {
return version.CompareTo(r.MinVersionObj()) < 0
}

// Recurses through all nested properties and parameters and changes their
// 'exclude' instance variable if the property is at a version below the
// one that is passed in.
func (r *Resource) ExcludeIfNotInVersion(version *product.Version) {
if !r.Exclude {
r.Exclude = r.NotInVersion(version)
}

if r.Properties != nil {
for _, p := range r.Properties {
p.ExcludeIfNotInVersion(version)
}
}

if r.Parameters != nil {
for _, p := range r.Parameters {
p.ExcludeIfNotInVersion(version)
}
}
}
Loading

0 comments on commit 5b03481

Please sign in to comment.