Merge pull request #3402 from warwickmm/constructor_virtual_members

Avoid referencing virtual members in MicrosoftGraphBackend constructor
This commit is contained in:
Rune Henriksen
2018-10-04 00:08:51 +02:00
committed by GitHub
4 changed files with 27 additions and 22 deletions
@@ -74,21 +74,23 @@ namespace Duplicati.Library.Backend
private readonly JsonSerializer m_serializer = new JsonSerializer();
private readonly OAuthHttpClient m_client;
private readonly string m_path;
private readonly int fragmentSize;
private readonly int fragmentRetryCount;
private readonly int fragmentRetryDelay; // In milliseconds
private string[] dnsNames = null;
private readonly Lazy<string> rootPathFromURL;
private string RootPath => this.rootPathFromURL.Value;
protected MicrosoftGraphBackend() { } // Constructor needed for dynamic loading to find it
protected MicrosoftGraphBackend(string url, Dictionary<string, string> options)
protected MicrosoftGraphBackend(string url, string protocolKey, Dictionary<string, string> options)
{
string authid;
options.TryGetValue(AUTHID_OPTION, out authid);
if (string.IsNullOrEmpty(authid))
throw new UserInformationException(Strings.MicrosoftGraph.MissingAuthId(OAuthHelper.OAUTH_LOGIN_URL(this.ProtocolKey)), "MicrosoftGraphBackendMissingAuthId");
throw new UserInformationException(Strings.MicrosoftGraph.MissingAuthId(OAuthHelper.OAUTH_LOGIN_URL(protocolKey)), "MicrosoftGraphBackendMissingAuthId");
string fragmentSizeStr;
if (options.TryGetValue(UPLOAD_SESSION_FRAGMENT_SIZE_OPTION, out fragmentSizeStr) && int.TryParse(fragmentSizeStr, out this.fragmentSize))
@@ -117,11 +119,12 @@ namespace Duplicati.Library.Backend
this.fragmentRetryDelay = UPLOAD_SESSION_FRAGMENT_DEFAULT_RETRY_DELAY;
}
this.m_client = new OAuthHttpClient(authid, this.ProtocolKey);
this.m_client = new OAuthHttpClient(authid, protocolKey);
this.m_client.BaseAddress = new System.Uri(BASE_ADDRESS);
// Extract out the path to the backup root folder from the given URI
this.m_path = NormalizeSlashes(this.GetRootPathFromUrl(url));
// Extract out the path to the backup root folder from the given URI. Since this can be an expensive operation,
// we will cache the value using a lazy initializer.
this.rootPathFromURL = new Lazy<string>(() => this.GetRootPathFromUrl(url));
}
public abstract string ProtocolKey { get; }
@@ -167,7 +170,7 @@ namespace Duplicati.Library.Backend
// To get the upload session endpoint, we can start an upload session and then immediately cancel it.
// We pick a random file name (using a guid) to make sure we don't conflict with an existing file
string dnsTestFile = string.Format("DNSNameTest-{0}", Guid.NewGuid());
UploadSession uploadSession = this.Post<UploadSession>(string.Format("{0}/root:{1}{2}:/createUploadSession", this.DrivePrefix, this.m_path, NormalizeSlashes(dnsTestFile)), null);
UploadSession uploadSession = this.Post<UploadSession>(string.Format("{0}/root:{1}{2}:/createUploadSession", this.DrivePrefix, this.RootPath, NormalizeSlashes(dnsTestFile)), null);
// Canceling an upload session is done by sending a DELETE to the upload URL
var request = new HttpRequestMessage(HttpMethod.Delete, uploadSession.UploadUrl);
@@ -240,7 +243,7 @@ namespace Duplicati.Library.Backend
{
string parentFolder = "root";
string parentFolderPath = string.Empty;
foreach (string folder in this.m_path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
foreach (string folder in this.RootPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
{
string nextPath = parentFolderPath + "/" + folder;
DriveItem folderItem;
@@ -268,7 +271,7 @@ namespace Duplicati.Library.Backend
{
try
{
return this.Enumerate<DriveItem>(string.Format("{0}/root:{1}:/children", this.DrivePrefix, this.m_path))
return this.Enumerate<DriveItem>(string.Format("{0}/root:{1}:/children", this.DrivePrefix, this.RootPath))
.Where(item => item.IsFile && !item.IsDeleted) // Exclude non-files and deleted items (not sure if they show up in this listing, but make sure anyway)
.Select(item =>
new FileEntry(
@@ -296,7 +299,7 @@ namespace Duplicati.Library.Backend
{
try
{
var response = this.m_client.GetAsync(string.Format("{0}/root:{1}{2}:/content", this.DrivePrefix, this.m_path, NormalizeSlashes(remotename))).Await();
var response = this.m_client.GetAsync(string.Format("{0}/root:{1}{2}:/content", this.DrivePrefix, this.RootPath, NormalizeSlashes(remotename))).Await();
this.CheckResponse(response);
using (Stream responseStream = response.Content.ReadAsStreamAsync().Await())
{
@@ -314,7 +317,7 @@ namespace Duplicati.Library.Backend
{
try
{
this.Patch(string.Format("{0}/root:{1}{2}", this.DrivePrefix, this.m_path, NormalizeSlashes(oldname)), new DriveItem() { Name = newname });
this.Patch(string.Format("{0}/root:{1}{2}", this.DrivePrefix, this.RootPath, NormalizeSlashes(oldname)), new DriveItem() { Name = newname });
}
catch (DriveItemNotFoundException ex)
{
@@ -338,7 +341,7 @@ namespace Duplicati.Library.Backend
{
StreamContent streamContent = new StreamContent(stream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
var response = this.m_client.PutAsync(string.Format("{0}/root:{1}{2}:/content", this.DrivePrefix, this.m_path, NormalizeSlashes(remotename)), streamContent).Await();
var response = this.m_client.PutAsync(string.Format("{0}/root:{1}{2}:/content", this.DrivePrefix, this.RootPath, NormalizeSlashes(remotename)), streamContent).Await();
// Make sure this response is a valid drive item, though we don't actually use it for anything currently.
this.ParseResponse<DriveItem>(response);
@@ -350,7 +353,7 @@ namespace Duplicati.Library.Backend
// The documentation seems somewhat contradictory - it states that uploads must be done sequentially,
// but also states that the nextExpectedRanges value returned may indicate multiple ranges...
// For now, this plays it safe and does a sequential upload.
HttpRequestMessage createSessionRequest = new HttpRequestMessage(HttpMethod.Post, string.Format("{0}/root:{1}{2}:/createUploadSession", this.DrivePrefix, this.m_path, NormalizeSlashes(remotename)));
HttpRequestMessage createSessionRequest = new HttpRequestMessage(HttpMethod.Post, string.Format("{0}/root:{1}{2}:/createUploadSession", this.DrivePrefix, this.RootPath, NormalizeSlashes(remotename)));
// Indicate that we want to replace any existing content with this new data we're uploading
this.PrepareContent(new UploadSession() { Item = new DriveItem() { ConflictBehavior = ConflictBehavior.Replace } });
@@ -429,7 +432,7 @@ namespace Duplicati.Library.Backend
public void Delete(string remotename)
{
var response = this.m_client.DeleteAsync(string.Format("{0}/root:{1}{2}", this.DrivePrefix, this.m_path, NormalizeSlashes(remotename))).Await();
var response = this.m_client.DeleteAsync(string.Format("{0}/root:{1}{2}", this.DrivePrefix, this.RootPath, NormalizeSlashes(remotename))).Await();
try
{
this.CheckResponse(response);
@@ -445,7 +448,7 @@ namespace Duplicati.Library.Backend
{
try
{
string rootPath = string.Format("{0}/root:{1}", this.DrivePrefix, this.m_path);
string rootPath = string.Format("{0}/root:{1}", this.DrivePrefix, this.RootPath);
this.Get<DriveItem>(rootPath);
}
catch (DriveItemNotFoundException ex)
@@ -10,13 +10,14 @@ namespace Duplicati.Library.Backend
{
private const string GROUP_EMAIL_OPTION = "group-email";
private const string GROUP_ID_OPTION = "group-id";
private const string PROTOCOL_KEY = "msgroup";
private readonly string drivePath;
public MicrosoftGroup() { } // Constructor needed for dynamic loading to find it
public MicrosoftGroup(string url, Dictionary<string, string> options)
: base(url, options)
: base(url, MicrosoftGroup.PROTOCOL_KEY, options)
{
string groupId = null;
string groupEmail;
@@ -46,7 +47,7 @@ namespace Duplicati.Library.Backend
public override string ProtocolKey
{
get { return "msgroup"; }
get { return MicrosoftGroup.PROTOCOL_KEY; }
}
public override string DisplayName
@@ -7,15 +7,15 @@ namespace Duplicati.Library.Backend
public class OneDriveV2 : MicrosoftGraphBackend
{
private const string DRIVE_ID_OPTION = "drive-id";
private const string DEFAULT_DRIVE_PATH = "/me/drive";
private const string PROTOCOL_KEY = "onedrivev2";
private readonly string drivePath;
public OneDriveV2() { } // Constructor needed for dynamic loading to find it
public OneDriveV2(string url, Dictionary<string, string> options)
: base(url, options)
: base(url, OneDriveV2.PROTOCOL_KEY, options)
{
string driveId;
if (options.TryGetValue(DRIVE_ID_OPTION, out driveId))
@@ -30,7 +30,7 @@ namespace Duplicati.Library.Backend
public override string ProtocolKey
{
get { return "onedrivev2"; }
get { return OneDriveV2.PROTOCOL_KEY; }
}
public override string DisplayName
@@ -11,6 +11,7 @@ namespace Duplicati.Library.Backend
public class SharePointV2 : MicrosoftGraphBackend
{
private const string SITE_ID_OPTION = "site-id";
private const string PROTOCOL_KEY = "sharepoint";
private readonly string drivePath;
private string siteId = null;
@@ -18,7 +19,7 @@ namespace Duplicati.Library.Backend
public SharePointV2() { } // Constructor needed for dynamic loading to find it
public SharePointV2(string url, Dictionary<string, string> options)
: base(url, options)
: base(url, SharePointV2.PROTOCOL_KEY, options)
{
// Check to see if a site ID was explicitly provided
string siteIdOption;
@@ -42,7 +43,7 @@ namespace Duplicati.Library.Backend
public override string ProtocolKey
{
get { return "sharepoint"; }
get { return SharePointV2.PROTOCOL_KEY; }
}
public override string DisplayName