Skip to content
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

refactor(pubsublite): internal utils for reservations #4363

Merged
merged 2 commits into from Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion pubsublite/integration_test.go
Expand Up @@ -130,7 +130,7 @@ func TestIntegration_ResourceAdminOperations(t *testing.T) {
region, _ := wire.ZoneToRegion(zone)
resourceID := resourceIDs.New()

locationPath := wire.LocationPath{Project: proj, Zone: zone}.String()
locationPath := wire.LocationPath{Project: proj, Location: zone}.String()
topicPath := wire.TopicPath{Project: proj, Zone: zone, TopicID: resourceID}.String()
subscriptionPath := wire.SubscriptionPath{Project: proj, Zone: zone, SubscriptionID: resourceID}.String()
t.Logf("Topic path: %s", topicPath)
Expand Down
48 changes: 41 additions & 7 deletions pubsublite/internal/wire/resources.go
Expand Up @@ -49,18 +49,18 @@ func ZoneToRegion(zone string) (string, error) {
return zone[0:strings.LastIndex(zone, "-")], nil
}

// LocationPath stores a path consisting of a project and zone.
// LocationPath stores a path consisting of a project and zone/region.
type LocationPath struct {
// A Google Cloud project. The project ID (e.g. "my-project") or the project
// number (e.g. "987654321") can be provided.
Project string

// A Google Cloud zone, for example "us-central1-a".
Zone string
// A Google Cloud zone or region, for example "us-central1-a", "us-central1".
Location string
}

func (l LocationPath) String() string {
return fmt.Sprintf("projects/%s/locations/%s", l.Project, l.Zone)
return fmt.Sprintf("projects/%s/locations/%s", l.Project, l.Location)
}

var locPathRE = regexp.MustCompile(`^projects/([^/]+)/locations/([^/]+)$`)
Expand All @@ -72,7 +72,7 @@ func ParseLocationPath(input string) (LocationPath, error) {
return LocationPath{}, fmt.Errorf("pubsublite: invalid location path %q. valid format is %q",
input, "projects/PROJECT_ID/locations/ZONE")
}
return LocationPath{Project: parts[1], Zone: parts[2]}, nil
return LocationPath{Project: parts[1], Location: parts[2]}, nil
}

// TopicPath stores the full path of a Pub/Sub Lite topic.
Expand All @@ -94,7 +94,7 @@ func (t TopicPath) String() string {

// Location returns the topic's location path.
func (t TopicPath) Location() LocationPath {
return LocationPath{Project: t.Project, Zone: t.Zone}
return LocationPath{Project: t.Project, Location: t.Zone}
}

var topicPathRE = regexp.MustCompile(`^projects/([^/]+)/locations/([^/]+)/topics/([^/]+)$`)
Expand Down Expand Up @@ -129,7 +129,7 @@ func (s SubscriptionPath) String() string {

// Location returns the subscription's location path.
func (s SubscriptionPath) Location() LocationPath {
return LocationPath{Project: s.Project, Zone: s.Zone}
return LocationPath{Project: s.Project, Location: s.Zone}
}

var subsPathRE = regexp.MustCompile(`^projects/([^/]+)/locations/([^/]+)/subscriptions/([^/]+)$`)
Expand All @@ -144,6 +144,40 @@ func ParseSubscriptionPath(input string) (SubscriptionPath, error) {
return SubscriptionPath{Project: parts[1], Zone: parts[2], SubscriptionID: parts[3]}, nil
}

// ReservationPath stores the full path of a Pub/Sub Lite reservation.
type ReservationPath struct {
// A Google Cloud project. The project ID (e.g. "my-project") or the project
// number (e.g. "987654321") can be provided.
Project string

// A Google Cloud region. An example region is "us-central1".
Region string

// The ID of the Pub/Sub Lite reservation, for example "my-reservation-name".
ReservationID string
}

func (r ReservationPath) String() string {
return fmt.Sprintf("projects/%s/locations/%s/reservations/%s", r.Project, r.Region, r.ReservationID)
}

// Location returns the reservation's location path.
func (r ReservationPath) Location() LocationPath {
return LocationPath{Project: r.Project, Location: r.Region}
}

var reservationPathRE = regexp.MustCompile(`^projects/([^/]+)/locations/([^/]+)/reservations/([^/]+)$`)

// ParseReservationPath parses the full path of a Pub/Sub Lite reservation.
func ParseReservationPath(input string) (ReservationPath, error) {
parts := reservationPathRE.FindStringSubmatch(input)
if len(parts) < 4 {
return ReservationPath{}, fmt.Errorf("pubsublite: invalid reservation path %q. valid format is %q",
input, "projects/PROJECT_ID/locations/REGION/reservations/RESERVATION_ID")
}
return ReservationPath{Project: parts[1], Region: parts[2], ReservationID: parts[3]}, nil
}

type topicPartition struct {
Path string
Partition int
Expand Down
59 changes: 58 additions & 1 deletion pubsublite/internal/wire/resources_test.go
Expand Up @@ -120,7 +120,7 @@ func TestParseLocationPath(t *testing.T) {
{
desc: "valid: location path",
input: "projects/987654321/locations/europe-west1-d",
wantPath: LocationPath{Project: "987654321", Zone: "europe-west1-d"},
wantPath: LocationPath{Project: "987654321", Location: "europe-west1-d"},
},
{
desc: "invalid: zone",
Expand Down Expand Up @@ -270,3 +270,60 @@ func TestParseSubscriptionPath(t *testing.T) {
})
}
}

func TestParseReservationPath(t *testing.T) {
for _, tc := range []struct {
desc string
input string
wantPath ReservationPath
wantErr bool
}{
{
desc: "valid: reservation path",
input: "projects/987654321/locations/europe-west1/reservations/my-reservation",
wantPath: ReservationPath{Project: "987654321", Region: "europe-west1", ReservationID: "my-reservation"},
},
{
desc: "invalid: region only",
input: "europe-west1",
wantErr: true,
},
{
desc: "invalid: topic path",
input: "projects/987654321/locations/europe-west1-d/topics/my-topic",
wantErr: true,
},
{
desc: "invalid: missing project",
input: "projects//locations/europe-west1/reservations/my-reservation",
wantErr: true,
},
{
desc: "invalid: missing region",
input: "projects/987654321/locations//reservations/my-reservation",
wantErr: true,
},
{
desc: "invalid: missing reservation id",
input: "projects/987654321/locations/europe-west1/reservations/",
wantErr: true,
},
{
desc: "invalid: has prefix",
input: "prefix/projects/987654321/locations/europe-west1/reservations/my-reservation",
wantErr: true,
},
{
desc: "invalid: has suffix",
input: "projects/my-project/locations/us-west1/reservations/my-reservation/subresource/desc",
wantErr: true,
},
} {
t.Run(tc.desc, func(t *testing.T) {
gotPath, gotErr := ParseReservationPath(tc.input)
if gotPath != tc.wantPath || (gotErr != nil) != tc.wantErr {
t.Errorf("ParseReservationPath(%q) = (%v, %v), want (%v, err=%v)", tc.input, gotPath, gotErr, tc.wantPath, tc.wantErr)
}
})
}
}