Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ internal BlobItem() { }
public bool? IsCurrentVersion { get { throw null; } }
public System.Collections.Generic.IDictionary<string, string> Metadata { get { throw null; } }
public string Name { get { throw null; } }
public System.Collections.Generic.IDictionary<string, System.Collections.Generic.IDictionary<string, string>> ObjectReplicationSourceProperties { get { throw null; } }
public Azure.Storage.Blobs.Models.BlobItemProperties Properties { get { throw null; } }
public string Snapshot { get { throw null; } }
public System.Collections.Generic.IDictionary<string, string> Tags { get { throw null; } }
Expand Down
57 changes: 47 additions & 10 deletions sdk/storage/Azure.Storage.Blobs/src/BlobExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ internal static BlobItem ToBlobItem(this BlobItemInternal blobItemInternal)
Metadata = blobItemInternal.Metadata?.Count > 0
? blobItemInternal.Metadata
: null,
Tags = blobItemInternal.BlobTags.ToTagDictionary()
Tags = blobItemInternal.BlobTags.ToTagDictionary(),
ObjectReplicationSourceProperties = blobItemInternal.ObjectReplicationMetadata?.Count > 0
? ParseObjectReplicationMetadata(blobItemInternal.ObjectReplicationMetadata)
: null
};
}

Expand Down Expand Up @@ -107,7 +110,9 @@ internal static BlobProperties ToBlobProperties(this BlobPropertiesInternal prop
CreatedOn = properties.CreatedOn,
Metadata = properties.Metadata,
ObjectReplicationDestinationPolicy = properties.ObjectReplicationPolicyId,
ObjectReplicationSourceProperties = BlobExtensions.ParseObjectReplicationIds(properties.ObjectReplicationRules),
ObjectReplicationSourceProperties = properties.ObjectReplicationRules?.Count > 0
? BlobExtensions.ParseObjectReplicationIds(properties.ObjectReplicationRules)
: null,
BlobType = properties.BlobType,
CopyCompletedOn = properties.CopyCompletedOn,
CopyStatusDescription = properties.CopyStatusDescription,
Expand Down Expand Up @@ -162,19 +167,13 @@ internal static BlobProperties ToBlobProperties(this BlobPropertiesInternal prop
/// If the blob has object replication policy applied and is the destination blob,
/// this method will return default as the policy id should be set in ObjectReplicationDestinationPolicy
/// (e.g. <see cref="BlobProperties.ObjectReplicationDestinationPolicy"/>,<see cref="BlobDownloadDetails.ObjectReplicationDestinationPolicy"/>).
/// Otherwise null will be returned.
/// Otherwise <c>null</c> will be returned.
/// </returns>
internal static IDictionary<string, IDictionary<string, string>> ParseObjectReplicationIds(this IDictionary<string, string> OrIds)
{
if (OrIds == null)
{
return null;
}
// If the dictionary is empty or it contains a key with policy id, we are not required to do any parsing since
// the policy id should already be stored in the ObjectReplicationDestinationPolicy.
if (OrIds.Count == 0 ||
(OrIds.Count > 0 &&
(OrIds.First().Key == "policy-id")))
if (OrIds.First().Key == "policy-id")
{
return default;
}
Expand All @@ -195,5 +194,43 @@ internal static IDictionary<string, IDictionary<string, string>> ParseObjectRepl
}
return OrProperties;
}

/// <summary>
/// Internal. Parses Object Replication Policy ID from Rule ID and sets the Policy ID for source blobs.
/// </summary>
/// <param name="OrMetadata">
/// Unparsed Object Replication headers.
/// For source blobs, the dictionary will contain keys that are prefixed with "or-" and followed by the
/// policy id and rule id separated by a underscore (e.g. or-policyId_ruleId).
/// The value of this metadata key will be the replication status (e.g. Complete, Failed).
/// </param>
/// <returns>
/// If the blob has object replication policy(s) applied and is the source blob, this method will return a
/// dictionary of policy Ids, with a dictionary of rules and replication status for each policy
/// (As each policy id, could have multiple rule ids).
/// </returns>
internal static IDictionary<string, IDictionary<string, string>> ParseObjectReplicationMetadata(this IDictionary<string, string> OrMetadata)
{
IDictionary<string, IDictionary<string, string>> OrProperties = new Dictionary<string, IDictionary<string, string>>();
foreach (KeyValuePair<string, string> status in OrMetadata)
{
string[] ParsedIds = status.Key.Split('_');
if (ParsedIds[0].StartsWith("or-", System.StringComparison.InvariantCulture))
{
ParsedIds[0] = ParsedIds[0].Substring("or-".Length);
}
if (OrProperties.ContainsKey(ParsedIds[0]))
{
OrProperties[ParsedIds[0]].Add(ParsedIds[1], status.Value);
}
else
{
IDictionary<string, string> NewRuleStatus = new Dictionary<string, string>();
NewRuleStatus.Add(ParsedIds[1], status.Value);
OrProperties.Add(ParsedIds[0], NewRuleStatus);
}
}
return OrProperties;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion sdk/storage/Azure.Storage.Blobs/src/Models/BlobInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ internal BlobDownloadInfo(FlattenedDownloadProperties flattened)
_flattened = flattened;
Details = new BlobDownloadDetails() {
_flattened = flattened,
ObjectReplicationSourceProperties = BlobExtensions.ParseObjectReplicationIds(flattened.ObjectReplicationRules)
ObjectReplicationSourceProperties = flattened.ObjectReplicationRules?.Count > 0
? BlobExtensions.ParseObjectReplicationIds(flattened.ObjectReplicationRules)
: null
};
}

Expand Down
5 changes: 5 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/src/Models/BlobItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,10 @@ internal BlobItem() { }
/// Tags.
/// </summary>
public IDictionary<string, string> Tags { get; internal set; }

/// <summary>
/// Holds the Object Replication Metadata as Dictionary ( policyId, Dictionary ( ruleId, replicationStatus ) ).
/// </summary>
public IDictionary<string, IDictionary<string, string> > ObjectReplicationSourceProperties { get; internal set; }
}
}
3 changes: 2 additions & 1 deletion sdk/storage/Azure.Storage.Blobs/src/Models/BlobProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public partial class BlobProperties
public string ObjectReplicationDestinationPolicy { get; internal set; }

/// <summary>
/// Parsed Object Replication Policy Id, Rule Id(s) and status of the source blob.
/// Holds the Object Replication Properties of the source blob as
/// Dictionary ( policyId, Dictionary ( ruleId, replicationStatus ) ).
/// </summary>
public IDictionary<string, IDictionary<string, string>> ObjectReplicationSourceProperties { get; internal set; }

Expand Down
9 changes: 9 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/swagger/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -1547,4 +1547,13 @@ directive:
delete $.enum
```

### Make BlobItemInternal ObjectReplicationMetadata XML value to OrMetadata
``` yaml
directive:
- from: swagger-document
where: $.definitions.BlobItemInternal
transform: >
$.properties.ObjectReplicationMetadata.xml = { "name": "OrMetadata" };
```

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fstorage%2FAzure.Storage.Blobs%2Fswagger%2Freadme.png)
42 changes: 42 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,27 @@ public async Task ListBlobsFlatSegmentAsync_VersionId()
Assert.AreEqual(setMetadataResponse.Value.VersionId, blobs[1].VersionId);
}

// TODO: Recorded Only
[Test]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2019_12_12)]
public async Task ListBlobsFlatSegmentAsync_ObjectReplication()
{
// TODO: The tests will temporarily use designated account, containers and blobs to check the
// existence of OR Metadata
BlobServiceClient sourceServiceClient = GetServiceClient_SharedKey();

// This is a recorded ONLY test with a special container we previously setup, as we can't auto setup policies yet
BlobContainerClient sourceContainer = InstrumentClient(sourceServiceClient.GetBlobContainerClient("test1"));

// Act
IList<BlobItem> blobs = await sourceContainer.GetBlobsAsync().ToListAsync();

// Assert
// Since this is a record ONLY test. We expect all the blobs in this source container/account
// to have OrMetadata
Assert.IsNotNull(blobs.First().ObjectReplicationSourceProperties);
}

[Test]
public async Task ListBlobsHierarchySegmentAsync()
{
Expand Down Expand Up @@ -2004,6 +2025,27 @@ await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
e => Assert.AreEqual("ContainerNotFound", e.ErrorCode));
}

// TODO: Recorded Only
[Test]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2019_12_12)]
public async Task ListBlobsHierarchySegmentAsync_ObjectReplication()
{
// TODO: The tests will temporarily use designated account, containers and blobs to check the
// existence of OR Metadata
BlobServiceClient sourceServiceClient = GetServiceClient_SharedKey();

// This is a recorded ONLY test with a special container we previously setup, as we can't auto setup policies yet
BlobContainerClient sourceContainer = InstrumentClient(sourceServiceClient.GetBlobContainerClient("test1"));

// Act
BlobHierarchyItem item = await sourceContainer.GetBlobsByHierarchyAsync().FirstAsync();

// Assert
// Since this is a record ONLY test. We expect all the blobs in this source container/account
// to have OrMetadata
Assert.IsNotNull(item.Blob.ObjectReplicationSourceProperties);
}

[Test]
public async Task UploadBlobAsync()
{
Expand Down
Loading