diff --git a/storage/grpc_client.go b/storage/grpc_client.go index 1274be5f2586..ea56f70065fa 100644 --- a/storage/grpc_client.go +++ b/storage/grpc_client.go @@ -550,7 +550,19 @@ func (c *grpcStorageClient) UpdateObject(ctx context.Context, bucket, object str if uattrs.ACL != nil || len(uattrs.PredefinedACL) > 0 { fieldMask.Paths = append(fieldMask.Paths, "acl") } - // TODO(cathyo): Handle metadata. Pending b/230510191. + + if uattrs.Metadata != nil { + // We don't support deleting a specific metadata key; metadata is deleted + // as a whole if provided an empty map, so we do not use dot notation here + if len(uattrs.Metadata) == 0 { + fieldMask.Paths = append(fieldMask.Paths, "metadata") + } else { + // We can, however, use dot notation for adding keys + for key := range uattrs.Metadata { + fieldMask.Paths = append(fieldMask.Paths, fmt.Sprintf("metadata.%s", key)) + } + } + } req.UpdateMask = fieldMask diff --git a/storage/integration_test.go b/storage/integration_test.go index e2db30cb4f5c..a557b2a6c20b 100644 --- a/storage/integration_test.go +++ b/storage/integration_test.go @@ -1349,7 +1349,8 @@ func TestIntegration_Objects(t *testing.T) { } func TestIntegration_ObjectUpdate(t *testing.T) { - multiTransportTest(skipGRPC("metadata pending b/230510191"), t, func(t *testing.T, ctx context.Context, bucket string, _ string, client *Client) { + ctx := skipJSONReads(context.Background(), "no reads in test") + multiTransportTest(ctx, t, func(t *testing.T, ctx context.Context, bucket string, _ string, client *Client) { b := client.Bucket(bucket) o := b.Object("update-obj" + uidSpaceObjects.New()) @@ -1361,11 +1362,11 @@ func TestIntegration_ObjectUpdate(t *testing.T) { if err := w.Close(); err != nil { t.Fatalf("w.Close: %v", err) } - t.Cleanup(func() { + defer func() { if err := o.Delete(ctx); err != nil { t.Errorf("o.Delete : %v", err) } - }) + }() attrs, err := o.Attrs(ctx) if err != nil { @@ -1401,6 +1402,21 @@ func TestIntegration_ObjectUpdate(t *testing.T) { t.Errorf("updated.Updated should be newer than update.Created") } + // Add another metadata key + anotherKey := map[string]string{"key2": "value2"} + metadata["key2"] = "value2" + + updated, err = o.Update(ctx, ObjectAttrsToUpdate{ + Metadata: anotherKey, + }) + if err != nil { + t.Fatalf("o.Update: %v", err) + } + + if got, want := updated.Metadata, metadata; !testutil.Equal(got, want) { + t.Errorf("updated.Metadata == %+v; want %+v", updated.Metadata, want) + } + // Delete ContentType and ContentLanguage and Metadata. updated, err = o.If(Conditions{MetagenerationMatch: updated.Metageneration}).Update(ctx, ObjectAttrsToUpdate{ ContentType: "", diff --git a/storage/storage.go b/storage/storage.go index 2a5723a22ff3..f121823d7722 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1163,7 +1163,7 @@ func (uattrs *ObjectAttrsToUpdate) toProtoObject(bucket, object string) *storage o.Acl = toProtoObjectACL(uattrs.ACL) } - // TODO(cathyo): Handle metadata. Pending b/230510191. + o.Metadata = uattrs.Metadata return o }