//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
{
///
/// A factory class to create restore option info
///
public class RestoreOptionFactory
{
private static RestoreOptionFactory instance = new RestoreOptionFactory();
Dictionary optionBuilders = new Dictionary();
///
/// Singleton instance
///
public static RestoreOptionFactory Instance
{
get
{
return instance;
}
}
public RestorePlanDetailInfo CreateAndValidate(string optionKey, IRestoreDatabaseTaskDataObject restoreDataObject)
{
RestorePlanDetailInfo restorePlanDetailInfo = CreateOptionInfo(optionKey, restoreDataObject);
UpdateOption(optionKey, restoreDataObject, restorePlanDetailInfo);
return restorePlanDetailInfo;
}
///
/// Create option info using the current values
///
/// Option name
/// Restore task object
///
public RestorePlanDetailInfo CreateOptionInfo(string optionKey, IRestoreDatabaseTaskDataObject restoreDataObject)
{
if(optionBuilders.ContainsKey(optionKey))
{
return Create(optionKey, restoreDataObject, optionBuilders[optionKey]);
}
else
{
Logger.Write(LogLevel.Warning, $"cannot find restore option builder for {optionKey}");
return null;
}
}
///
/// Update the option info by validating the option
///
///
///
///
public void UpdateOption(string optionKey, IRestoreDatabaseTaskDataObject restoreDataObject, RestorePlanDetailInfo optionInfo)
{
if (optionBuilders.ContainsKey(optionKey))
{
var builder = optionBuilders[optionKey];
var currentValue = builder.CurrentValueFunction(restoreDataObject);
var defaultValue = builder.DefaultValueFunction(restoreDataObject);
var validateResult = builder.ValidateFunction(restoreDataObject, currentValue, defaultValue);
optionInfo.IsReadOnly = validateResult.IsReadOnly;
}
else
{
Logger.Write(LogLevel.Warning, $"cannot find restore option builder for {optionKey}");
}
}
///
/// Set the option value in restore task object using the values in the restore request
///
///
///
public void SetAndValidate(string optionKey, IRestoreDatabaseTaskDataObject restoreDataObject)
{
this.SetValue(optionKey, restoreDataObject);
this.ValidateOption(optionKey, restoreDataObject);
}
///
/// Set the option value in restore task object using the values in the restore request
///
///
///
public void SetValue(string optionKey, IRestoreDatabaseTaskDataObject restoreDataObject)
{
if (restoreDataObject != null)
{
if (optionBuilders.ContainsKey(optionKey))
{
var builder = optionBuilders[optionKey];
if (restoreDataObject.RestoreParams != null && restoreDataObject.RestoreParams.Options.ContainsKey(optionKey))
{
try
{
var value = restoreDataObject.RestoreParams.GetOptionValue(optionKey);
builder.SetValueFunction(restoreDataObject, value);
}
catch (Exception ex)
{
var defaultValue = builder.DefaultValueFunction(restoreDataObject);
builder.SetValueFunction(restoreDataObject, defaultValue);
Logger.Write(LogLevel.Warning, $"Failed tp set restore option {optionKey} error:{ex.Message}");
}
}
else
{
try
{
var defaultValue = builder.DefaultValueFunction(restoreDataObject);
builder.SetValueFunction(restoreDataObject, defaultValue);
}
catch (Exception)
{
Logger.Write(LogLevel.Warning, $"Failed to set restore option {optionKey} to default value");
}
}
}
else
{
Logger.Write(LogLevel.Warning, $"cannot find restore option builder for {optionKey}");
}
}
}
///
/// Validates the options, if option is not set correctly, set to default and return the error
///
///
///
///
public string ValidateOption(string optionKey, IRestoreDatabaseTaskDataObject restoreDataObject)
{
string errorMessage = string.Empty;
if (optionBuilders.ContainsKey(optionKey))
{
var builder = optionBuilders[optionKey];
var currentValue = builder.CurrentValueFunction(restoreDataObject);
var defaultValue = builder.DefaultValueFunction(restoreDataObject);
OptionValidationResult result = optionBuilders[optionKey].ValidateFunction(restoreDataObject, currentValue, defaultValue);
if (result.IsReadOnly)
{
if(!ValueEqualsDefault(currentValue, defaultValue))
{
builder.SetValueFunction(restoreDataObject, defaultValue);
errorMessage = $"{optionKey} is ready only and cannot be modified";
}
}
if (!string.IsNullOrEmpty(result.ErrorMessage))
{
errorMessage = result.ErrorMessage;
builder.SetValueFunction(restoreDataObject, defaultValue);
}
}
else
{
errorMessage = $"cannot find restore option builder for {optionKey}";
Logger.Write(LogLevel.Warning, errorMessage);
}
return errorMessage;
}
private bool ValueEqualsDefault(object currentValue, object defaultValue)
{
if(currentValue == null && defaultValue == null)
{
return true;
}
if(currentValue == null && defaultValue != null)
{
return false;
}
if (currentValue != null && defaultValue == null)
{
return false;
}
return currentValue.Equals(defaultValue);
}
private RestoreOptionFactory()
{
Register(RestoreOptionsHelper.RelocateDbFiles,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return false;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.RelocateAllFiles;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult
{
IsReadOnly = restoreDataObject.DbFiles.Count == 0
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.RelocateAllFiles = restoreDataObject.RestoreParams.GetOptionValue(RestoreOptionsHelper.RelocateDbFiles);
return true;
}
});
Register(RestoreOptionsHelper.DataFileFolder,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.DefaultDataFileFolder;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.DataFilesFolder;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult
{
IsReadOnly = !restoreDataObject.RelocateAllFiles
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.DataFilesFolder = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.LogFileFolder,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.DefaultLogFileFolder;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.LogFilesFolder;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult
{
IsReadOnly = !restoreDataObject.RelocateAllFiles
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.LogFilesFolder = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.ReplaceDatabase,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return false;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.RestoreOptions.ReplaceDatabase;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult();
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.RestoreOptions.ReplaceDatabase = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.KeepReplication,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return false;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.RestoreOptions.KeepReplication;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
IsReadOnly = restoreDataObject.RestoreOptions.RecoveryState == DatabaseRecoveryState.WithNoRecovery
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.RestoreOptions.KeepReplication = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.SetRestrictedUser,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return false;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.RestoreOptions.SetRestrictedUser;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.RestoreOptions.SetRestrictedUser = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.RecoveryState,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return DatabaseRecoveryState.WithRecovery.ToString();
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.RestoreOptions.RecoveryState.ToString();
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.RestoreOptions.RecoveryState = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.StandbyFile,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.DefaultStandbyFile;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.RestoreOptions.StandByFile;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
IsReadOnly = restoreDataObject.RestoreOptions.RecoveryState != DatabaseRecoveryState.WithStandBy
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.RestoreOptions.StandByFile = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.BackupTailLog,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.IsTailLogBackupPossible;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.BackupTailLog;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
IsReadOnly = !restoreDataObject.IsTailLogBackupPossible
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.BackupTailLog = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.TailLogBackupFile,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.DefaultTailLogbackupFile;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.TailLogBackupFile;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
IsReadOnly = !restoreDataObject.BackupTailLog | !restoreDataObject.IsTailLogBackupPossible
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.TailLogBackupFile = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.TailLogWithNoRecovery,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.IsTailLogBackupWithNoRecoveryPossible;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.TailLogWithNoRecovery;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
IsReadOnly = !restoreDataObject.BackupTailLog | !restoreDataObject.IsTailLogBackupWithNoRecoveryPossible
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.TailLogWithNoRecovery = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.CloseExistingConnections,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return false;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.CloseExistingConnections;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
//TODO: make the method public in SMO bool canDropExistingConnections = restoreDataObject.RestorePlan.CanDropExistingConnections(this.Data.RestorePlanner.DatabaseName);
IsReadOnly = false
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.CloseExistingConnections = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.SourceDatabaseName,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.DefaultSourceDbName;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.SourceDatabaseName;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
string errorMessage = string.Empty;
var sourceDbNames = restoreDataObject.SourceDbNames;
if (currentValue == null || (sourceDbNames != null &&
!sourceDbNames.Any(x => string.Compare(x, currentValue.ToString(), StringComparison.InvariantCultureIgnoreCase) == 0)))
{
errorMessage = "Source database name is not valid";
}
return new OptionValidationResult()
{
ErrorMessage = errorMessage
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.SourceDatabaseName = GetValueAs(value);
return true;
}
});
Register(RestoreOptionsHelper.TargetDatabaseName,
new OptionBuilder
{
DefaultValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.CanChangeTargetDatabase ? restoreDataObject.DefaultSourceDbName : restoreDataObject.DefaultTargetDbName;
},
CurrentValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject) =>
{
return restoreDataObject.TargetDatabaseName;
},
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
{
return new OptionValidationResult()
{
IsReadOnly = !restoreDataObject.CanChangeTargetDatabase
};
},
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
{
restoreDataObject.TargetDatabaseName = GetValueAs(value);
return true;
}
});
}
internal T GetValueAs(object value)
{
return GeneralRequestDetails.GetValueAs(value);
}
private void Register(string optionKey, OptionBuilder optionBuilder)
{
optionBuilders.Add(optionKey, optionBuilder);
}
private RestorePlanDetailInfo Create(
string optionKey,
IRestoreDatabaseTaskDataObject restoreDataObject,
OptionBuilder optionBuilder)
{
object currnetValue = optionBuilder.CurrentValueFunction(restoreDataObject);
object defaultValue = optionBuilder.DefaultValueFunction(restoreDataObject);
OptionValidationResult validationResult = optionBuilder.ValidateFunction(restoreDataObject, currnetValue, defaultValue);
return new RestorePlanDetailInfo
{
Name = optionKey,
CurrentValue = currnetValue,
DefaultValue = defaultValue,
IsReadOnly = validationResult.IsReadOnly,
IsVisiable = validationResult.IsVisible,
ErrorMessage = validationResult.ErrorMessage
};
}
}
internal class OptionBuilder
{
public Func DefaultValueFunction { get; set; }
public Func ValidateFunction { get; set; }
public Func CurrentValueFunction { get; set; }
public Func SetValueFunction { get; set; }
}
internal class OptionValidationResult
{
public OptionValidationResult()
{
IsVisible = true;
IsReadOnly = false;
ErrorMessage = string.Empty;
}
public bool IsReadOnly { get; set; }
public bool IsVisible { get; set; }
public string ErrorMessage { get; set; }
}
}