Kit.Core/LibExternal/Minio/DataModel/Args/CopyObjectArgs.cs

253 lines
9.8 KiB
C#

/*
* MinIO .NET Library for Amazon S3 Compatible Cloud Storage, (C) 2020, 2021 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using Minio.DataModel.Encryption;
using Minio.DataModel.ObjectLock;
using Minio.Helper;
namespace Minio.DataModel.Args;
public class CopyObjectArgs : ObjectWriteArgs<CopyObjectArgs>
{
public CopyObjectArgs()
{
RequestMethod = HttpMethod.Put;
SourceObject = new CopySourceObjectArgs();
ReplaceTagsDirective = false;
ReplaceMetadataDirective = false;
ObjectLockSet = false;
RetentionUntilDate = default;
}
internal CopySourceObjectArgs SourceObject { get; set; }
internal ObjectStat SourceObjectInfo { get; set; }
internal bool ReplaceTagsDirective { get; set; }
internal bool ReplaceMetadataDirective { get; set; }
internal string StorageClass { get; set; }
internal ObjectRetentionMode ObjectLockRetentionMode { get; set; }
internal DateTime RetentionUntilDate { get; set; }
internal bool ObjectLockSet { get; set; }
internal override void Validate()
{
Utils.ValidateBucketName(BucketName);
if (SourceObject is null)
throw new InvalidOperationException(nameof(SourceObject) + " has not been assigned. Please use " +
nameof(WithCopyObjectSource));
if (SourceObjectInfo is null)
throw new InvalidOperationException(
"StatObject result for the copy source object needed to continue copy operation. Use " +
nameof(WithCopyObjectSourceStats) + " to initialize StatObject result.");
if (!string.IsNullOrEmpty(NotMatchETag) && !string.IsNullOrEmpty(MatchETag))
throw new InvalidOperationException("Invalid to set both Etag match conditions " + nameof(NotMatchETag) +
" and " + nameof(MatchETag));
if (!ModifiedSince.Equals(default) &&
!UnModifiedSince.Equals(default))
throw new InvalidOperationException("Invalid to set both modified date match conditions " +
nameof(ModifiedSince) + " and " + nameof(UnModifiedSince));
Populate();
}
private void Populate()
{
if (string.IsNullOrEmpty(ObjectName)) ObjectName = SourceObject.ObjectName;
if (SSE?.GetEncryptionType().Equals(EncryptionType.SSE_C) == true)
{
Headers = new Dictionary<string, string>(StringComparer.Ordinal);
SSE.Marshal(Headers);
}
if (!ReplaceMetadataDirective)
{
// Check in copy conditions if replace metadata has been set
var copyReplaceMeta = SourceObject.CopyOperationConditions?.HasReplaceMetadataDirective() == true;
WithReplaceMetadataDirective(copyReplaceMeta);
}
Headers ??= new Dictionary<string, string>(StringComparer.Ordinal);
if (ReplaceMetadataDirective)
{
if (Headers is not null)
#if NETSTANDARD
foreach (var pair in SourceObjectInfo.MetaData.ToList())
#else
foreach (var pair in SourceObjectInfo.MetaData)
#endif
{
var comparer = StringComparer.OrdinalIgnoreCase;
var newDictionary = new Dictionary<string, string>(Headers, comparer);
SourceObjectInfo.MetaData.Remove(pair.Key);
}
Headers = Headers
.Concat(SourceObjectInfo.MetaData)
.GroupBy(item => item.Key, StringComparer.Ordinal)
.ToDictionary(item => item.Key, item =>
item.Last().Value, StringComparer.Ordinal);
}
if (Headers is not null)
{
var newKVList = new List<Tuple<string, string>>();
#if NETSTANDARD
foreach (var item in Headers.ToList())
#else
foreach (var item in Headers)
#endif
{
var key = item.Key;
if (!OperationsUtil.IsSupportedHeader(item.Key) &&
!item.Key.StartsWith("x-amz-meta",
StringComparison.OrdinalIgnoreCase) &&
!OperationsUtil.IsSSEHeader(key))
{
newKVList.Add(new Tuple<string, string>("x-amz-meta-" +
key.ToLowerInvariant(), item.Value));
Headers.Remove(item.Key);
}
newKVList.Add(new Tuple<string, string>(key, item.Value));
}
foreach (var item in newKVList) Headers[item.Item1] = item.Item2;
}
}
public CopyObjectArgs WithCopyObjectSource(CopySourceObjectArgs cs)
{
if (cs is null)
throw new InvalidOperationException("The copy source object needed for copy operation is not initialized.");
SourceObject.RequestMethod = HttpMethod.Put;
SourceObject ??= new CopySourceObjectArgs();
SourceObject.BucketName = cs.BucketName;
SourceObject.ObjectName = cs.ObjectName;
SourceObject.VersionId = cs.VersionId;
SourceObject.SSE = cs.SSE;
SourceObject.Headers = cs.Headers;
SourceObject.MatchETag = cs.MatchETag;
SourceObject.ModifiedSince = cs.ModifiedSince;
SourceObject.NotMatchETag = cs.NotMatchETag;
SourceObject.UnModifiedSince = cs.UnModifiedSince;
SourceObject.CopySourceObjectPath = $"{cs.BucketName}/{Utils.UrlEncode(cs.ObjectName)}";
SourceObject.CopyOperationConditions = cs.CopyOperationConditions?.Clone();
return this;
}
public CopyObjectArgs WithReplaceTagsDirective(bool replace)
{
ReplaceTagsDirective = replace;
return this;
}
public CopyObjectArgs WithReplaceMetadataDirective(bool replace)
{
ReplaceMetadataDirective = replace;
return this;
}
public CopyObjectArgs WithObjectLockMode(ObjectRetentionMode mode)
{
ObjectLockSet = true;
ObjectLockRetentionMode = mode;
return this;
}
public CopyObjectArgs WithObjectLockRetentionDate(DateTime untilDate)
{
ObjectLockSet = true;
RetentionUntilDate = new DateTime(untilDate.Year, untilDate.Month, untilDate.Day,
untilDate.Hour, untilDate.Minute, untilDate.Second, untilDate.Kind);
return this;
}
internal CopyObjectArgs WithCopyObjectSourceStats(ObjectStat info)
{
SourceObjectInfo = info;
if (info.MetaData is not null && !ReplaceMetadataDirective)
{
SourceObject.Headers ??= new Dictionary<string, string>(StringComparer.Ordinal);
SourceObject.Headers = SourceObject.Headers.Concat(info.MetaData)
.GroupBy(item => item.Key, StringComparer.Ordinal)
.ToDictionary(item => item.Key, item => item.First().Value, StringComparer.Ordinal);
}
return this;
}
internal CopyObjectArgs WithStorageClass(string storageClass)
{
StorageClass = storageClass;
return this;
}
public CopyObjectArgs WithRetentionUntilDate(DateTime date)
{
ObjectLockSet = true;
RetentionUntilDate = date;
return this;
}
internal override HttpRequestMessageBuilder BuildRequest(HttpRequestMessageBuilder requestMessageBuilder)
{
if (!string.IsNullOrEmpty(MatchETag))
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-copy-source-if-match", MatchETag);
if (!string.IsNullOrEmpty(NotMatchETag))
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-copy-source-if-none-match", NotMatchETag);
if (ModifiedSince != default)
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-copy-source-if-unmodified-since",
Utils.To8601String(ModifiedSince));
if (UnModifiedSince != default)
{
using var request = requestMessageBuilder.Request;
request.Headers.Add("x-amz-copy-source-if-modified-since",
Utils.To8601String(UnModifiedSince));
}
if (!string.IsNullOrEmpty(VersionId)) requestMessageBuilder.AddQueryParameter("versionId", VersionId);
if (ObjectTags?.TaggingSet?.Tag.Count > 0)
{
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-tagging", ObjectTags.GetTagString());
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-tagging-directive",
ReplaceTagsDirective ? "COPY" : "REPLACE");
}
if (ReplaceMetadataDirective)
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-metadata-directive", "REPLACE");
if (!string.IsNullOrEmpty(StorageClass))
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-storage-class", StorageClass);
if (LegalHoldEnabled == true)
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-object-lock-legal-hold", "ON");
if (ObjectLockSet)
{
if (!RetentionUntilDate.Equals(default))
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-object-lock-retain-until-date",
Utils.To8601String(RetentionUntilDate));
requestMessageBuilder.AddOrUpdateHeaderParameter("x-amz-object-lock-mode",
ObjectLockRetentionMode == ObjectRetentionMode.GOVERNANCE ? "GOVERNANCE" : "COMPLIANCE");
}
return requestMessageBuilder;
}
}