diff --git a/storage/bucket.go b/storage/bucket.go index 20302d1625e..ea83e81a1bf 100644 --- a/storage/bucket.go +++ b/storage/bucket.go @@ -775,6 +775,7 @@ func newBucketFromProto(b *storagepb.Bucket) *BucketAttrs { LocationType: b.GetLocationType(), RPO: toRPOFromProto(b), CustomPlacementConfig: customPlacementFromProto(b.GetCustomPlacementConfig()), + ProjectNumber: parseProjectNumber(b.GetProject()), // this can return 0 the project resource name is ID based } } diff --git a/storage/storage.go b/storage/storage.go index 77c7475277c..d7e06fca710 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -33,6 +33,7 @@ import ( "reflect" "regexp" "sort" + "strconv" "strings" "time" "unicode/utf8" @@ -2002,6 +2003,24 @@ func parseBucketName(b string) string { return b[sep+1:] } +// parseProjectNumber consume the given resource name and parses out the project +// number if one is present i.e. it is not a project ID. +func parseProjectNumber(r string) uint64 { + projectID := regexp.MustCompile(`projects\/([0-9]+)\/?`) + if matches := projectID.FindStringSubmatch(r); len(matches) > 0 { + // Capture group follows the matched segment. For example: + // input: projects/123/bars/456 + // output: [projects/123/, 123] + number, err := strconv.ParseUint(matches[1], 10, 64) + if err != nil { + return 0 + } + return number + } + + return 0 +} + // toProjectResource accepts a project ID and formats it as a Project resource // name. func toProjectResource(project string) string { diff --git a/storage/storage_test.go b/storage/storage_test.go index 18a19f4c255..5f137c7647d 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -2341,6 +2341,24 @@ func TestSignedURLOptionsClone(t *testing.T) { } } +func TestParseProjectNumber(t *testing.T) { + for _, tst := range []struct { + input string + want uint64 + }{ + {"projects/123", 123}, + {"projects/123/foos/456", 123}, + {"projects/abc-123/foos/456", 0}, + {"projects/abc-123", 0}, + {"projects/abc", 0}, + {"projects/abc/foos", 0}, + } { + if got := parseProjectNumber(tst.input); got != tst.want { + t.Errorf("For %q: got %v, expected %v", tst.input, got, tst.want) + } + } +} + // isZeroValue reports whether v is the zero value for its type // It errors if the argument is unknown func isZeroValue(v reflect.Value) (bool, error) {